make progress, seems functional

This commit is contained in:
Arthur Passos 2024-06-20 15:07:16 -03:00
parent 1514dcbb34
commit 179d54505a
13 changed files with 119 additions and 60 deletions

View File

@ -525,7 +525,6 @@ std::optional<AuthResult> IAccessStorage::authenticateImpl(
if (!isAddressAllowed(*user, address))
throwAddressNotAllowed(address);
// todo arthur
// for now, just throw exception in case a user exists with invalid auth method
// back in the day, it would also throw an exception. There might be a smarter alternative
// like a user scan during startup.

View File

@ -265,13 +265,16 @@ namespace
user->authentication_methods.emplace_back();
}
auto auth_type = user->authentication_methods.back().getType();
if (((auth_type == AuthenticationType::NO_PASSWORD) && !allow_no_password) ||
((auth_type == AuthenticationType::PLAINTEXT_PASSWORD) && !allow_plaintext_password))
for (const auto & authentication_method : user->authentication_methods)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Authentication type {} is not allowed, check the setting allow_{} in the server configuration",
toString(auth_type), AuthenticationTypeInfo::get(auth_type).name);
auto auth_type = authentication_method.getType();
if (((auth_type == AuthenticationType::NO_PASSWORD) && !allow_no_password) ||
((auth_type == AuthenticationType::PLAINTEXT_PASSWORD) && !allow_plaintext_password))
{
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Authentication type {} is not allowed, check the setting allow_{} in the server configuration",
toString(auth_type), AuthenticationTypeInfo::get(auth_type).name);
}
}
const auto profile_name_config = user_config + ".profile";

View File

@ -890,16 +890,19 @@ public:
Messaging::MessageTransport & mt,
const Poco::Net::SocketAddress & address)
{
AuthenticationType user_auth_type;
try
{
user_auth_type = session.getAuthenticationTypeOrLogInFailure(user_name);
if (type_to_method.find(user_auth_type) != type_to_method.end())
const auto user_authentication_types = session.getAuthenticationTypesOrLogInFailure(user_name);
for (auto user_authentication_type : user_authentication_types)
{
type_to_method[user_auth_type]->authenticate(user_name, session, mt, address);
mt.send(Messaging::AuthenticationOk(), true);
LOG_DEBUG(log, "Authentication for user {} was successful.", user_name);
return;
if (type_to_method.find(user_authentication_type) != type_to_method.end())
{
type_to_method[user_authentication_type]->authenticate(user_name, session, mt, address);
mt.send(Messaging::AuthenticationOk(), true);
LOG_DEBUG(log, "Authentication for user {} was successful.", user_name);
return;
}
}
}
catch (const Exception&)
@ -913,7 +916,7 @@ public:
mt.send(Messaging::ErrorOrNoticeResponse(Messaging::ErrorOrNoticeResponse::ERROR, "0A000", "Authentication method is not supported"),
true);
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "Authentication method is not supported: {}", user_auth_type);
throw Exception(ErrorCodes::NOT_IMPLEMENTED, "None of the authentication methods registered for the user are supported");
}
};
}

View File

@ -40,6 +40,7 @@ namespace
const std::optional<RolesOrUsersSet> & override_grantees,
const std::optional<time_t> & valid_until,
bool reset_authentication_methods,
bool replace_authentication_methods,
bool allow_implicit_no_password,
bool allow_no_password,
bool allow_plaintext_password)
@ -51,45 +52,48 @@ namespace
else if (query.names->size() == 1)
user.setName(query.names->front()->toString());
// todo arthur check if auth_data.empty makes sense
if (!query.attach && !query.alter && !auth_data.empty() && !allow_implicit_no_password)
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Authentication type NO_PASSWORD must "
"be explicitly specified, check the setting allow_implicit_no_password "
"in the server configuration");
if (!auth_data.empty())
{
for (const auto & authentication_method : auth_data)
{
user.authentication_methods.push_back(authentication_method);
}
}
else if (user.authentication_methods.empty())
if (user.authentication_methods.empty() && auth_data.empty())
{
// previously, a user always had a default constructed auth method.. maybe I should put this somewhere else
user.authentication_methods.emplace_back();
}
if (reset_authentication_methods)
if (replace_authentication_methods)
{
// todo check if element exists
auto primary_authentication_method = user.authentication_methods.back();
user.authentication_methods.clear();
user.authentication_methods.push_back(primary_authentication_method);
}
if (!auth_data.empty() || !query.alter)
for (const auto & authentication_method : auth_data)
{
// I suppose it is guaranteed a user will always have at least one authentication method
auto auth_type = user.authentication_methods.back().getType();
if (((auth_type == AuthenticationType::NO_PASSWORD) && !allow_no_password) ||
((auth_type == AuthenticationType::PLAINTEXT_PASSWORD) && !allow_plaintext_password))
user.authentication_methods.emplace_back(authentication_method);
}
if (reset_authentication_methods)
{
auto backup_authentication_method = user.authentication_methods.back();
user.authentication_methods.clear();
user.authentication_methods.emplace_back(backup_authentication_method);
}
if (!query.alter)
{
for (const auto & authentication_method : user.authentication_methods)
{
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Authentication type {} is not allowed, check the setting allow_{} in the server configuration",
toString(auth_type),
AuthenticationTypeInfo::get(auth_type).name);
auto auth_type = authentication_method.getType();
if (((auth_type == AuthenticationType::NO_PASSWORD) && !allow_no_password) ||
((auth_type == AuthenticationType::PLAINTEXT_PASSWORD) && !allow_plaintext_password))
{
throw Exception(ErrorCodes::BAD_ARGUMENTS,
"Authentication type {} is not allowed, check the setting allow_{} in the server configuration",
toString(auth_type),
AuthenticationTypeInfo::get(auth_type).name);
}
}
}
@ -222,7 +226,8 @@ BlockIO InterpreterCreateUserQuery::execute()
auto updated_user = typeid_cast<std::shared_ptr<User>>(entity->clone());
updateUserFromQueryImpl(
*updated_user, query, auth_data, {}, default_roles_from_query, settings_from_query, grantees_from_query,
valid_until, query.reset_authentication_methods_to_new, implicit_no_password_allowed, no_password_allowed, plaintext_password_allowed);
valid_until, query.reset_authentication_methods_to_new, query.replace_authentication_methods,
implicit_no_password_allowed, no_password_allowed, plaintext_password_allowed);
return updated_user;
};
@ -242,7 +247,8 @@ BlockIO InterpreterCreateUserQuery::execute()
auto new_user = std::make_shared<User>();
updateUserFromQueryImpl(
*new_user, query, auth_data, name, default_roles_from_query, settings_from_query, RolesOrUsersSet::AllTag{},
valid_until, query.reset_authentication_methods_to_new, implicit_no_password_allowed, no_password_allowed, plaintext_password_allowed);
valid_until, query.reset_authentication_methods_to_new, query.replace_authentication_methods,
implicit_no_password_allowed, no_password_allowed, plaintext_password_allowed);
new_users.emplace_back(std::move(new_user));
}
@ -290,7 +296,19 @@ void InterpreterCreateUserQuery::updateUserFromQuery(User & user, const ASTCreat
}
}
updateUserFromQueryImpl(user, query, auth_data, {}, {}, {}, {}, {}, query.reset_authentication_methods_to_new, allow_no_password, allow_plaintext_password, true);
updateUserFromQueryImpl(user,
query,
auth_data,
{},
{},
{},
{},
{},
query.reset_authentication_methods_to_new,
query.replace_authentication_methods,
allow_no_password,
allow_plaintext_password,
true);
}
void registerInterpreterCreateUserQuery(InterpreterFactory & factory)

View File

@ -307,16 +307,25 @@ Session::~Session()
}
}
AuthenticationType Session::getAuthenticationType(const String & user_name) const
std::unordered_set<AuthenticationType> Session::getAuthenticationTypes(const String & user_name) const
{
return global_context->getAccessControl().read<User>(user_name)->authentication_methods.back().getType();
std::unordered_set<AuthenticationType> authentication_types;
const auto user_to_query = global_context->getAccessControl().read<User>(user_name);
for (const auto & authentication_method : user_to_query->authentication_methods)
{
authentication_types.insert(authentication_method.getType());
}
return authentication_types;
}
AuthenticationType Session::getAuthenticationTypeOrLogInFailure(const String & user_name) const
std::unordered_set<AuthenticationType> Session::getAuthenticationTypesOrLogInFailure(const String & user_name) const
{
try
{
return getAuthenticationType(user_name);
return getAuthenticationTypes(user_name);
}
catch (const Exception & e)
{

View File

@ -43,10 +43,10 @@ public:
Session & operator=(const Session &) = delete;
/// Provides information about the authentication type of a specified user.
AuthenticationType getAuthenticationType(const String & user_name) const;
std::unordered_set<AuthenticationType> getAuthenticationTypes(const String & user_name) const;
/// Same as getAuthenticationType, but adds LoginFailure event in case of error.
AuthenticationType getAuthenticationTypeOrLogInFailure(const String & user_name) const;
std::unordered_set<AuthenticationType> getAuthenticationTypesOrLogInFailure(const String & user_name) const;
/// Sets the current user, checks the credentials and that the specified address is allowed to connect from.
/// The function throws an exception if there is no such user or password is wrong.

View File

@ -154,7 +154,9 @@ void ASTAuthenticationData::formatImpl(const FormatSettings & settings, FormatSt
auth_type_name = AuthenticationTypeInfo::get(*type).name;
}
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << " IDENTIFIED" << (settings.hilite ? IAST::hilite_none : "");
const char * identified_string = settings.additional_authentication_method ? " ADD NEW AUTHENTICATION METHOD IDENTIFIED" : " IDENTIFIED";
settings.ostr << (settings.hilite ? IAST::hilite_keyword : "") << identified_string << (settings.hilite ? IAST::hilite_none : "");
if (!auth_type_name.empty())
{

View File

@ -20,9 +20,14 @@ namespace
void formatAuthenticationData(const std::vector<std::shared_ptr<ASTAuthenticationData>> & auth_data, const IAST::FormatSettings & settings)
{
for (const auto & authentication_method : auth_data)
auth_data[0]->format(settings);
auto settings_with_additional_authentication_method = settings;
settings_with_additional_authentication_method.additional_authentication_method = true;
for (auto i = 1u; i < auth_data.size(); i++)
{
authentication_method->format(settings);
auth_data[i]->format(settings_with_additional_authentication_method);
}
}
@ -187,8 +192,9 @@ ASTPtr ASTCreateUserQuery::clone() const
{
for (const auto & authentication_method : auth_data)
{
res->auth_data.push_back(std::static_pointer_cast<ASTAuthenticationData>(authentication_method->clone()));
res->children.push_back(res->auth_data.back());
auto ast_clone = std::static_pointer_cast<ASTAuthenticationData>(authentication_method->clone());
res->auth_data.push_back(ast_clone);
res->children.push_back(ast_clone);
}
}

View File

@ -43,6 +43,7 @@ public:
bool if_not_exists = false;
bool or_replace = false;
bool reset_authentication_methods_to_new = false;
bool replace_authentication_methods = false;
std::shared_ptr<ASTUserNamesWithHost> names;
std::optional<String> new_name;

View File

@ -517,11 +517,9 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
continue;
}
// todo arthur maybe check that neither identified with or add new auth method has been parsed
if (!reset_authentication_methods_to_new.has_value())
{
reset_authentication_methods_to_new = parseResetAuthenticationMethods(pos, expected);
continue;
}
if (!valid_until)
@ -621,7 +619,8 @@ bool ParserCreateUserQuery::parseImpl(Pos & pos, ASTPtr & node, Expected & expec
query->default_database = std::move(default_database);
query->valid_until = std::move(valid_until);
query->storage_name = std::move(storage_name);
query->reset_authentication_methods_to_new = parsed_identified_with || reset_authentication_methods_to_new.value_or(false);
query->reset_authentication_methods_to_new = reset_authentication_methods_to_new.value_or(false);
query->replace_authentication_methods = parsed_identified_with;
for (const auto & authentication_method : query->auth_data)
{

View File

@ -201,6 +201,7 @@ public:
bool show_secrets; /// Show secret parts of the AST (e.g. passwords, encryption keys).
char nl_or_ws; /// Newline or whitespace.
LiteralEscapingStyle literal_escaping_style;
bool additional_authentication_method;
explicit FormatSettings(
WriteBuffer & ostr_,
@ -209,7 +210,8 @@ public:
bool always_quote_identifiers_ = false,
IdentifierQuotingStyle identifier_quoting_style_ = IdentifierQuotingStyle::Backticks,
bool show_secrets_ = true,
LiteralEscapingStyle literal_escaping_style_ = LiteralEscapingStyle::Regular)
LiteralEscapingStyle literal_escaping_style_ = LiteralEscapingStyle::Regular,
bool additional_authentication_method_ = false)
: ostr(ostr_)
, one_line(one_line_)
, hilite(hilite_)
@ -218,6 +220,7 @@ public:
, show_secrets(show_secrets_)
, nl_or_ws(one_line ? ' ' : '\n')
, literal_escaping_style(literal_escaping_style_)
, additional_authentication_method(additional_authentication_method_)
{
}
@ -230,6 +233,7 @@ public:
, show_secrets(other.show_secrets)
, nl_or_ws(other.nl_or_ws)
, literal_escaping_style(other.literal_escaping_style)
, additional_authentication_method(other.additional_authentication_method)
{
}

View File

@ -373,11 +373,16 @@ void MySQLHandler::authenticate(const String & user_name, const String & auth_pl
{
try
{
// For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible
// (if password is specified using double SHA1). Otherwise, SHA256 plugin is used.
if (session->getAuthenticationTypeOrLogInFailure(user_name) == DB::AuthenticationType::SHA256_PASSWORD)
const auto user_authentication_types = session->getAuthenticationTypesOrLogInFailure(user_name);
for (const auto user_authentication_type : user_authentication_types)
{
authPluginSSL();
// For compatibility with JavaScript MySQL client, Native41 authentication plugin is used when possible
// (if password is specified using double SHA1). Otherwise, SHA256 plugin is used.
if (user_authentication_type == DB::AuthenticationType::SHA256_PASSWORD)
{
authPluginSSL();
}
}
std::optional<String> auth_response = auth_plugin_name == auth_plugin->getName() ? std::make_optional<String>(initial_auth_response) : std::nullopt;

View File

@ -1498,7 +1498,17 @@ void TCPHandler::receiveHello()
/// Perform handshake for SSH authentication
if (is_ssh_based_auth)
{
if (session->getAuthenticationTypeOrLogInFailure(user) != AuthenticationType::SSH_KEY)
const auto authentication_types = session->getAuthenticationTypesOrLogInFailure(user);
bool user_supports_ssh_authentication = std::find_if(
authentication_types.begin(),
authentication_types.end(),
[](auto authentication_type)
{
return authentication_type == AuthenticationType::SSH_KEY;
}) != authentication_types.end();
if (!user_supports_ssh_authentication)
throw Exception(ErrorCodes::AUTHENTICATION_FAILED, "Expected authentication with SSH key");
if (client_tcp_protocol_version < DBMS_MIN_REVISION_WITH_SSH_AUTHENTICATION)