mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-09-19 16:20:50 +00:00
make progress, seems functional
This commit is contained in:
parent
1514dcbb34
commit
179d54505a
@ -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.
|
||||
|
@ -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";
|
||||
|
@ -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");
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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.
|
||||
|
@ -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())
|
||||
{
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user