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
48using namespace std;
49
50#define prolog std::string("CredentialsManager::").append(__func__).append("() - ")
51
52// Class vocabulary
53const char *CredentialsManager::ENV_ID_KEY = "CMAC_ID";
54const char *CredentialsManager::ENV_ACCESS_KEY = "CMAC_ACCESS_KEY";
55const char *CredentialsManager::ENV_REGION_KEY = "CMAC_REGION";
56const char *CredentialsManager::ENV_BUCKET_KEY = "CMAC_BUCKET";
57const char *CredentialsManager::ENV_URL_KEY = "CMAC_URL";
58
59const char *CredentialsManager::USE_ENV_CREDS_KEY_VALUE = "ENV_CREDS";
60
65
69static std::once_flag d_cmac_init_once;
70
71
72// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
73//
74// Helper Functions
75//
84std::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
106std::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
138void CredentialsManager::initialize_instance()
139{
141#ifdef HAVE_ATEXIT
142 atexit(delete_instance);
143#endif
144
145}
146
150CredentialsManager::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
169void CredentialsManager::delete_instance()
170{
171 delete theMngr;
172 theMngr = 0;
173}
174
175
181void
182CredentialsManager::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
198CredentialsManager::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
226bool file_exists(const string &filename) {
227 struct stat buffer;
228 return (stat (filename.c_str(), &buffer) == 0);
229}
230
251bool 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
426AccessCredentials *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
460std::string NGAP_S3_BASE_DEFAULT="https://";
465void 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