bes  Updated for version 3.20.6
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 <cstring>
29 #include <iomanip>
30 #include <sstream>
31 #include <locale>
32 #include <string>
33 #include <sys/stat.h>
34 
35 #include <WhiteList.h>
36 #include <TheBESKeys.h>
37 #include <kvp_utils.h>
38 #include <BESInternalError.h>
39 #include <BESDebug.h>
40 
41 #include "CredentialsManager.h"
42 
43 using namespace std;
44 
45 #define MODULE "dmrpp:creds"
46 
51 
52 // Scope: public members of AccessCredentials
53 const string AccessCredentials::ID_KEY="id";
54 const string AccessCredentials::KEY_KEY="key";
55 const string AccessCredentials::REGION_KEY="region";
56 const string AccessCredentials::BUCKET_KEY="bucket";
57 const string AccessCredentials::URL_KEY="url";
58 
59 // Scope: public members of CredentialsManager
60 const string CredentialsManager::ENV_ID_KEY="CMAC_ID";
61 const string CredentialsManager::ENV_ACCESS_KEY="CMAC_ACCESS_KEY";
62 const string CredentialsManager::ENV_REGION_KEY="CMAC_REGION";
63 const string CredentialsManager::ENV_BUCKET_KEY="CMAC_BUCKET";
64 const string CredentialsManager::ENV_URL_KEY="CMAC_URL";
65 const string CredentialsManager::ENV_CREDS_KEY_VALUE="ENV_CREDS";
66 
67 
68 
77 std::string get_env_value(const string &key){
78  string value;
79  const char *cstr = getenv(key.c_str());
80  if(cstr){
81  value.assign(cstr);
82  BESDEBUG(MODULE, __FILE__ << " " << __LINE__ << " From system environment - " << key << ": " << value << endl);
83  }
84  else {
85  value.clear();
86  }
87  return value;
88 }
89 
98 std::string get_config_value(const string &key){
99  string value;
100  bool key_found=false;
101  TheBESKeys::TheKeys()->get_value(key, value, key_found);
102  if (key_found) {
103  BESDEBUG(MODULE, __FILE__ << " " << __LINE__ << " Using " << key << " from TheBESKeys" << endl);
104  }
105  else {
106  value.clear();
107  }
108  return value;
109 }
110 
115  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
116  delete it->second;
117  }
118  creds.clear();
119 }
120 
124 CredentialsManager::CredentialsManager(){}
125 
129 void CredentialsManager::initialize_instance()
130 {
131  theMngr = new CredentialsManager;
132 #ifdef HAVE_ATEXIT
133  atexit(delete_instance);
134 #endif
135 
136 }
137 
141 void CredentialsManager::delete_instance()
142 {
143  delete theMngr;
144  theMngr = 0;
145 }
146 
147 
153 void
154 CredentialsManager::add(const std::string &key, AccessCredentials *ac){
155  creds.insert(std::pair<std::string,AccessCredentials *>(key, ac));
156  BESDEBUG(MODULE, "Added AccessCredentials to CredentialsManager. credentials: " << endl << ac->to_json() << endl);
157 }
158 
166 CredentialsManager::get(const std::string &url){
167  AccessCredentials *best_match = NULL;
168  std::string best_key("");
169 
170  if(url.find("http://") == 0 || url.find("https://") == 0) {
171  for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
172  std::string key = it->first;
173  if (url.rfind(key, 0) == 0) {
174  // url starts with key
175  if (key.length() > best_key.length()) {
176  best_key = key;
177  best_match = it->second;
178  }
179  }
180  }
181  }
182  return best_match;
183 }
184 
190 bool file_exists(const string &filename) {
191  struct stat buffer;
192  return (stat (filename.c_str(), &buffer) == 0);
193 }
194 
215 bool file_is_secured(const string &filename) {
216  struct stat st;
217  if (stat(filename.c_str(), &st) != 0) {
218  string err;
219  err.append("file_is_secured() Unable to access file ");
220  err.append(filename).append(" strerror: ").append(strerror(errno));
221  throw BESInternalError(err, __FILE__, __LINE__);
222  }
223 
224  mode_t perm = st.st_mode;
225  bool status;
226  status = (perm & S_IRUSR) && !(
227  // (perm & S_IWUSR) || // We don't need to enforce user no write
228  (perm & S_IXUSR) ||
229  (perm & S_IRGRP) ||
230  (perm & S_IWGRP) ||
231  (perm & S_IXGRP) ||
232  (perm & S_IROTH) ||
233  (perm & S_IWOTH) ||
234  (perm & S_IXOTH));
235  BESDEBUG(MODULE, "file_is_secured() " << filename << " secured: " << (status ? "true" : "false") << endl);
236  return status;
237 }
238 
270 
271  bool found_key = true;
272  AccessCredentials *accessCredentials;
273  map<string, AccessCredentials *> credential_sets;
274 
275  string config_file;
276  TheBESKeys::TheKeys()->get_value(CATALOG_MANAGER_CREDENTIALS, config_file, found_key);
277  if(!found_key){
278  BESDEBUG(MODULE, "The BES key " << CATALOG_MANAGER_CREDENTIALS
279  << " was not found in the BES configuration tree. No AccessCredentials were loaded" << endl);
280  return;
281  }
282 
283  // Does the configuration indicate that credentials will be submitted via the runtime environment?
284  if(config_file == ENV_CREDS_KEY_VALUE){
285  // Apparently so...
286  accessCredentials = load_credentials_from_env();
287  if(accessCredentials){
288  // So if we have them, we add them to theCM() and then return without processing the configuration.
289  string url = accessCredentials->get(AccessCredentials::URL_KEY);
290  theCM()->add(url,accessCredentials);
291  }
292  // Environment injected credentials override all other configuration credentials.
293  // Since the value of CATALOG_MANAGER_CREDENTIALS is ENV_CREDS_VALUE there is no
294  // Configuration file identified, so wether or not valid credentials information was
295  // found in the ENV we simply return.
296  return;
297  }
298 
299  if(!file_exists(config_file)){
300  BESDEBUG(MODULE, "The file specified by the BES key " << CATALOG_MANAGER_CREDENTIALS
301  << " does not exist. No Access Credentials were loaded." << endl);
302  return;
303  }
304 
305  if (!file_is_secured(config_file)) {
306  string err;
307  err.append("CredentialsManager config file ");
308  err.append(config_file);
309  err.append(" is not secured! ");
310  err.append("Set the access permissions to -rw------- (600) and try again.");
311  throw BESInternalError(err, __FILE__, __LINE__);
312  }
313  BESDEBUG(MODULE, "CredentialsManager config file '" << config_file << "' is secured." << endl);
314 
315  map <string, vector<string>> keystore;
316 
317  kvp::load_keys(config_file, keystore);
318 
319  for(map <string, vector<string>>::iterator it=keystore.begin(); it!=keystore.end(); it++) {
320  string creds_name = it->first;
321  vector<string> &credentials_entries = it->second;
322  map<string, AccessCredentials *>::iterator mit;
323  mit = credential_sets.find(creds_name);
324  if (mit != credential_sets.end()) { // New?
325  // Nope.
326  accessCredentials = mit->second;
327  } else {
328  // Make new one
329  accessCredentials = new AccessCredentials(creds_name);
330  credential_sets.insert(pair<string, AccessCredentials *>(creds_name, accessCredentials));
331  }
332  for (vector<string>::iterator jt = credentials_entries.begin(); jt != credentials_entries.end(); jt++) {
333  string credentials_entry = *jt;
334  int index = credentials_entry.find(":");
335  if (index > 0) {
336  string key_name = credentials_entry.substr(0, index);
337  string value = credentials_entry.substr(index + 1);
338  BESDEBUG(MODULE, creds_name << ":" << key_name << "=" << value << endl);
339  accessCredentials->add(key_name, value);
340  }
341  }
342  }
343  BESDEBUG(MODULE, "CredentialsManager loaded " << credential_sets.size() << " AccessCredentials" << endl);
344  vector<AccessCredentials *> bad_creds;
345  map<string,AccessCredentials *>::iterator acit;
346 
347  for (acit = credential_sets.begin(); acit != credential_sets.end(); acit++) {
348  accessCredentials = acit->second;
349  string url = accessCredentials->get(AccessCredentials::URL_KEY);
350  if(url.length()){
351  theCM()->add(url,accessCredentials);
352  }
353  else {
354  bad_creds.push_back(acit->second);
355  }
356  }
357  if(bad_creds.size()){
358  stringstream ss;
359  vector<AccessCredentials * >::iterator bc;
360 
361  ss << "Encountered " << bad_creds.size() << " AccessCredentials "
362  << " definitions missing an associated URL. offenders: ";
363 
364  for (bc = bad_creds.begin(); bc != bad_creds.end(); bc++) {
365  ss << (*bc)->name() << " ";
366  credential_sets.erase((*bc)->name());
367  delete *bc;
368  }
369  throw BESInternalError( ss.str(), __FILE__, __LINE__);
370  }
371  BESDEBUG(MODULE, "CredentialsManager has successfully ingested " << theCM()->size() << " AccessCredentials" << endl);
372 
373 }
374 AccessCredentials *CredentialsManager::load_credentials_from_env( ) {
375 
376  AccessCredentials *ac = NULL;
377  string env_url, env_id, env_access_key, env_region, env_bucket;
378 
379  // If we are in developer mode then we compile this section which
380  // allows us to inject credentials via the system environment
381 
382  env_id.assign( get_env_value(ENV_ID_KEY));
383  env_access_key.assign(get_env_value(ENV_ACCESS_KEY));
384  env_region.assign( get_env_value(ENV_REGION_KEY));
385  env_bucket.assign( get_env_value(ENV_BUCKET_KEY));
386  env_url.assign( get_env_value(ENV_URL_KEY));
387 
388  if(env_url.length() &&
389  env_id.length() &&
390  env_access_key.length() &&
391  env_region.length() &&
392  env_bucket.length()){
393  ac = new AccessCredentials();
394  ac->add(AccessCredentials::URL_KEY, env_url);
395  ac->add(AccessCredentials::ID_KEY, env_id);
396  ac->add(AccessCredentials::KEY_KEY, env_access_key);
397  ac->add(AccessCredentials::REGION_KEY, env_region);
398  ac->add(AccessCredentials::BUCKET_KEY, env_bucket);
399  }
400  return ac;
401 }
402 
403 /*****************************************************************************************************/
404 /*****************************************************************************************************/
405 /**************************************** AccessCredentials ******************************************/
406 /*****************************************************************************************************/
407 /*****************************************************************************************************/
408 // AccessCredentials methods follow.
409 
410 
411 
417 std::string
418 AccessCredentials::get(const std::string &key){
419  std::map<std::string, std::string>::iterator it;
420  std::string value("");
421  it = kvp.find(key);
422  if (it != kvp.end())
423  value = it->second;
424  return value;
425 }
426 
432 void
434  const std::string &key,
435  const std::string &value){
436  kvp.insert(std::pair<std::string, std::string>(key, value));
437 }
438 
444  if(!s3_tested){
445  is_s3 = get(URL_KEY).length()>0 &&
446  get(ID_KEY).length()>0 &&
447  get(KEY_KEY).length()>0 &&
448  get(REGION_KEY).length()>0 &&
449  get(BUCKET_KEY).length()>0;
450  s3_tested = true;
451  }
452  return is_s3;
453 }
454 
455 string AccessCredentials::to_json(){
456  stringstream ss;
457  ss << "{" << endl << " \"AccessCredentials\": { " << endl;
458  ss << " \"name\": \"" << d_config_name << "\"," << endl;
459  for (std::map<string, string>::iterator it = kvp.begin(); it != kvp.end(); ++it) {
460  std::string key = it->first;
461  std::string value = it->second;
462 
463  if(it!=kvp.begin())
464  ss << ", " << endl ;
465 
466  ss << " \"" << it->first << "\": \"" << it->second << "\"";
467  }
468  ss << endl << " }" << endl << "}" << endl;
469  return ss.str();
470 }
CredentialsManager
Definition: CredentialsManager.h:63
AccessCredentials::get
std::string get(const std::string &key)
Definition: CredentialsManager.cc:418
CredentialsManager::add
void add(const std::string &url, AccessCredentials *ac)
Definition: CredentialsManager.cc:154
AccessCredentials::add
void add(const std::string &key, const std::string &value)
Definition: CredentialsManager.cc:433
TheBESKeys::TheKeys
static TheBESKeys * TheKeys()
Definition: TheBESKeys.cc:62
CredentialsManager::load_credentials
static void load_credentials()
Definition: CredentialsManager.cc:269
BESInternalError
exception thrown if internal error encountered
Definition: BESInternalError.h:43
TheBESKeys::get_value
void get_value(const std::string &s, std::string &val, bool &found)
Retrieve the value of a given key, if set.
Definition: TheBESKeys.cc:272
CredentialsManager::get
AccessCredentials * get(const std::string &url)
Definition: CredentialsManager.cc:166
AccessCredentials
Definition: CredentialsManager.h:36
CredentialsManager::~CredentialsManager
~CredentialsManager()
Definition: CredentialsManager.cc:114
AccessCredentials::isS3Cred
bool isS3Cred()
Definition: CredentialsManager.cc:443
CredentialsManager::theMngr
static CredentialsManager * theMngr
Definition: CredentialsManager.h:81