bes  Updated for version 3.20.10
CredentialsManager.cc
1 // -*- mode: c++; c-basic-offset:4 -*-
2 
3 // This file is part of the BES
4 
5 // Copyright (c) 2020 OPeNDAP, Inc.
6 // Author: Nathan Potter<ndp@opendap.org>
7 //
8 // This library is free software; you can redistribute it and/or
9 // modify it under the terms of the GNU Lesser General Public
10 // License as published by the Free Software Foundation; either
11 // version 2.1 of the License, or (at your option) any later version.
12 //
13 // This library is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 // Lesser General Public License for more details.
17 //
18 // You should have received a copy of the GNU Lesser General Public
19 // License along with this library; if not, write to the Free Software
20 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 //
22 // You can contact OPeNDAP, Inc. at PO Box 112, Saunderstown, RI. 02874-0112.
23 // Created by ndp on 12/11/19.
24 //
25 
26 #include "config.h"
27 
28 #include <cstdlib>
29 #include <cstring>
30 #include <iomanip>
31 #include <sstream>
32 #include <locale>
33 #include <string>
34 #include <sys/stat.h>
35 
36 #include "AllowedHosts.h"
37 #include "TheBESKeys.h"
38 #include "kvp_utils.h"
39 #include "BESInternalError.h"
40 #include "BESDebug.h"
41 #include "CurlUtils.h"
42 #include "HttpNames.h"
43 
44 #include "CredentialsManager.h"
45 #include "NgapS3Credentials.h"
46 #include "DmrppNames.h"
47 
48 using namespace std;
49 
50 #define prolog std::string("CredentialsManager::").append(__func__).append("() - ")
51 
52 // Class vocabulary
53 const char *CredentialsManager::ENV_ID_KEY = "CMAC_ID";
54 const char *CredentialsManager::ENV_ACCESS_KEY = "CMAC_ACCESS_KEY";
55 const char *CredentialsManager::ENV_REGION_KEY = "CMAC_REGION";
56 const char *CredentialsManager::ENV_BUCKET_KEY = "CMAC_BUCKET";
57 const char *CredentialsManager::ENV_URL_KEY = "CMAC_URL";
58 
59 const char *CredentialsManager::USE_ENV_CREDS_KEY_VALUE = "ENV_CREDS";
60 
65 
69 static std::once_flag d_cmac_init_once;
70 
71 
72 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73 //
74 // Helper Functions
75 //
84 std::string get_env_value(const string &key){
85  string value;
86  const char *cstr = getenv(key.c_str());
87  if(cstr){
88  value.assign(cstr);
89  BESDEBUG(CREDS, prolog << "From system environment - " << key << ": " << value << endl);
90  }
91  else {
92  value.clear();
93  }
94  return value;
95 }
96 
97 #if 0
106 std::string get_config_value(const string &key){
107  string value;
108  bool key_found=false;
109  TheBESKeys::TheKeys()->get_value(key, value, key_found);
110  if (key_found) {
111  BESDEBUG(CREDS, prolog << "Using " << key << " from TheBESKeys" << endl);
112  }
113  else {
114  value.clear();
115  }
116  return value;
117 }
118 #endif
119 
120 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
121 //
122 // class CredentialsManager
123 //
124 
130 
131  std::call_once(d_cmac_init_once,CredentialsManager::initialize_instance);
132  return theMngr;
133 }
134 
138 void CredentialsManager::initialize_instance()
139 {
140  theMngr = new CredentialsManager;
141 #ifdef HAVE_ATEXIT
142  atexit(delete_instance);
143 #endif
144 
145 }
146 
150 CredentialsManager::CredentialsManager(): ngaps3CredentialsLoaded(false){
151  // d_netrc_filename = curl::get_netrc_filename();
152 }
153 
154 
159  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
160  delete it->second;
161  }
162  creds.clear();
163 }
164 
165 
169 void CredentialsManager::delete_instance()
170 {
171  delete theMngr;
172  theMngr = 0;
173 }
174 
175 
181 void
182 CredentialsManager::add(const std::string &key, AccessCredentials *ac){
183  // This lock is a RAII implementation. It will block until the mutex is
184  // available and the lock will be released when the instance is destroyed.
185  std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
186 
187  creds.insert(std::pair<std::string,AccessCredentials *>(key, ac));
188  BESDEBUG(CREDS, prolog << "Added AccessCredentials to CredentialsManager. credentials: " << endl << ac->to_json() << endl);
189 }
190 
198 CredentialsManager::get(shared_ptr<http::url> &url){
199  // This lock is a RAII implementation. It will block until the mutex is
200  // available and the lock will be released when the instance is destroyed.
201  std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
202 
203  AccessCredentials *best_match = NULL;
204  std::string best_key("");
205 
206  if(url->protocol() == HTTP_PROTOCOL || url->protocol() == HTTPS_PROTOCOL) {
207  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
208  std::string key = it->first;
209  if (url->str().rfind(key, 0) == 0) {
210  // url starts with key
211  if (key.length() > best_key.length()) {
212  best_key = key;
213  best_match = it->second;
214  }
215  }
216  }
217  }
218  return best_match;
219 }
220 
226 bool file_exists(const string &filename) {
227  struct stat buffer;
228  return (stat (filename.c_str(), &buffer) == 0);
229 }
230 
251 bool file_is_secured(const string &filename) {
252  struct stat st;
253  if (stat(filename.c_str(), &st) != 0) {
254  string err;
255  err.append("file_is_secured() Unable to access file ");
256  err.append(filename).append(" strerror: ").append(strerror(errno));
257  throw BESInternalError(err, __FILE__, __LINE__);
258  }
259 
260  mode_t perm = st.st_mode;
261  bool status;
262  status = (perm & S_IRUSR) && !(
263  // (perm & S_IWUSR) || // We don't need to enforce user no write
264  (perm & S_IXUSR) ||
265  (perm & S_IRGRP) ||
266  (perm & S_IWGRP) ||
267  (perm & S_IXGRP) ||
268  (perm & S_IROTH) ||
269  (perm & S_IWOTH) ||
270  (perm & S_IXOTH));
271  BESDEBUG(CREDS, prolog << "file_is_secured() " << filename << " secured: " << (status ? "true" : "false") << endl);
272  return status;
273 }
274 
306 
307  // This lock is a RAII implementation. It will block until the mutex is
308  // available and the lock will be released when the instance is destroyed.
309  std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
310 
311  bool found_key = true;
312  AccessCredentials *accessCredentials;
313  map<string, AccessCredentials *> credential_sets;
314 
315  string config_file;
316  TheBESKeys::TheKeys()->get_value(CATALOG_MANAGER_CREDENTIALS, config_file, found_key);
317  if(!found_key){
318  BESDEBUG(CREDS, prolog << "The BES key " << CATALOG_MANAGER_CREDENTIALS
319  << " was not found in the BES configuration tree. No AccessCredentials were loaded" << endl);
320  return;
321  }
322 
323  // Does the configuration indicate that credentials will be submitted via the runtime environment?
324  if(config_file == string(CredentialsManager::USE_ENV_CREDS_KEY_VALUE)){
325  // Apparently so...
326  accessCredentials = theCM()->load_credentials_from_env();
327  if(accessCredentials){
328  // So if we have them, we add them to theCM() and then return without processing the configuration.
329  string url = accessCredentials->get(AccessCredentials::URL_KEY);
330  theCM()->add(url,accessCredentials);
331  }
332  // Environment injected credentials override all other configuration credentials.
333  // Since the value of CATALOG_MANAGER_CREDENTIALS is ENV_CREDS_VALUE there is no
334  // Configuration file identified, so wether or not valid credentials information was
335  // found in the ENV we simply return.
336  return;
337  }
338 
339  load_ngap_s3_credentials();
340 
341  if(!file_exists(config_file)){
342  BESDEBUG(CREDS, prolog << "The file specified by the BES key " << CATALOG_MANAGER_CREDENTIALS
343  << " does not exist. No Access Credentials were loaded." << endl);
344  return;
345  }
346 
347  if (!file_is_secured(config_file)) {
348  string err;
349  err.append("CredentialsManager config file ");
350  err.append(config_file);
351  err.append(" is not secured! ");
352  err.append("Set the access permissions to -rw------- (600) and try again.");
353  throw BESInternalError(err, __FILE__, __LINE__);
354  }
355  BESDEBUG(CREDS, prolog << "The config file '" << config_file << "' is secured." << endl);
356 
357  map <string, vector<string>> keystore;
358 
359  kvp::load_keys(config_file, keystore);
360 
361  for(map <string, vector<string>>::iterator it=keystore.begin(); it!=keystore.end(); it++) {
362  string creds_name = it->first;
363  vector<string> &credentials_entries = it->second;
364  map<string, AccessCredentials *>::iterator mit;
365  mit = credential_sets.find(creds_name);
366  if (mit != credential_sets.end()) { // New?
367  // Nope.
368  accessCredentials = mit->second;
369  } else {
370  // Make new one
371  accessCredentials = new AccessCredentials(creds_name);
372  credential_sets.insert(pair<string, AccessCredentials *>(creds_name, accessCredentials));
373  }
374  for (vector<string>::iterator jt = credentials_entries.begin(); jt != credentials_entries.end(); jt++) {
375  string credentials_entry = *jt;
376  int index = credentials_entry.find(":");
377  if (index > 0) {
378  string key_name = credentials_entry.substr(0, index);
379  string value = credentials_entry.substr(index + 1);
380  BESDEBUG(CREDS, prolog << creds_name << ":" << key_name << "=" << value << endl);
381  accessCredentials->add(key_name, value);
382  }
383  }
384  }
385  BESDEBUG(CREDS, prolog << "Loaded " << credential_sets.size() << " AccessCredentials" << endl);
386  vector<AccessCredentials *> bad_creds;
387  map<string,AccessCredentials *>::iterator acit;
388 
389  for (acit = credential_sets.begin(); acit != credential_sets.end(); acit++) {
390  accessCredentials = acit->second;
391  string url = accessCredentials->get(AccessCredentials::URL_KEY);
392  if(url.length()){
393  theCM()->add(url,accessCredentials);
394  }
395  else {
396  bad_creds.push_back(acit->second);
397  }
398  }
399  if(bad_creds.size()){
400  stringstream ss;
401  vector<AccessCredentials * >::iterator bc;
402 
403  ss << "Encountered " << bad_creds.size() << " AccessCredentials "
404  << " definitions missing an associated URL. offenders: ";
405 
406  for (bc = bad_creds.begin(); bc != bad_creds.end(); bc++) {
407  ss << (*bc)->name() << " ";
408  credential_sets.erase((*bc)->name());
409  delete *bc;
410  }
411  throw BESInternalError( ss.str(), __FILE__, __LINE__);
412  }
413  BESDEBUG(CREDS, prolog << "Successfully ingested " << theCM()->size() << " AccessCredentials" << endl);
414 
415 }
416 
417 
426 AccessCredentials *CredentialsManager::load_credentials_from_env( ) {
427 
428  // This lock is a RAII implementation. It will block until the mutex is
429  // available and the lock will be released when the instance is destroyed.
430  std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
431 
432  AccessCredentials *ac = nullptr;
433  string env_url, env_id, env_access_key, env_region, env_bucket;
434 
435  // If we are in developer mode then we compile this section which
436  // allows us to inject credentials via the system environment
437 
438  env_id.assign( get_env_value(CredentialsManager::ENV_ID_KEY));
439  env_access_key.assign(get_env_value(CredentialsManager::ENV_ACCESS_KEY));
440  env_region.assign( get_env_value(CredentialsManager::ENV_REGION_KEY));
441  //env_bucket.assign( get_env_value(CredentialsManager::ENV_BUCKET_KEY));
442  env_url.assign( get_env_value(CredentialsManager::ENV_URL_KEY));
443 
444  if(env_url.length() &&
445  env_id.length() &&
446  env_access_key.length() &&
447  // env_bucket.length() &&
448  env_region.length() ){
449  ac = new AccessCredentials();
450  ac->add(AccessCredentials::URL_KEY, env_url);
451  ac->add(AccessCredentials::ID_KEY, env_id);
452  ac->add(AccessCredentials::KEY_KEY, env_access_key);
453  ac->add(AccessCredentials::REGION_KEY, env_region);
454  // ac->add(AccessCredentials::BUCKET_KEY, env_bucket);
455  }
456  return ac;
457 }
458 
459 
460 std::string NGAP_S3_BASE_DEFAULT="https://";
465 void CredentialsManager::load_ngap_s3_credentials( ){
466  // This lock is a RAII implementation. It will block until the mutex is
467  // available and the lock will be released when the instance is destroyed.
468  std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
469 
470  string s3_distribution_endpoint_url;
471  bool found;
472  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY,s3_distribution_endpoint_url,found);
473  if(found) {
474  string value;
475 
476  long refresh_margin = 600;
477  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_REFRESH_KEY, value, found);
478  if (found) {
479  refresh_margin = strtol(value.c_str(), 0, 10);
480  }
481 
482  string s3_base_url = NGAP_S3_BASE_DEFAULT;
483  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_URL_BASE, value, found);
484  if (found) {
485  s3_base_url = value;
486  }
487 
488  NgapS3Credentials *nsc = new NgapS3Credentials(s3_distribution_endpoint_url, refresh_margin);
489  nsc->add(NgapS3Credentials::URL_KEY, s3_base_url);
490  nsc->name("NgapS3Credentials");
491 
492  CredentialsManager::theCM()->add(s3_base_url,nsc);
493  CredentialsManager::theCM()->ngaps3CredentialsLoaded = true;
494 
495  }
496  else {
497  BESDEBUG(CREDS,prolog << "WARNING: The BES configuration did not contain an instance of " <<
498  NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY <<
499  " NGAP S3 Credentials NOT loaded." << endl);
500  }
501 }
502 
void add(const std::string &key, const std::string &value)
Add the key and value pair.
virtual std::string get(const std::string &key)
exception thrown if internal error encountered
void add(const std::string &url, AccessCredentials *ac)
static CredentialsManager * theMngr
AccessCredentials * get(std::shared_ptr< http::url > &url)
static CredentialsManager * theCM()
Returns the singleton instance of the CrednetialsManager.
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:340
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71