bes Updated for version 3.20.13
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// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
98//
99// class CredentialsManager
100//
101
107
108 std::call_once(d_cmac_init_once,CredentialsManager::initialize_instance);
109 return theMngr;
110}
111
112void CredentialsManager::initialize_instance()
113{
115#ifdef HAVE_ATEXIT
116 atexit(delete_instance);
117#endif
118
119}
120
124CredentialsManager::CredentialsManager(): ngaps3CredentialsLoaded(false){
125 // d_netrc_filename = curl::get_netrc_filename();
126}
127
128
133 for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
134 delete it->second;
135 }
136 creds.clear();
137}
138
139
143void CredentialsManager::delete_instance()
144{
145 delete theMngr;
146 theMngr = 0;
147}
148
149
155void
156CredentialsManager::add(const std::string &key, AccessCredentials *ac){
157 // This lock is a RAII implementation. It will block until the mutex is
158 // available and the lock will be released when the instance is destroyed.
159 std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
160
161 creds.insert(std::pair<std::string,AccessCredentials *>(key, ac));
162 BESDEBUG(CREDS, prolog << "Added AccessCredentials to CredentialsManager. credentials: " << endl << ac->to_json() << endl);
163}
164
172CredentialsManager::get(shared_ptr<http::url> &url){
173 // This lock is a RAII implementation. It will block until the mutex is
174 // available and the lock will be released when the instance is destroyed.
175 std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
176
177 AccessCredentials *best_match = NULL;
178 std::string best_key("");
179
180 if(url->protocol() == HTTP_PROTOCOL || url->protocol() == HTTPS_PROTOCOL) {
181 for (std::map<std::string, AccessCredentials *>::iterator it = creds.begin(); it != creds.end(); ++it) {
182 std::string key = it->first;
183 if (url->str().rfind(key, 0) == 0) {
184 // url starts with key
185 if (key.length() > best_key.length()) {
186 best_key = key;
187 best_match = it->second;
188 }
189 }
190 }
191 }
192 return best_match;
193}
194
200bool file_exists(const string &filename) {
201 struct stat buffer;
202 return (stat (filename.c_str(), &buffer) == 0);
203}
204
225bool file_is_secured(const string &filename) {
226 struct stat st;
227 if (stat(filename.c_str(), &st) != 0) {
228 string err;
229 err.append("file_is_secured() Unable to access file ");
230 err.append(filename).append(" strerror: ").append(strerror(errno));
231 throw BESInternalError(err, __FILE__, __LINE__);
232 }
233
234 mode_t perm = st.st_mode;
235 bool status;
236 status = (perm & S_IRUSR) && !(
237 // (perm & S_IWUSR) || // We don't need to enforce user no write
238 (perm & S_IXUSR) ||
239 (perm & S_IRGRP) ||
240 (perm & S_IWGRP) ||
241 (perm & S_IXGRP) ||
242 (perm & S_IROTH) ||
243 (perm & S_IWOTH) ||
244 (perm & S_IXOTH));
245 BESDEBUG(CREDS, prolog << "file_is_secured() " << filename << " secured: " << (status ? "true" : "false") << endl);
246 return status;
247}
248
280
281 // This lock is a RAII implementation. It will block until the mutex is
282 // available and the lock will be released when the instance is destroyed.
283 std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
284
285 bool found_key = true;
286 AccessCredentials *accessCredentials;
287 map<string, AccessCredentials *> credential_sets;
288
289 string config_file;
290 TheBESKeys::TheKeys()->get_value(CATALOG_MANAGER_CREDENTIALS, config_file, found_key);
291 if(!found_key){
292 BESDEBUG(CREDS, prolog << "The BES key " << CATALOG_MANAGER_CREDENTIALS
293 << " was not found in the BES configuration tree. No AccessCredentials were loaded" << endl);
294 return;
295 }
296
297 // Does the configuration indicate that credentials will be submitted via the runtime environment?
298 if(config_file == string(CredentialsManager::USE_ENV_CREDS_KEY_VALUE)){
299 // Apparently so...
300 accessCredentials = theCM()->load_credentials_from_env();
301 if(accessCredentials){
302 // So if we have them, we add them to theCM() and then return without processing the configuration.
303 string url = accessCredentials->get(AccessCredentials::URL_KEY);
304 theCM()->add(url,accessCredentials);
305 }
306 // Environment injected credentials override all other configuration credentials.
307 // Since the value of CATALOG_MANAGER_CREDENTIALS is ENV_CREDS_VALUE there is no
308 // Configuration file identified, so wether or not valid credentials information was
309 // found in the ENV we simply return.
310 return;
311 }
312
313 load_ngap_s3_credentials();
314
315 if(!file_exists(config_file)){
316 BESDEBUG(CREDS, prolog << "The file specified by the BES key " << CATALOG_MANAGER_CREDENTIALS
317 << " does not exist. No Access Credentials were loaded." << endl);
318 return;
319 }
320
321 if (!file_is_secured(config_file)) {
322 string err;
323 err.append("CredentialsManager config file ");
324 err.append(config_file);
325 err.append(" is not secured! ");
326 err.append("Set the access permissions to -rw------- (600) and try again.");
327 throw BESInternalError(err, __FILE__, __LINE__);
328 }
329 BESDEBUG(CREDS, prolog << "The config file '" << config_file << "' is secured." << endl);
330
331 map <string, vector<string>> keystore;
332
333 kvp::load_keys(config_file, keystore);
334
335 for(map <string, vector<string>>::iterator it=keystore.begin(); it!=keystore.end(); it++) {
336 string creds_name = it->first;
337 vector<string> &credentials_entries = it->second;
338 map<string, AccessCredentials *>::iterator mit;
339 mit = credential_sets.find(creds_name);
340 if (mit != credential_sets.end()) { // New?
341 // Nope.
342 accessCredentials = mit->second;
343 } else {
344 // Make new one
345 accessCredentials = new AccessCredentials(creds_name);
346 credential_sets.insert(pair<string, AccessCredentials *>(creds_name, accessCredentials));
347 }
348 for (vector<string>::iterator jt = credentials_entries.begin(); jt != credentials_entries.end(); jt++) {
349 string credentials_entry = *jt;
350 int index = credentials_entry.find(":");
351 if (index > 0) {
352 string key_name = credentials_entry.substr(0, index);
353 string value = credentials_entry.substr(index + 1);
354 BESDEBUG(CREDS, prolog << creds_name << ":" << key_name << "=" << value << endl);
355 accessCredentials->add(key_name, value);
356 }
357 }
358 }
359 BESDEBUG(CREDS, prolog << "Loaded " << credential_sets.size() << " AccessCredentials" << endl);
360 vector<AccessCredentials *> bad_creds;
361 map<string,AccessCredentials *>::iterator acit;
362
363 for (acit = credential_sets.begin(); acit != credential_sets.end(); acit++) {
364 accessCredentials = acit->second;
365 string url = accessCredentials->get(AccessCredentials::URL_KEY);
366 if(url.length()){
367 theCM()->add(url,accessCredentials);
368 }
369 else {
370 bad_creds.push_back(acit->second);
371 }
372 }
373 if(bad_creds.size()){
374 stringstream ss;
375 vector<AccessCredentials * >::iterator bc;
376
377 ss << "Encountered " << bad_creds.size() << " AccessCredentials "
378 << " definitions missing an associated URL. offenders: ";
379
380 for (bc = bad_creds.begin(); bc != bad_creds.end(); bc++) {
381 ss << (*bc)->name() << " ";
382 credential_sets.erase((*bc)->name());
383 delete *bc;
384 }
385 throw BESInternalError( ss.str(), __FILE__, __LINE__);
386 }
387 BESDEBUG(CREDS, prolog << "Successfully ingested " << theCM()->size() << " AccessCredentials" << endl);
388
389}
390
391
400AccessCredentials *CredentialsManager::load_credentials_from_env( ) {
401
402 // This lock is a RAII implementation. It will block until the mutex is
403 // available and the lock will be released when the instance is destroyed.
404 std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
405
406 AccessCredentials *ac = nullptr;
407 string env_url, env_id, env_access_key, env_region, env_bucket;
408
409 // If we are in developer mode then we compile this section which
410 // allows us to inject credentials via the system environment
411
412 env_id.assign( get_env_value(CredentialsManager::ENV_ID_KEY));
413 env_access_key.assign(get_env_value(CredentialsManager::ENV_ACCESS_KEY));
414 env_region.assign( get_env_value(CredentialsManager::ENV_REGION_KEY));
415 //env_bucket.assign( get_env_value(CredentialsManager::ENV_BUCKET_KEY));
416 env_url.assign( get_env_value(CredentialsManager::ENV_URL_KEY));
417
418 if(env_url.length() &&
419 env_id.length() &&
420 env_access_key.length() &&
421 // env_bucket.length() &&
422 env_region.length() ){
423 ac = new AccessCredentials();
424 ac->add(AccessCredentials::URL_KEY, env_url);
425 ac->add(AccessCredentials::ID_KEY, env_id);
426 ac->add(AccessCredentials::KEY_KEY, env_access_key);
427 ac->add(AccessCredentials::REGION_KEY, env_region);
428 // ac->add(AccessCredentials::BUCKET_KEY, env_bucket);
429 }
430 return ac;
431}
432
433
434std::string NGAP_S3_BASE_DEFAULT="https://";
439void CredentialsManager::load_ngap_s3_credentials( ){
440 // This lock is a RAII implementation. It will block until the mutex is
441 // available and the lock will be released when the instance is destroyed.
442 std::lock_guard<std::recursive_mutex> lock_me(d_lock_mutex);
443
444 string s3_distribution_endpoint_url;
445 bool found;
446 TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY,s3_distribution_endpoint_url,found);
447 if(found) {
448 string value;
449
450 long refresh_margin = 600;
451 TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_REFRESH_KEY, value, found);
452 if (found) {
453 refresh_margin = strtol(value.c_str(), 0, 10);
454 }
455
456 string s3_base_url = NGAP_S3_BASE_DEFAULT;
457 TheBESKeys::TheKeys()->get_value(NgapS3Credentials::BES_CONF_URL_BASE, value, found);
458 if (found) {
459 s3_base_url = value;
460 }
461
462 NgapS3Credentials *nsc = new NgapS3Credentials(s3_distribution_endpoint_url, refresh_margin);
463 nsc->add(NgapS3Credentials::URL_KEY, s3_base_url);
464 nsc->name("NgapS3Credentials");
465
466 CredentialsManager::theCM()->add(s3_base_url,nsc);
467 CredentialsManager::theCM()->ngaps3CredentialsLoaded = true;
468
469 }
470 else {
471 BESDEBUG(CREDS,prolog << "WARNING: The BES configuration did not contain an instance of " <<
472 NgapS3Credentials::BES_CONF_S3_ENDPOINT_KEY <<
473 " NGAP S3 Credentials NOT loaded." << endl);
474 }
475}
476
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