bes  Updated for version 3.20.8
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 
43 #include "CredentialsManager.h"
44 #include "NgapS3Credentials.h"
45 #include "DmrppNames.h"
46 
47 using namespace std;
48 
49 
50 #define prolog std::string("CredentialsManager::").append(__func__).append("() - ")
51 
56 
57 // Scope: public members of CredentialsManager
58 const string CredentialsManager::ENV_ID_KEY="CMAC_ID";
59 const string CredentialsManager::ENV_ACCESS_KEY="CMAC_ACCESS_KEY";
60 const string CredentialsManager::ENV_REGION_KEY="CMAC_REGION";
61 //const string CredentialsManager::ENV_BUCKET_KEY="CMAC_BUCKET";
62 const string CredentialsManager::ENV_URL_KEY="CMAC_URL";
63 const string CredentialsManager::ENV_CREDS_KEY_VALUE="ENV_CREDS";
64 
65 
74 std::string get_env_value(const string &key){
75  string value;
76  const char *cstr = getenv(key.c_str());
77  if(cstr){
78  value.assign(cstr);
79  BESDEBUG(CREDS, prolog << "From system environment - " << key << ": " << value << endl);
80  }
81  else {
82  value.clear();
83  }
84  return value;
85 }
86 
95 std::string get_config_value(const string &key){
96  string value;
97  bool key_found=false;
98  TheBESKeys::TheKeys()->get_value(key, value, key_found);
99  if (key_found) {
100  BESDEBUG(CREDS, prolog << "Using " << key << " from TheBESKeys" << endl);
101  }
102  else {
103  value.clear();
104  }
105  return value;
106 }
107 
112  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
113  delete it->second;
114  }
115  creds.clear();
116 }
117 
121 CredentialsManager::CredentialsManager(): ngaps3CredentialsLoaded(false){
122  d_netrc_filename = curl::get_netrc_filename();
123 }
124 
128 void CredentialsManager::initialize_instance()
129 {
131 #ifdef HAVE_ATEXIT
132  atexit(delete_instance);
133 #endif
134 
135 }
136 
140 void CredentialsManager::delete_instance()
141 {
142  delete theMngr;
143  theMngr = 0;
144 }
145 
146 
152 void
153 CredentialsManager::add(const std::string &key, AccessCredentials *ac){
154  creds.insert(std::pair<std::string,AccessCredentials *>(key, ac));
155  BESDEBUG(CREDS, prolog << "Added AccessCredentials to CredentialsManager. credentials: " << endl << ac->to_json() << endl);
156 }
157 
165 CredentialsManager::get(const std::string &url){
166  AccessCredentials *best_match = NULL;
167  std::string best_key("");
168 
169  if(url.find("http://") == 0 || url.find("https://") == 0) {
170  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
171  std::string key = it->first;
172  if (url.rfind(key, 0) == 0) {
173  // url starts with key
174  if (key.length() > best_key.length()) {
175  best_key = key;
176  best_match = it->second;
177  }
178  }
179  }
180  }
181  return best_match;
182 }
183 
189 bool file_exists(const string &filename) {
190  struct stat buffer;
191  return (stat (filename.c_str(), &buffer) == 0);
192 }
193 
214 bool file_is_secured(const string &filename) {
215  struct stat st;
216  if (stat(filename.c_str(), &st) != 0) {
217  string err;
218  err.append("file_is_secured() Unable to access file ");
219  err.append(filename).append(" strerror: ").append(strerror(errno));
220  throw BESInternalError(err, __FILE__, __LINE__);
221  }
222 
223  mode_t perm = st.st_mode;
224  bool status;
225  status = (perm & S_IRUSR) && !(
226  // (perm & S_IWUSR) || // We don't need to enforce user no write
227  (perm & S_IXUSR) ||
228  (perm & S_IRGRP) ||
229  (perm & S_IWGRP) ||
230  (perm & S_IXGRP) ||
231  (perm & S_IROTH) ||
232  (perm & S_IWOTH) ||
233  (perm & S_IXOTH));
234  BESDEBUG(CREDS, prolog << "file_is_secured() " << filename << " secured: " << (status ? "true" : "false") << endl);
235  return status;
236 }
237 
269 
270  bool found_key = true;
271  AccessCredentials *accessCredentials;
272  map<string, AccessCredentials *> credential_sets;
273 
274  string config_file;
275  TheBESKeys::TheKeys()->get_value(CATALOG_MANAGER_CREDENTIALS, config_file, found_key);
276  if(!found_key){
277  BESDEBUG(CREDS, prolog << "The BES key " << CATALOG_MANAGER_CREDENTIALS
278  << " was not found in the BES configuration tree. No AccessCredentials were loaded" << endl);
279  return;
280  }
281 
282  // Does the configuration indicate that credentials will be submitted via the runtime environment?
283  if(config_file == ENV_CREDS_KEY_VALUE){
284  // Apparently so...
285  accessCredentials = theCM()->load_credentials_from_env();
286  if(accessCredentials){
287  // So if we have them, we add them to theCM() and then return without processing the configuration.
288  string url = accessCredentials->get(AccessCredentials::URL_KEY);
289  theCM()->add(url,accessCredentials);
290  }
291  // Environment injected credentials override all other configuration credentials.
292  // Since the value of CATALOG_MANAGER_CREDENTIALS is ENV_CREDS_VALUE there is no
293  // Configuration file identified, so wether or not valid credentials information was
294  // found in the ENV we simply return.
295  return;
296  }
297 
298  theCM()->load_ngap_s3_credentials();
299 
300  if(!file_exists(config_file)){
301  BESDEBUG(CREDS, prolog << "The file specified by the BES key " << CATALOG_MANAGER_CREDENTIALS
302  << " does not exist. No Access Credentials were loaded." << endl);
303  return;
304  }
305 
306  if (!file_is_secured(config_file)) {
307  string err;
308  err.append("CredentialsManager config file ");
309  err.append(config_file);
310  err.append(" is not secured! ");
311  err.append("Set the access permissions to -rw------- (600) and try again.");
312  throw BESInternalError(err, __FILE__, __LINE__);
313  }
314  BESDEBUG(CREDS, prolog << "The config file '" << config_file << "' is secured." << endl);
315 
316  map <string, vector<string>> keystore;
317 
318  kvp::load_keys(config_file, keystore);
319 
320  for(map <string, vector<string>>::iterator it=keystore.begin(); it!=keystore.end(); it++) {
321  string creds_name = it->first;
322  vector<string> &credentials_entries = it->second;
323  map<string, AccessCredentials *>::iterator mit;
324  mit = credential_sets.find(creds_name);
325  if (mit != credential_sets.end()) { // New?
326  // Nope.
327  accessCredentials = mit->second;
328  } else {
329  // Make new one
330  accessCredentials = new AccessCredentials(creds_name);
331  credential_sets.insert(pair<string, AccessCredentials *>(creds_name, accessCredentials));
332  }
333  for (vector<string>::iterator jt = credentials_entries.begin(); jt != credentials_entries.end(); jt++) {
334  string credentials_entry = *jt;
335  int index = credentials_entry.find(":");
336  if (index > 0) {
337  string key_name = credentials_entry.substr(0, index);
338  string value = credentials_entry.substr(index + 1);
339  BESDEBUG(CREDS, prolog << creds_name << ":" << key_name << "=" << value << endl);
340  accessCredentials->add(key_name, value);
341  }
342  }
343  }
344  BESDEBUG(CREDS, prolog << "Loaded " << credential_sets.size() << " AccessCredentials" << endl);
345  vector<AccessCredentials *> bad_creds;
346  map<string,AccessCredentials *>::iterator acit;
347 
348  for (acit = credential_sets.begin(); acit != credential_sets.end(); acit++) {
349  accessCredentials = acit->second;
350  string url = accessCredentials->get(AccessCredentials::URL_KEY);
351  if(url.length()){
352  theCM()->add(url,accessCredentials);
353  }
354  else {
355  bad_creds.push_back(acit->second);
356  }
357  }
358  if(bad_creds.size()){
359  stringstream ss;
360  vector<AccessCredentials * >::iterator bc;
361 
362  ss << "Encountered " << bad_creds.size() << " AccessCredentials "
363  << " definitions missing an associated URL. offenders: ";
364 
365  for (bc = bad_creds.begin(); bc != bad_creds.end(); bc++) {
366  ss << (*bc)->name() << " ";
367  credential_sets.erase((*bc)->name());
368  delete *bc;
369  }
370  throw BESInternalError( ss.str(), __FILE__, __LINE__);
371  }
372  BESDEBUG(CREDS, prolog << "Successfully ingested " << theCM()->size() << " AccessCredentials" << endl);
373 
374 }
375 
376 
381 AccessCredentials *CredentialsManager::load_credentials_from_env( ) {
382 
383  AccessCredentials *ac = NULL;
384  string env_url, env_id, env_access_key, env_region, env_bucket;
385 
386  // If we are in developer mode then we compile this section which
387  // allows us to inject credentials via the system environment
388 
389  env_id.assign( get_env_value(ENV_ID_KEY));
390  env_access_key.assign(get_env_value(ENV_ACCESS_KEY));
391  env_region.assign( get_env_value(ENV_REGION_KEY));
392  //env_bucket.assign( get_env_value(ENV_BUCKET_KEY));
393  env_url.assign( get_env_value(ENV_URL_KEY));
394 
395  if(env_url.length() &&
396  env_id.length() &&
397  env_access_key.length() &&
398  // env_bucket.length() &&
399  env_region.length() ){
400  ac = new AccessCredentials();
401  ac->add(AccessCredentials::URL_KEY, env_url);
402  ac->add(AccessCredentials::ID_KEY, env_id);
403  ac->add(AccessCredentials::KEY_KEY, env_access_key);
404  ac->add(AccessCredentials::REGION_KEY, env_region);
405  // ac->add(AccessCredentials::BUCKET_KEY, env_bucket);
406  }
407  return ac;
408 }
409 
410 
411 std::string NGAP_S3_BASE_DEFAULT="https://";
416 void CredentialsManager::load_ngap_s3_credentials( ){
417  string s3_distribution_endpoint_url;
418  bool found;
419  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY,s3_distribution_endpoint_url,found);
420  if(found) {
421  string value;
422 
423  long refresh_margin = 600;
424  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_REFRESH_KEY, value, found);
425  if (found) {
426  refresh_margin = strtol(value.c_str(), 0, 10);
427  }
428 
429  string s3_base_url = NGAP_S3_BASE_DEFAULT;
430  TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_URL_BASE, value, found);
431  if (found) {
432  s3_base_url = value;
433  }
434 
435  NgapS3Credentials *nsc = new NgapS3Credentials(s3_distribution_endpoint_url, refresh_margin);
436  nsc->add(NgapS3Credentials::URL_KEY, s3_base_url);
437  nsc->name("NgapS3Credentials");
438 
439  CredentialsManager::theCM()->add(s3_base_url,nsc);
440  CredentialsManager::theCM()->ngaps3CredentialsLoaded = true;
441 
442  }
443  else {
444  BESDEBUG(CREDS,prolog << "WARNING: The BES configuration did not contain an instance of " <<
445  NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY <<
446  " NGAP S3 Credentials NOT loaded." << endl);
447  }
448 }
449 
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(const std::string &url)
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:339
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:71