diff --git a/README.md b/README.md index 13c6c0a..bfb50ff 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,9 @@ This shared library makes use of `aws-sdk-cpp` internally and leaves all S3 cred You can easily build and use `s3fs-fuse-awscred-lib` by following the steps below. _See the `.github/workflows/ci.yml` file for build details._ -### Build +## Build -#### Build and Install AWS-SDK-CPP on Ubuntu20.04 +### Build and Install AWS-SDK-CPP on Ubuntu20.04 ``` $ sudo apt-get install libcurl4-openssl-dev libssl-dev uuid-dev zlib1g-dev libpulse-dev $ git clone --recurse-submodules https://github.com/aws/aws-sdk-cpp @@ -30,12 +30,12 @@ $ make $ sudo make install ``` -#### Install AWS-SDK-CPP by brew on macOS +### Install AWS-SDK-CPP by brew on macOS ``` $ brew install aws-sdk-cpp ``` -#### Build s3fs-fuse-awscred-lib +### Build s3fs-fuse-awscred-lib ``` $ git clone git@github.com:ggtakec/s3fs-fuse-awscred-lib.git $ cd s3fs-fuse-awscred-lib @@ -44,14 +44,14 @@ $ cmake --build build ``` After that, you can find `libs3fsawscred.so` in `build` sub directory. -### Run s3fs +## Run s3fs ``` $ s3fs -o credlib=libs3fsawscred.so -o credlib_opts=Off ``` To specify this `s3fs-fuse-awscred-lib` for s3fs, use the following options: -#### credlib +### credlib An option to specify the `s3fs-fuse-awscred-lib` library. You can specify only the library name or the path to the library file. The s3fs use `dlopen` to search for the specified `s3fs-fuse-awscred-lib` library and load it. @@ -61,20 +61,30 @@ Example: -o credlib=libs3fsawscred.so ``` -#### credlib_opts +### credlib_opts Specifies the options provided by `s3fs-fuse-awscred-lib`. -If you specify `s3fs-fuse-awscred-lib`, you can specify the output level of the debug message shown below for this option: -- Off -- Fatal -- Error -- Warn -- Info -- Debug -- Trace - +- LogLevel +Specify the output level of the debug message shown below for this option: _These options are the same as the log level defined in `aws-sdk-cpp`(Aws::Utils::Logging::LogLevel)._ + - Off + - Fatal + - Error + - Warn + - Info + - Debug + - Trace +- SSOProfile(SSOProf) +Specify the SSO profile name. _(mainly the name written in sso-session in `.aws/config`.)_ +_This DSO cannot handle that authentication callback when it comes to SSO, so it is a temporary token acquisition._ + +If you want to specify multiple options above, please specify them using a comma(`,`) as a delimiter. + +For the LogLevel option, you can omit `LogLevel` and specify its value directly. +For example, `Loglevel=Info` is the same as `Info`. Example: ``` +-o credlib_opts="Loglevel=Info" -o credlib_opts=Info +-o credlib_opts="Loglevel=Info,SSOProfile=MyProf" ``` diff --git a/awscred.cpp b/awscred.cpp index 649b413..eab9f20 100644 --- a/awscred.cpp +++ b/awscred.cpp @@ -30,15 +30,21 @@ static const char S3fsDefaultCredentialsProviderChainTag[] = "DefaultAWSCreden //---------------------------------------------------------- // Methods : S3fsAWSCredentialsProviderChain //---------------------------------------------------------- -S3fsAWSCredentialsProviderChain::S3fsAWSCredentialsProviderChain() : Aws::Auth::AWSCredentialsProviderChain() +S3fsAWSCredentialsProviderChain::S3fsAWSCredentialsProviderChain(const char* ssoprofile) : Aws::Auth::AWSCredentialsProviderChain() { AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); - AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); - + + // SSO + if(ssoprofile){ + AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag, ssoprofile)); + }else{ + AddProvider(Aws::MakeShared(S3fsDefaultCredentialsProviderChainTag)); + } + // // ECS TaskRole Credentials only available when ENVIRONMENT VARIABLE is set // diff --git a/awscred.h b/awscred.h index df6c3f8..cf30ffc 100644 --- a/awscred.h +++ b/awscred.h @@ -36,7 +36,7 @@ class S3fsAWSCredentialsProviderChain : public Aws::Auth::AWSCredentialsProviderChain { public: - S3fsAWSCredentialsProviderChain(); + S3fsAWSCredentialsProviderChain(const char* ssoprofile = nullptr); }; /* diff --git a/awscred_func.cpp b/awscred_func.cpp index f2d8552..8d75a3a 100644 --- a/awscred_func.cpp +++ b/awscred_func.cpp @@ -18,11 +18,61 @@ #include #include +#include +#include #include "config.h" #include "awscred.h" #include "awscred_func.h" +//---------------------------------------------------------- +// S3fsAwsCredParseOption +//---------------------------------------------------------- +static size_t S3fsAwsCredParseOption(const char* options, std::map& opts) +{ + opts.clear(); + if(!options || 0 == strlen(options)){ + return opts.size(); + } + std::string strOptions = options; + + // Cut double/single quote pair + if(2 <= strOptions.length() && (('"' == *strOptions.begin() && '"' == *strOptions.rbegin()) || ('\'' == *strOptions.begin() && '\'' == *strOptions.rbegin()))){ + strOptions = strOptions.substr(1, strOptions.length() - 2); + } + + // Parse ',' and Set option/value + std::string::size_type FoundPos; + do{ + std::string::size_type StartPos = (FoundPos == std::string::npos ? 0 : (FoundPos + 1)); + FoundPos = strOptions.find(',', StartPos); + + std::string FoundPair; + if(FoundPos == std::string::npos){ + FoundPair = strOptions; + }else if(StartPos != FoundPos){ + FoundPair = strOptions.substr(0, FoundPos); + strOptions = strOptions.substr(FoundPos + 1); + } + + if(!FoundPair.empty()){ + std::string strLowKey; + std::string strValue; + std::string::size_type PairEqPos = FoundPair.find('='); + if(PairEqPos == std::string::npos){ + strLowKey = FoundPair; + }else{ + strLowKey = FoundPair.substr(0, PairEqPos); + strValue = FoundPair.substr(++PairEqPos); + } + std::transform(strLowKey.cbegin(), strLowKey.cend(), strLowKey.begin(), ::tolower); + opts[strLowKey] = strValue; + } + }while(FoundPos != std::string::npos); + + return opts.size(); +} + //---------------------------------------------------------- // Aws::SDKOptions //---------------------------------------------------------- @@ -38,6 +88,15 @@ static Aws::SDKOptions& GetSDKOptions() return options; } +//---------------------------------------------------------- +// SSO Profile name +//---------------------------------------------------------- +static Aws::String& GetSSOProfile() +{ + static Aws::String ssoprofile; + return ssoprofile; +} + //---------------------------------------------------------- // Export interface functions //---------------------------------------------------------- @@ -81,26 +140,176 @@ bool InitS3fsCredential(const char* popts, char** pperrstr) // Check option arguments and set it // Aws::SDKOptions& options = GetSDKOptions(); - if(popts && 0 < strlen(popts)){ - if(0 == strcasecmp(popts, "Off")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Off; - }else if(0 == strcasecmp(popts, "Fatal")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Fatal; - }else if(0 == strcasecmp(popts, "Error")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Error; - }else if(0 == strcasecmp(popts, "Warn")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Warn; - }else if(0 == strcasecmp(popts, "Info")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Info; - }else if(0 == strcasecmp(popts, "Debug")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug; - }else if(0 == strcasecmp(popts, "Trace")){ - options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace; - }else{ - if(pperrstr){ - *pperrstr = strdup("Unknown option(Aws::Utils::Logging::LogLevel) is specified."); + + // Parse option string + std::map ParsedOpts; + size_t OptCnt = S3fsAwsCredParseOption(popts, ParsedOpts); + + if(0 < OptCnt){ + bool isSetLogLevel = false; + + for(std::map::const_iterator iter = ParsedOpts.begin(); iter != ParsedOpts.end(); ++iter){ + std::string strLowkey = iter->first; + std::string strValue = iter->second; + + if(0 == strcasecmp(strLowkey.c_str(), "SSOProfile") || 0 == strcasecmp(strLowkey.c_str(), "SSOProf")){ + if(strValue.empty()){ + if(pperrstr){ + *pperrstr = strdup("Option(SSOProfile) value is empty."); + } + return false; + } + + Aws::String& ssoprofile = GetSSOProfile(); + if(!ssoprofile.empty()){ + if(pperrstr){ + *pperrstr = strdup("Already specified SSO Profile name."); + } + return false; + } + ssoprofile = strValue.c_str(); + + }else if(0 == strcasecmp(strLowkey.c_str(), "LogLevel")){ + if(0 == strcasecmp(strValue.c_str(), "Off")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Off; + + }else if(0 == strcasecmp(strValue.c_str(), "Fatal")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Fatal; + + }else if(0 == strcasecmp(strValue.c_str(), "Error")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Error; + + }else if(0 == strcasecmp(strValue.c_str(), "Warn")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Warn; + + }else if(0 == strcasecmp(strValue.c_str(), "Info")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Info; + + }else if(0 == strcasecmp(strValue.c_str(), "Debug")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug; + + }else if(0 == strcasecmp(strValue.c_str(), "Trace")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace; + }else{ + if(pperrstr){ + *pperrstr = strdup("Unknown option(LogLevel) value is specified."); + } + return false; + } + + // [NOTE] + // The key name only(no value) option is an abbreviation for the LogLevel option. + // + }else if(0 == strcasecmp(strLowkey.c_str(), "Off")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Off; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Fatal")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Fatal; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Error")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Error; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Warn")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Warn; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Info")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Info; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Debug")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Debug; + + }else if(0 == strcasecmp(strLowkey.c_str(), "Trace")){ + if(isSetLogLevel){ + if(pperrstr){ + *pperrstr = strdup("Option(LogLevel) is already specified, so could not set Off."); + } + return false; + } + options.loggingOptions.logLevel = Aws::Utils::Logging::LogLevel::Trace; + }else{ + if(pperrstr){ + *pperrstr = strdup("Unknown option is specified."); + } + return false; } - return false; } } @@ -144,7 +353,12 @@ bool UpdateS3fsCredential(char** ppaccess_key_id, char** ppserect_access_key, ch *pperrstr = NULL; } - S3fsAWSCredentialsProviderChain providerChains; + // Get SSO Profile option + const Aws::String& ssoprofile = GetSSOProfile(); + const char* pSSOProf = ssoprofile.empty() ? nullptr : ssoprofile.c_str(); + + // Create provider chain + S3fsAWSCredentialsProviderChain providerChains(pSSOProf); Aws::SDKOptions& options = GetSDKOptions(); auto credentials = providerChains.GetAWSCredentials(); bool result = true;