diff options
author | Bulat Gayazov <brgayazov@yandex-team.ru> | 2023-08-24 14:07:47 +0300 |
---|---|---|
committer | brgayazov <bulat@ydb.tech> | 2023-08-24 14:47:31 +0300 |
commit | 509a971515bc6421a4a824fda0a8cb19c683d85f (patch) | |
tree | f38e11f2d86f8ccde4f297190636df21aaa38d28 | |
parent | 0d5dd26da4012de80c0af860d7b244ca2be85cd3 (diff) | |
download | ydb-509a971515bc6421a4a824fda0a8cb19c683d85f.tar.gz |
Added command ydb config info which print current connection parameters
Added command ydb config info which print current connection parameters
Pull Request resolved: #285
-rw-r--r-- | ydb/public/lib/ydb_cli/commands/ydb_profile.cpp | 94 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/commands/ydb_profile.h | 12 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp | 584 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/commands/ydb_root_common.h | 11 | ||||
-rw-r--r-- | ydb/public/lib/ydb_cli/common/command.h | 8 |
5 files changed, 542 insertions, 167 deletions
diff --git a/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp b/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp index 0644465d27..2ca086c606 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_profile.cpp @@ -27,6 +27,7 @@ TCommandConfig::TCommandConfig() : TClientCommandTree("config", {}, "Manage YDB CLI configuration") { AddCommand(std::make_unique<TCommandProfile>()); + AddCommand(std::make_unique<TCommandConnectionInfo>()); } void TCommandConfig::Config(TConfig& config) { @@ -196,6 +197,16 @@ namespace { } } + TString TryBlurValue(const TString& authMethod, const TString& value) { + if (!IsStdoutInteractive() || authMethod == "sa-key-file" || authMethod == "token-file" || authMethod == "yc-token-file") { + return value; + } + if (authMethod == "password") { + return ReplaceWithAsterisks(value); + } + return BlurSecret(value); + } + void PrintProfileContent(std::shared_ptr<IProfile> profile) { if (profile->Has("endpoint")) { Cout << " endpoint: " << profile->GetValue("endpoint").as<TString>() << Endl; @@ -209,14 +220,8 @@ namespace { Cout << " " << authMethod; if (authMethod == "ydb-token" ||authMethod == "iam-token" || authMethod == "yc-token" || authMethod == "sa-key-file" - || authMethod == "token-file" || authMethod == "yc-token-file") - { - TString authData = authValue["data"].as<TString>(); - if (authMethod == "sa-key-file" || authMethod == "token-file" || authMethod == "yc-token-file") { - Cout << ": " << authData; - } else { - Cout << ": " << BlurSecret(authData); - } + || authMethod == "token-file" || authMethod == "yc-token-file") { + Cout << ": " << TryBlurValue(authMethod, authValue["data"].as<TString>()); } else if (authMethod == "static-credentials") { auto authData = authValue["data"]; if (authData) { @@ -224,7 +229,7 @@ namespace { Cout << Endl << " user: " << authData["user"].as<TString>(); } if (authData["password"]) { - Cout << Endl << " password: " << ReplaceWithAsterisks(authData["password"].as<TString>()); + Cout << Endl << " password: " << TryBlurValue("password", authData["password"].as<TString>()); } if (authData["password-file"]) { Cout << Endl << " password file: " << authData["password-file"].as<TString>(); @@ -242,6 +247,77 @@ namespace { } } +TCommandConnectionInfo::TCommandConnectionInfo() + : TClientCommand("info", {}, "List current connection parameters") +{} + +void TCommandConnectionInfo::Config(TConfig& config) { + TClientCommand::Config(config); + + config.NeedToConnect = false; + config.SetFreeArgsNum(0); +} + +int TCommandConnectionInfo::Run(TConfig& config) { + if (config.IsVerbose()) { + PrintVerboseInfo(config); + } else { + PrintInfo(config); + } + return EXIT_SUCCESS; +} + +void TCommandConnectionInfo::PrintInfo(TConfig& config) { + if (config.Address) { + Cout << "endpoint: " << config.Address << Endl; + } + if (config.Database) { + Cout << "database: " << config.Database << Endl; + } + if (config.SecurityToken) { + Cout << "token: " << TryBlurValue("token", config.SecurityToken) << Endl; + } + if (config.UseIamAuth) { + if (config.YCToken) { + Cout << "yc-token: " << TryBlurValue("yc-token", config.YCToken) << Endl; + } + if (config.SaKeyFile) { + Cout << "sa-key-file: " << config.SaKeyFile << Endl; + } + if (config.UseMetadataCredentials) { + Cout << "use-metadata-credentials" << Endl; + } + if (config.IamEndpoint) { + Cout << "iam-endpoint: " << config.IamEndpoint << Endl; + } + } + if (config.UseStaticCredentials) { + if (config.StaticCredentials.User) { + Cout << "user: " << config.StaticCredentials.User << Endl; + } + if (config.StaticCredentials.Password) { + Cout << "password: " << TryBlurValue("password", config.StaticCredentials.Password) << Endl; + } + } + if (config.CaCertsFile) { + Cout << "ca-file: " << config.CaCertsFile << Endl; + } +} + +void TCommandConnectionInfo::PrintVerboseInfo(TConfig& config) { + Cout << Endl; + PrintInfo(config); + Cout << "current auth method: " << (config.ChosenAuthMethod ? config.ChosenAuthMethod : "no-auth") << Endl; + for (const auto& [name, params] : config.ConnectionParams) { + size_t cnt = 1; + Cout << Endl << "\"" << name << "\" sources:" << Endl; + for (const auto& [value, source] : params) { + Cout << " " << cnt << ". Value: " << TryBlurValue(name, value) << ". Got from: " << source << Endl; + ++cnt; + } + } +} + TCommandInit::TCommandInit() : TCommandProfileCommon("init", {}, "YDB CLI initialization") {} diff --git a/ydb/public/lib/ydb_cli/commands/ydb_profile.h b/ydb/public/lib/ydb_cli/commands/ydb_profile.h index 5272fc5985..a7333dd3b0 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_profile.h +++ b/ydb/public/lib/ydb_cli/commands/ydb_profile.h @@ -12,6 +12,18 @@ public: virtual void Config(TConfig& config) override; }; +class TCommandConnectionInfo : public TClientCommand { +public: + TCommandConnectionInfo(); + + virtual void Config(TConfig& config) override; + virtual int Run(TConfig& config) override; + +private: + static void PrintInfo(TConfig& config); + static void PrintVerboseInfo(TConfig& config); +}; + class TCommandProfile : public TClientCommandTree { public: TCommandProfile(); diff --git a/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp b/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp index 5baa84ced0..eae8a8a355 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp +++ b/ydb/public/lib/ydb_cli/commands/ydb_root_common.cpp @@ -19,6 +19,7 @@ #include <util/folder/path.h> #include <util/folder/dirut.h> #include <util/string/strip.h> +#include <util/string/builder.h> #include <util/system/env.h> namespace NYdb { @@ -254,59 +255,145 @@ void TClientCommandRootCommon::Parse(TConfig& config) { config.VerbosityLevel = std::min(static_cast<TConfig::EVerbosityLevel>(VerbosityLevel), TConfig::EVerbosityLevel::DEBUG); } +namespace { + inline void PrintSettingFromProfile(const TString& setting, std::shared_ptr<IProfile> profile, bool explicitOption) { + Cout << "Using " << setting << " due to configuration in" << (explicitOption ? "" : " active") << " profile \"" + << profile->GetName() << "\"" << (explicitOption ? " from explicit --profile option" : "") << Endl; + } + + inline TString GetProfileSource(std::shared_ptr<IProfile> profile, bool explicitOption) { + Y_VERIFY(profile, "No profile to get source"); + if (explicitOption) { + return TStringBuilder() << "profile \"" << profile->GetName() << "\" from explicit --profile option"; + } + return TStringBuilder() << "active profile \"" << profile->GetName() << "\""; + } +} + +bool TClientCommandRootCommon::TryGetParamFromProfile(const TString& name, std::shared_ptr<IProfile> profile, bool explicitOption, + std::function<bool(const TString&, const TString&, bool)> callback) { + if (profile && profile->Has(name)) { + return callback(profile->GetValue(name).as<TString>(), GetProfileSource(profile, explicitOption), explicitOption); + } + return false; +} + void TClientCommandRootCommon::ParseCaCerts(TConfig& config) { - if (CaCertsFile.empty()) { - auto profile = Profile; - if (!profile) { - profile = ProfileManager->GetActiveProfile(); + auto getCaFile = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { + if (!IsCaCertsFileSet && (explicitOption || !Profile)) { + config.CaCertsFile = param; + IsCaCertsFileSet = true; + GetCaCerts(config); } - if (profile && profile->Has("ca-file")) { - CaCertsFile = profile->GetValue("ca-file").as<TString>(); + if (!IsVerbose()) { + return true; } + config.ConnectionParams["ca-file"].push_back({param, sourceText}); + return false; + }; + // Priority 1. Explicit --ca-file option + if (CaCertsFile && getCaFile(CaCertsFile, "explicit --ca-file option", true)) { + return; + } + // Priority 2. Explicit --profile option + if (TryGetParamFromProfile("ca-file", Profile, true, getCaFile)) { + return; + } + // Priority 3. Active profile (if --profile option is not specified) + if (TryGetParamFromProfile("ca-file", ProfileManager->GetActiveProfile(), false, getCaFile)) { + return; } - if (!config.EnableSsl && !CaCertsFile.empty()) { +} + +void TClientCommandRootCommon::GetCaCerts(TConfig& config) { + if (!config.EnableSsl && !config.CaCertsFile.empty()) { throw TMisuseException() << "\"ca-file\" option provided for a non-ssl connection. Use grpcs:// prefix for host to connect using SSL."; } - if (!CaCertsFile.empty()) { - config.CaCerts = ReadFromFile(CaCertsFile, "CA certificates"); + if (!config.CaCertsFile.empty()) { + config.CaCerts = ReadFromFile(config.CaCertsFile, "CA certificates"); } } void TClientCommandRootCommon::ParseAddress(TConfig& config) { - TString hostname; - TString port = "2135"; - - if (Address.empty()) { - auto profile = Profile; - if (!profile) { - profile = ProfileManager->GetActiveProfile(); - } - if (profile && profile->Has("endpoint")) { - Address = profile->GetValue("endpoint").as<TString>(); - } + auto getAddress = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { + TString address; + if (!IsAddressSet && (explicitOption || !Profile)) { + config.Address = param; + IsAddressSet = true; + GetAddressFromString(config); + address = config.Address; + } + if (!IsVerbose()) { + return true; + } + if (!address) { + address = param; + GetAddressFromString(config, &address); + } + config.ConnectionParams["endpoint"].push_back({address, sourceText}); + return false; + }; + // Priority 1. Explicit --endpoint option + if (Address && getAddress(Address, "explicit --endpoint option", true)) { + return; } + // Priority 2. Explicit --profile option + if (TryGetParamFromProfile("endpoint", Profile, true, getAddress)) { + return; + } + // Priority 3. Active profile (if --profile option is not specified) + if (TryGetParamFromProfile("endpoint", ProfileManager->GetActiveProfile(), false, getAddress)) { + return; + } +} +void TClientCommandRootCommon::GetAddressFromString(TConfig& config, TString* result) { + TString port = "2135"; + Address = result ? *result : config.Address; if (!Address.empty()) { - config.EnableSsl = Settings.EnableSsl.GetRef(); + if (!result) { + config.EnableSsl = Settings.EnableSsl.GetRef(); + } TString message; - if (!ParseProtocol(config, message)) { + if ((result && !ParseProtocolNoConfig(message)) || (!result && !ParseProtocol(config, message))) { MisuseErrors.push_back(message); } auto colon_pos = Address.find(":"); if (colon_pos == TString::npos) { - config.Address = Address + ":" + port; + if (result) { + *result = Address + ":" + port; + } else { + config.Address = Address + ":" + port; + } } else { if (colon_pos == Address.rfind(":")) { - config.Address = Address; + if (result) { + *result = Address; + } else { + config.Address = Address; + } } else { MisuseErrors.push_back("Wrong format for option 'endpoint': more than one colon found."); - return; } } } } +bool TClientCommandRootCommon::ParseProtocolNoConfig(TString& message) { + auto separator_pos = Address.find("://"); + if (separator_pos != TString::npos) { + TString protocol = Address.substr(0, separator_pos); + protocol.to_lower(); + if (protocol != "grpcs" && protocol != "grpc") { + message = TStringBuilder() << "Unknown protocol \"" << protocol << "\"."; + return false; + } + Address = Address.substr(separator_pos + 3); + } + return true; +} + void TClientCommandRootCommon::ParseProfile() { if (ProfileName) { if (ProfileManager->HasProfile(ProfileName)) { @@ -320,37 +407,64 @@ void TClientCommandRootCommon::ParseProfile() { } void TClientCommandRootCommon::ParseDatabase(TConfig& config) { - if (Database.empty()) { - auto profile = Profile; - if (!profile) { - profile = ProfileManager->GetActiveProfile(); + auto getDatabase = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { + if (!IsDatabaseSet && (explicitOption || !Profile)) { + config.Database = param; + IsDatabaseSet = true; } - if (profile && profile->Has("database")) { - Database = profile->GetValue("database").as<TString>(); + if (!IsVerbose()) { + return true; } + config.ConnectionParams["database"].push_back({param, sourceText}); + return false; + }; + // Priority 1. Explicit --database option + if (Database && getDatabase(Database, "explicit --database option", true)) { + return; + } + // Priority 2. Explicit --profile option + if (TryGetParamFromProfile("database", Profile, true, getDatabase)) { + return; + } + // Priority 3. Active profile (if --profile option is not specified) + if (TryGetParamFromProfile("database", ProfileManager->GetActiveProfile(), false, getDatabase)) { + return; } - - config.Database = Database; } void TClientCommandRootCommon::ParseIamEndpoint(TConfig& config) { - if (IamEndpoint.empty()) { - auto profile = Profile; - if (!profile) { - profile = ProfileManager->GetActiveProfile(); + auto getIamEndpoint = [this, &config] (const TString& param, const TString& sourceText, bool explicitOption) { + if (!IsIamEndpointSet && (explicitOption || !Profile)) { + config.IamEndpoint = param; + IsIamEndpointSet = true; } - if (profile && profile->Has("iam-endpoint")) { - IamEndpoint = profile->GetValue("iam-endpoint").as<TString>(); + if (!IsVerbose()) { + return true; } + config.ConnectionParams["iam-endpoint"].push_back({param, sourceText}); + return false; + }; + // Priority 1. Explicit --iam-endpoint option + if (IamEndpoint && getIamEndpoint(IamEndpoint, "explicit --iam-endpoint option", true)) { + return; } - if (!IamEndpoint.empty()) { - config.IamEndpoint = IamEndpoint; + // Priority 2. Explicit --profile option + if (TryGetParamFromProfile("iam-endpoint", Profile, true, getIamEndpoint)) { + return; + } + // Priority 3. Active profile (if --profile option is not specified) + if (TryGetParamFromProfile("iam-endpoint", ProfileManager->GetActiveProfile(), false, getIamEndpoint)) { + return; + } + // Priority 4. Default value + if (IsVerbose()) { + config.ConnectionParams["iam-endpoint"].push_back({config.IamEndpoint, "default value"}); } } void TClientCommandRootCommon::Validate(TConfig& config) { TClientCommandRootBase::Validate(config); - if (!config.NeedToConnect) { + if (!config.NeedToConnect && !IsVerbose()) { return; } @@ -362,20 +476,25 @@ void TClientCommandRootCommon::Validate(TConfig& config) { } errors << *it; } - - throw TMisuseException() << errors; + if (!config.NeedToConnect) { + Cerr << "Connection parameters parsing errors:" << Endl << errors << Endl; + } else { + throw TMisuseException() << errors; + } + } + if (!config.NeedToConnect) { + return; } - if (Address.empty()) { - throw TMisuseException() - << "Missing required option 'endpoint'."; + if (config.Address.empty()) { + throw TMisuseException() << "Missing required option 'endpoint'."; } - if (Database.empty()) { + if (config.Database.empty()) { throw TMisuseException() << "Missing required option 'database'."; - } else if (!Database.StartsWith('/')) { - throw TMisuseException() << "Path to a database \"" << Database + } else if (!config.Database.StartsWith('/')) { + throw TMisuseException() << "Path to a database \"" << config.Database << "\" is incorrect. It must be absolute and thus must begin with '/'."; } } @@ -398,13 +517,6 @@ int TClientCommandRootCommon::Run(TConfig& config) { return EXIT_SUCCESS; } -namespace { - inline void PrintSettingFromProfile(const TString& setting, std::shared_ptr<IProfile> profile, bool explicitOption) { - Cout << "Using " << setting << " due to configuration in" << (explicitOption ? "" : " active") << " profile \"" - << profile->GetName() << "\"" << (explicitOption ? " from explicit --profile option" : "") << Endl; - } -} - bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfile> profile, TConfig& config, bool explicitOption) { if (!profile || !profile->Has("authentication")) { return false; @@ -417,15 +529,29 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil TString authMethod = authValue["method"].as<TString>(); if (authMethod == "use-metadata-credentials") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("metadata service", profile, explicitOption); + } + config.UseMetadataCredentials = true; + config.ChosenAuthMethod = "use-metadata-credentials"; + IsAuthSet = true; + } if (IsVerbose()) { - PrintSettingFromProfile("metadata service", profile, explicitOption); + config.ConnectionParams["use-metadata-credentials"].push_back({"true", GetProfileSource(profile, explicitOption)}); } - config.UseMetadataCredentials = true; return true; } if (authMethod == "anonymous-auth") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("anonymous authentication", profile, explicitOption); + } + config.ChosenAuthMethod = "anonymous-auth"; + IsAuthSet = true; + } if (IsVerbose()) { - PrintSettingFromProfile("anonymous authentication", profile, explicitOption); + config.ConnectionParams["anonymous-auth"].push_back({"true", GetProfileSource(profile, explicitOption)}); } return true; } @@ -453,14 +579,18 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil auto authData = authValue["data"]; if (authMethod == "iam-token") { - if (IsVerbose()) { - PrintSettingFromProfile("iam token", profile, explicitOption); + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("iam token", profile, explicitOption); + } + config.SecurityToken = authData.as<TString>(); + config.ChosenAuthMethod = "token"; + IsAuthSet = true; } - config.SecurityToken = authData.as<TString>(); - } else if (authMethod == "token-file") { if (IsVerbose()) { - PrintSettingFromProfile("token file", profile, explicitOption); + config.ConnectionParams["token"].push_back({authData.as<TString>(), GetProfileSource(profile, explicitOption)}); } + } else if (authMethod == "token-file") { TString filename = authData.as<TString>(); TString fileContent; if (!ReadFromFileIfExists(filename, "token", fileContent, true)) { @@ -471,16 +601,30 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil MisuseErrors.push_back(TStringBuilder() << "Empty token file " << filename << " provided"); return false; } - config.SecurityToken = fileContent; - } else if (authMethod == "yc-token") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("token file", profile, explicitOption); + } + config.SecurityToken = fileContent; + config.ChosenAuthMethod = "token"; + IsAuthSet = true; + } if (IsVerbose()) { - PrintSettingFromProfile("Yandex.Cloud Passport token (yc-token)", profile, explicitOption); + config.ConnectionParams["token"].push_back({fileContent, GetProfileSource(profile, explicitOption)}); + } + } else if (authMethod == "yc-token") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("Yandex.Cloud Passport token (yc-token)", profile, explicitOption); + } + config.YCToken = authData.as<TString>(); + config.ChosenAuthMethod = "yc-token"; + IsAuthSet = true; } - config.YCToken = authData.as<TString>(); - } else if (authMethod == "yc-token-file") { if (IsVerbose()) { - PrintSettingFromProfile("Yandex.Cloud Passport token file (yc-token-file)", profile, explicitOption); + config.ConnectionParams["yc-token"].push_back({authData.as<TString>(), GetProfileSource(profile, explicitOption)}); } + } else if (authMethod == "yc-token-file") { TString filename = authData.as<TString>(); TString fileContent; if (!ReadFromFileIfExists(filename, "token", fileContent, true)) { @@ -491,32 +635,66 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil MisuseErrors.push_back(TStringBuilder() << "Empty token file " << filename << " provided"); return false; } - config.YCToken = fileContent; - } else if (authMethod == "sa-key-file") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("Yandex.Cloud Passport token file (yc-token-file)", profile, explicitOption); + } + config.YCToken = fileContent; + config.ChosenAuthMethod = "yc-token"; + IsAuthSet = true; + } if (IsVerbose()) { - PrintSettingFromProfile("service account key file (sa-key-file)", profile, explicitOption); + config.ConnectionParams["yc-token"].push_back({fileContent, GetProfileSource(profile, explicitOption)}); } + } else if (authMethod == "sa-key-file") { TString filePath = authData.as<TString>(); if (filePath.StartsWith("~")) { filePath = HomeDir + filePath.substr(1); } - config.SaKeyFile = filePath; + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("service account key file (sa-key-file)", profile, explicitOption); + } + config.SaKeyFile = filePath; + config.ChosenAuthMethod = "sa-key-file"; + IsAuthSet = true; + } + if (IsVerbose()) { + config.ConnectionParams["sa-key-file"].push_back({filePath, GetProfileSource(profile, explicitOption)}); + } } else if (authMethod == "ydb-token") { + if (!IsAuthSet && (explicitOption || !Profile)) { + if (IsVerbose()) { + PrintSettingFromProfile("OAuth token (ydb-token)", profile, explicitOption); + } + config.SecurityToken = authData.as<TString>(); + config.ChosenAuthMethod = "token"; + IsAuthSet = true; + } if (IsVerbose()) { - PrintSettingFromProfile("OAuth token (ydb-token)", profile, explicitOption); + config.ConnectionParams["token"].push_back({authData.as<TString>(), GetProfileSource(profile, explicitOption)}); } - config.SecurityToken = authData.as<TString>(); } else if (authMethod == "static-credentials") { - if (IsVerbose()) { + if (!IsAuthSet && (explicitOption || !Profile) && IsVerbose()) { PrintSettingFromProfile("user name & password", profile, explicitOption); } if (authData["user"]) { - config.StaticCredentials.User = authData["user"].as<TString>(); + if (!IsAuthSet && (explicitOption || !Profile)) { + config.StaticCredentials.User = authData["user"].as<TString>(); + } + if (IsVerbose()) { + config.ConnectionParams["user"].push_back({authData["user"].as<TString>(), GetProfileSource(profile, explicitOption)}); + } } if (authData["password"]) { - config.StaticCredentials.Password = authData["password"].as<TString>(); - if (!config.StaticCredentials.Password) { - DoNotAskForPassword = true; + if (!IsAuthSet && (explicitOption || !Profile)) { + config.StaticCredentials.Password = authData["password"].as<TString>(); + if (!config.StaticCredentials.Password) { + DoNotAskForPassword = true; + } + } + if (IsVerbose()) { + config.ConnectionParams["password"].push_back({authData["password"].as<TString>(), GetProfileSource(profile, explicitOption)}); } } if (authData["password-file"]) { @@ -527,13 +705,18 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil DoNotAskForPassword = true; return false; } - config.StaticCredentials.Password = fileContent; - if (!config.StaticCredentials.Password) { - DoNotAskForPassword = true; + if (!IsAuthSet && (explicitOption || !Profile)) { + config.StaticCredentials.Password = fileContent; + if (!config.StaticCredentials.Password) { + DoNotAskForPassword = true; + } + } + if (IsVerbose()) { + config.ConnectionParams["password"].push_back({fileContent, GetProfileSource(profile, explicitOption)}); } - } - + config.ChosenAuthMethod = "static-credentials"; + IsAuthSet = true; } else { return false; } @@ -541,15 +724,77 @@ bool TClientCommandRootCommon::GetCredentialsFromProfile(std::shared_ptr<IProfil } void TClientCommandRootCommon::ParseCredentials(TConfig& config) { - size_t explicitAuthMethodCount = (size_t)(!TokenFile.empty()) + (size_t)(!YCTokenFile.empty()) + size_t explicitAuthMethodCount = (size_t)(config.ParseResult->Has("iam-token-file")) + (size_t)(config.ParseResult->Has("token-file")) + + (size_t)(!YCTokenFile.empty()) + (size_t)UseMetadataCredentials + (size_t)(!SaKeyFile.empty()) + (size_t)(!UserName.empty() || !PasswordFile.empty() || DoNotAskForPassword); switch (explicitAuthMethodCount) { + case 1: + // Priority 1. Exactly one explicit auth method. Using it. + if (config.ParseResult->Has("token-file")) { + config.SecurityToken = ReadFromFile(TokenFile, "token"); + config.ChosenAuthMethod = "token"; + if (IsVerbose()) { + Cout << "Using token from file provided with explicit option" << Endl; + config.ConnectionParams["token"].push_back({config.SecurityToken, "file provided with explicit --token-file option"}); + } + } else if (config.ParseResult->Has("iam-token-file")) { + config.SecurityToken = ReadFromFile(TokenFile, "token"); + config.ChosenAuthMethod = "token"; + if (IsVerbose()) { + Cout << "Using IAM token from file provided with explicit option" << Endl; + config.ConnectionParams["token"].push_back({config.SecurityToken, "file provided with explicit --iam-token-file option"}); + } + } else if (YCTokenFile) { + config.YCToken = ReadFromFile(YCTokenFile, "token"); + config.ChosenAuthMethod = "yc-token"; + if (IsVerbose()) { + Cout << "Using Yandex.Cloud Passport token from file provided with --yc-token-file option" << Endl; + config.ConnectionParams["yc-token"].push_back({config.YCToken, "file provided with explicit --yc-token-file option"}); + } + } else if (UseMetadataCredentials) { + config.ChosenAuthMethod = "use-metadata-credentials"; + config.UseMetadataCredentials = true; + if (IsVerbose()) { + Cout << "Using metadata service due to --use-metadata-credentials option" << Endl; + config.ConnectionParams["use-metadata-credentials"].push_back({"true", "explicit --use-metadata-credentials option"}); + } + } else if (SaKeyFile) { + config.SaKeyFile = SaKeyFile; + config.ChosenAuthMethod = "sa-key-file"; + if (IsVerbose()) { + Cout << "Using service account key file provided with --sa-key-file option" << Endl; + config.ConnectionParams["sa-key-file"].push_back({config.SaKeyFile, "explicit --sa-key-file option"}); + } + } else if (UserName || PasswordFile) { + if (UserName) { + config.StaticCredentials.User = UserName; + if (IsVerbose()) { + Cout << "Using user name provided with --user option" << Endl; + config.ConnectionParams["user"].push_back({UserName, "explicit --user option"}); + } + } + if (PasswordFile) { + config.StaticCredentials.Password = ReadFromFile(PasswordFile, "password", true); + if (!config.StaticCredentials.Password) { + DoNotAskForPassword = true; + } + if (IsVerbose()) { + Cout << "Using user password from file provided with --password-file option" << Endl; + config.ConnectionParams["password"].push_back({config.StaticCredentials.Password, "file provided with explicit --password-file option"}); + } + } + config.ChosenAuthMethod = "static-credentials"; + } + IsAuthSet = true; + if (!IsVerbose()) { + break; + } case 0: { // Priority 2. No explicit auth methods. Checking configuration profile given via --profile option. - if (GetCredentialsFromProfile(Profile, config, true)) { + if (GetCredentialsFromProfile(Profile, config, true) && !IsVerbose()) { break; } @@ -557,128 +802,148 @@ void TClientCommandRootCommon::ParseCredentials(TConfig& config) { if (config.UseIamAuth) { TString envIamToken = GetEnv("IAM_TOKEN"); if (!envIamToken.empty()) { - if (IsVerbose()) { - Cout << "Using iam token from IAM_TOKEN env variable" << Endl; + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using iam token from IAM_TOKEN env variable" << Endl; + } + config.ChosenAuthMethod = "token"; + config.SecurityToken = envIamToken; + IsAuthSet = true; } - config.SecurityToken = envIamToken; - break; + if (!IsVerbose()) { + break; + } + config.ConnectionParams["token"].push_back({envIamToken, "IAM_TOKEN enviroment variable"}); } TString envYcToken = GetEnv("YC_TOKEN"); if (!envYcToken.empty()) { - if (IsVerbose()) { - Cout << "Using Yandex.Cloud Passport token from YC_TOKEN env variable" << Endl; + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using Yandex.Cloud Passport token from YC_TOKEN env variable" << Endl; + } + config.ChosenAuthMethod = "yc-token"; + config.YCToken = envYcToken; + IsAuthSet = true; } - config.YCToken = envYcToken; - break; + if (!IsVerbose()) { + break; + } + config.ConnectionParams["yc-token"].push_back({envYcToken, "YC_TOKEN enviroment variable"}); } if (GetEnv("USE_METADATA_CREDENTIALS") == "1") { - if (IsVerbose()) { - Cout << "Using metadata service due to USE_METADATA_CREDENTIALS=\"1\" env variable" << Endl; + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using metadata service due to USE_METADATA_CREDENTIALS=\"1\" env variable" << Endl; + } + config.ChosenAuthMethod = "use-metadata-credentials"; + config.UseMetadataCredentials = true; + IsAuthSet = true; } - config.UseMetadataCredentials = true; - break; + if (!IsVerbose()) { + break; + } + config.ConnectionParams["use-metadata-credentials"].push_back({"true", "USE_METADATA_CREDENTIALS enviroment variable"}); } TString envSaKeyFile = GetEnv("SA_KEY_FILE"); if (!envSaKeyFile.empty()) { - if (IsVerbose()) { - Cout << "Using service account key file from SA_KEY_FILE env variable" << Endl; + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using service account key file from SA_KEY_FILE env variable" << Endl; + } + config.ChosenAuthMethod = "sa-key-file"; + config.SaKeyFile = envSaKeyFile; + IsAuthSet = true; } - config.SaKeyFile = envSaKeyFile; - break; + if (!IsVerbose()) { + break; + } + config.ConnectionParams["sa-key-file"].push_back({envSaKeyFile, "SA_KEY_FILE enviroment variable"}); } } if (config.UseOAuthToken) { TString envYdbToken = GetEnv("YDB_TOKEN"); if (!envYdbToken.empty()) { - if (IsVerbose()) { - Cout << "Using OAuth token from YDB_TOKEN env variable" << Endl; + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using OAuth token from YDB_TOKEN env variable" << Endl; + } + config.ChosenAuthMethod = "token"; + config.SecurityToken = envYdbToken; + IsAuthSet = true; } - config.SecurityToken = envYdbToken; - break; + if (!IsVerbose()) { + break; + } + config.ConnectionParams["token"].push_back({envYdbToken, "YDB_TOKEN enviroment variable"}); } } if (config.UseStaticCredentials) { TString userName = GetEnv("YDB_USER"); + bool hasStaticCredentials = false; if (!userName.empty()) { + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using user name from YDB_USER env variable" << Endl; + } + hasStaticCredentials = true; + config.StaticCredentials.User = userName; + } if (IsVerbose()) { - Cout << "Using user name from YDB_USER env variable" << Endl; + config.ConnectionParams["user"].push_back({userName, "YDB_USER enviroment variable"}); } - config.StaticCredentials.User = userName; } TString password = GetEnv("YDB_PASSWORD"); if (!password.empty()) { + if (!IsAuthSet) { + if (IsVerbose()) { + Cout << "Using user password from YDB_PASSWORD env variable" << Endl; + } + hasStaticCredentials = true; + config.StaticCredentials.Password = password; + } if (IsVerbose()) { - Cout << "Using user password from YDB_PASSWORD env variable" << Endl; + config.ConnectionParams["password"].push_back({password, "YDB_PASSWORD enviroment variable"}); } - config.StaticCredentials.Password = password; } - if (!userName.empty() || !password.empty()) { + if (hasStaticCredentials) { + config.ChosenAuthMethod = "static-credentials"; + IsAuthSet = true; + } + if (!IsVerbose() && (!userName.empty() || !password.empty())) { break; } } // Priority 4. No auth methods from environment variables too. Checking active configuration profile. // (if --profile option is not set) - if (!Profile && GetCredentialsFromProfile(ProfileManager->GetActiveProfile(), config, false)) { + if (GetCredentialsFromProfile(ProfileManager->GetActiveProfile(), config, false) && !IsVerbose()) { break; } if (Settings.UseDefaultTokenFile.GetRef()) { // Priority 5. No auth methods from active configuration profile. Checking default token file. TString tokenFile = defaultTokenFile; - if (ReadFromFileIfExists(tokenFile, "default token", config.SecurityToken)) { + TString fileContent; + if (ReadFromFileIfExists(tokenFile, "default token", fileContent)) { + if (!IsAuthSet) { + if (!IsVerbose()) { + Cout << "Using auth token from default token file " << defaultTokenFile << Endl; + } + config.ChosenAuthMethod = "token"; + config.SecurityToken = fileContent; + } if (IsVerbose()) { - Cout << "Using auth token from default token file " << defaultTokenFile << Endl; + config.ConnectionParams["token"].push_back({fileContent, "default token file"}); } } else { - if (IsVerbose()) { + if (!IsAuthSet && IsVerbose()) { Cout << "No authentication methods were found. Going without authentication" << Endl; } } } break; } - case 1: - // Priority 1. Exactly one explicit auth method. Using it. - if (TokenFile) { - if (IsVerbose()) { - Cout << "Using token from file provided with explicit option" << Endl; - } - config.SecurityToken = ReadFromFile(TokenFile, "token"); - } else if (YCTokenFile) { - if (IsVerbose()) { - Cout << "Using Yandex.Cloud Passport token from file provided with --yc-token-file option" << Endl; - } - config.YCToken = ReadFromFile(YCTokenFile, "token"); - } else if (UseMetadataCredentials) { - if (IsVerbose()) { - Cout << "Using metadata service due to --use-metadata-credentials option" << Endl; - } - config.UseMetadataCredentials = true; - } else if (SaKeyFile) { - if (IsVerbose()) { - Cout << "Using service account key file provided with --sa-key-file option" << Endl; - } - config.SaKeyFile = SaKeyFile; - } else if (UserName || PasswordFile) { - if (UserName) { - if (IsVerbose()) { - Cout << "Using user name provided with --user option" << Endl; - } - config.StaticCredentials.User = UserName; - } - if (PasswordFile) { - if (IsVerbose()) { - Cout << "Using user password from file provided with --password-file option" << Endl; - } - config.StaticCredentials.Password = ReadFromFile(PasswordFile, "password", true); - if (!config.StaticCredentials.Password) { - DoNotAskForPassword = true; - } - } - } - break; default: TStringBuilder str; str << explicitAuthMethodCount << " methods were provided via options:"; @@ -704,6 +969,9 @@ void TClientCommandRootCommon::ParseCredentials(TConfig& config) { if (!config.StaticCredentials.Password && !DoNotAskForPassword) { Cerr << "Enter password for user " << config.StaticCredentials.User << ": "; config.StaticCredentials.Password = InputPassword(); + if (IsVerbose()) { + config.ConnectionParams["password"].push_back({config.StaticCredentials.Password, "standard input"}); + } } } else { if (config.StaticCredentials.Password) { diff --git a/ydb/public/lib/ydb_cli/commands/ydb_root_common.h b/ydb/public/lib/ydb_cli/commands/ydb_root_common.h index 4946e1ad00..ed08c53525 100644 --- a/ydb/public/lib/ydb_cli/commands/ydb_root_common.h +++ b/ydb/public/lib/ydb_cli/commands/ydb_root_common.h @@ -46,6 +46,11 @@ private: void ParseDatabase(TConfig& config); void ParseIamEndpoint(TConfig& config); void ParseCaCerts(TConfig& config) override; + void GetAddressFromString(TConfig& config, TString* result = nullptr); + bool ParseProtocolNoConfig(TString& message); + void GetCaCerts(TConfig& config); + bool TryGetParamFromProfile(const TString& name, std::shared_ptr<IProfile> profile, bool explicitOption, + std::function<bool(const TString&, const TString&, bool)> callback); TString Database; @@ -70,6 +75,12 @@ private: TString IamEndpoint; const TClientSettings& Settings; TVector<TString> MisuseErrors; + + bool IsAddressSet = false; + bool IsDatabaseSet = false; + bool IsIamEndpointSet = false; + bool IsCaCertsFileSet = false; + bool IsAuthSet = false; }; } diff --git a/ydb/public/lib/ydb_cli/common/command.h b/ydb/public/lib/ydb_cli/common/command.h index a9bd59e1b8..ed06e376c5 100644 --- a/ydb/public/lib/ydb_cli/common/command.h +++ b/ydb/public/lib/ydb_cli/common/command.h @@ -36,6 +36,11 @@ public: NLastGetopt::TOpts* Options; }; + struct TConnectionParam { + TString Value; + TString Source; + }; + public: using TCredentialsGetter = std::function<std::shared_ptr<ICredentialsProviderFactory>(const TClientCommand::TConfig&)>; @@ -89,6 +94,8 @@ public: TString Address; TString Database; TString CaCerts; + TString CaCertsFile; + TMap<TString, TVector<TConnectionParam>> ConnectionParams; bool EnableSsl = false; bool IsNetworkIntensive = false; @@ -109,6 +116,7 @@ public: TString SaKeyFile; TString IamEndpoint; TString YScope; + TString ChosenAuthMethod; TString ProfileFile; bool UseOAuthToken = true; |