mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-30 11:32:03 +00:00
Refactor exception handling in login() et al.
Simplify LDAPClient and LDAPAccessStorage
This commit is contained in:
parent
00a354cd37
commit
7f47719768
@ -14,6 +14,8 @@ namespace ErrorCodes
|
||||
extern const int ACCESS_ENTITY_ALREADY_EXISTS;
|
||||
extern const int ACCESS_ENTITY_NOT_FOUND;
|
||||
extern const int ACCESS_STORAGE_READONLY;
|
||||
extern const int WRONG_PASSWORD;
|
||||
extern const int IP_ADDRESS_NOT_ALLOWED;
|
||||
extern const int AUTHENTICATION_FAILED;
|
||||
extern const int LOGICAL_ERROR;
|
||||
}
|
||||
@ -420,7 +422,14 @@ UUID IAccessStorage::login(
|
||||
const Poco::Net::IPAddress & address,
|
||||
const ExternalAuthenticators & external_authenticators) const
|
||||
{
|
||||
return loginImpl(user_name, password, address, external_authenticators);
|
||||
try {
|
||||
return loginImpl(user_name, password, address, external_authenticators);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
tryLogCurrentException(getLogger(), user_name + ": Authentication failed");
|
||||
throwCannotAuthenticate(user_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -434,11 +443,16 @@ UUID IAccessStorage::loginImpl(
|
||||
{
|
||||
if (auto user = tryRead<User>(*id))
|
||||
{
|
||||
if (isPasswordCorrectImpl(*user, password, external_authenticators) && isAddressAllowedImpl(*user, address))
|
||||
return *id;
|
||||
if (!isPasswordCorrectImpl(*user, password, external_authenticators))
|
||||
throwInvalidPassword();
|
||||
|
||||
if (!isAddressAllowedImpl(*user, address))
|
||||
throwAddressNotAllowed(address);
|
||||
|
||||
return *id;
|
||||
}
|
||||
}
|
||||
throwCannotAuthenticate(user_name);
|
||||
throwNotFound(EntityType::USER, user_name);
|
||||
}
|
||||
|
||||
|
||||
@ -554,6 +568,15 @@ void IAccessStorage::throwReadonlyCannotRemove(EntityType type, const String & n
|
||||
ErrorCodes::ACCESS_STORAGE_READONLY);
|
||||
}
|
||||
|
||||
void IAccessStorage::throwAddressNotAllowed(const Poco::Net::IPAddress & address)
|
||||
{
|
||||
throw Exception("Connections from " + address.toString() + " are not allowed", ErrorCodes::IP_ADDRESS_NOT_ALLOWED);
|
||||
}
|
||||
|
||||
void IAccessStorage::throwInvalidPassword()
|
||||
{
|
||||
throw Exception("Invalid password", ErrorCodes::WRONG_PASSWORD);
|
||||
}
|
||||
|
||||
void IAccessStorage::throwCannotAuthenticate(const String & user_name)
|
||||
{
|
||||
|
@ -182,6 +182,8 @@ protected:
|
||||
[[noreturn]] void throwReadonlyCannotInsert(EntityType type, const String & name) const;
|
||||
[[noreturn]] void throwReadonlyCannotUpdate(EntityType type, const String & name) const;
|
||||
[[noreturn]] void throwReadonlyCannotRemove(EntityType type, const String & name) const;
|
||||
[[noreturn]] static void throwAddressNotAllowed(const Poco::Net::IPAddress & address);
|
||||
[[noreturn]] static void throwInvalidPassword();
|
||||
[[noreturn]] static void throwCannotAuthenticate(const String & user_name);
|
||||
|
||||
using Notification = std::tuple<OnChangedHandler, UUID, AccessEntityPtr>;
|
||||
|
@ -230,42 +230,50 @@ bool LDAPAccessStorage::hasSubscriptionImpl(EntityType type) const
|
||||
UUID LDAPAccessStorage::loginImpl(const String & user_name, const String & password, const Poco::Net::IPAddress & address, const ExternalAuthenticators & external_authenticators) const
|
||||
{
|
||||
std::scoped_lock lock(mutex);
|
||||
try
|
||||
auto id = memory_storage.find<User>(user_name);
|
||||
if (id)
|
||||
{
|
||||
auto id = memory_storage.find<User>(user_name);
|
||||
if (id)
|
||||
{
|
||||
auto user = memory_storage.tryRead<User>(*id);
|
||||
if (user && isAddressAllowedImpl(*user, address) && isPasswordCorrectImpl(*user, password, external_authenticators))
|
||||
return *id;
|
||||
}
|
||||
else
|
||||
{
|
||||
// User does not exist, so we create one, and will add it if authentication is successful.
|
||||
auto user = std::make_shared<User>();
|
||||
user->setName(user_name);
|
||||
user->authentication = Authentication(Authentication::Type::LDAP_SERVER);
|
||||
user->authentication.setServerName(ldap_server);
|
||||
auto user = memory_storage.read<User>(*id);
|
||||
|
||||
if (isAddressAllowedImpl(*user, address) && isPasswordCorrectImpl(*user, password, external_authenticators))
|
||||
{
|
||||
for (const auto& role_name : default_role_names)
|
||||
{
|
||||
std::optional<UUID> role_id = access_control_manager->find<Role>(role_name);
|
||||
if (!role_id)
|
||||
throw Exception("One of the default roles, the role '" + role_name + "', is not found", IAccessEntity::TypeInfo::get(IAccessEntity::Type::ROLE).not_found_error_code);
|
||||
roles_of_interest.insert(role_id.value());
|
||||
user->granted_roles.grant(role_id.value());
|
||||
}
|
||||
return memory_storage.insert(user);
|
||||
}
|
||||
}
|
||||
if (!isPasswordCorrectImpl(*user, password, external_authenticators))
|
||||
throwInvalidPassword();
|
||||
|
||||
if (!isAddressAllowedImpl(*user, address))
|
||||
throwAddressNotAllowed(address);
|
||||
|
||||
return *id;
|
||||
}
|
||||
catch (...)
|
||||
else
|
||||
{
|
||||
tryLogCurrentException(getLogger(), "Authentication failed for user '" + user_name + "' from access storage '" + getStorageName() + "'");
|
||||
// User does not exist, so we create one, and will add it if authentication is successful.
|
||||
auto user = std::make_shared<User>();
|
||||
user->setName(user_name);
|
||||
user->authentication = Authentication(Authentication::Type::LDAP_SERVER);
|
||||
user->authentication.setServerName(ldap_server);
|
||||
|
||||
if (!isPasswordCorrectImpl(*user, password, external_authenticators))
|
||||
throwInvalidPassword();
|
||||
|
||||
if (!isAddressAllowedImpl(*user, address))
|
||||
throwAddressNotAllowed(address);
|
||||
|
||||
for (const auto& role_name : default_role_names)
|
||||
{
|
||||
std::optional<UUID> role_id = access_control_manager->find<Role>(role_name);
|
||||
if (!role_id)
|
||||
throwDefaultRoleNotFound(role_name);
|
||||
|
||||
roles_of_interest.insert(role_id.value());
|
||||
user->granted_roles.grant(role_id.value());
|
||||
}
|
||||
|
||||
return memory_storage.insert(user);
|
||||
}
|
||||
throwCannotAuthenticate(user_name);
|
||||
}
|
||||
|
||||
void LDAPAccessStorage::throwDefaultRoleNotFound(const String & role_name)
|
||||
{
|
||||
throw Exception("One of the default roles, the role '" + role_name + "', is not found", IAccessEntity::TypeInfo::get(IAccessEntity::Type::ROLE).not_found_error_code);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -55,6 +55,8 @@ private:
|
||||
void setConfiguration(AccessControlManager * access_control_manager_, const Poco::Util::AbstractConfiguration & config, const String & prefix);
|
||||
void processRoleChange(const UUID & id, const AccessEntityPtr & entity);
|
||||
|
||||
[[noreturn]] static void throwDefaultRoleNotFound(const String & role_name);
|
||||
|
||||
mutable std::recursive_mutex mutex;
|
||||
AccessControlManager * access_control_manager = nullptr;
|
||||
String ldap_server;
|
||||
|
@ -106,14 +106,6 @@ void LDAPClient::openConnection()
|
||||
{
|
||||
std::scoped_lock lock(ldap_global_mutex);
|
||||
|
||||
const bool graceful_bind_failure = false;
|
||||
diag(openConnection(graceful_bind_failure));
|
||||
}
|
||||
|
||||
int LDAPClient::openConnection(const bool graceful_bind_failure)
|
||||
{
|
||||
std::scoped_lock lock(ldap_global_mutex);
|
||||
|
||||
closeConnection();
|
||||
|
||||
{
|
||||
@ -244,8 +236,6 @@ int LDAPClient::openConnection(const bool graceful_bind_failure)
|
||||
if (params.enable_tls == LDAPServerParams::TLSEnable::YES_STARTTLS)
|
||||
diag(ldap_start_tls_s(handle, nullptr, nullptr));
|
||||
|
||||
int rc = LDAP_OTHER;
|
||||
|
||||
switch (params.sasl_mechanism)
|
||||
{
|
||||
case LDAPServerParams::SASLMechanism::SIMPLE:
|
||||
@ -256,16 +246,15 @@ int LDAPClient::openConnection(const bool graceful_bind_failure)
|
||||
cred.bv_val = const_cast<char *>(params.password.c_str());
|
||||
cred.bv_len = params.password.size();
|
||||
|
||||
rc = ldap_sasl_bind_s(handle, dn.c_str(), LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, nullptr);
|
||||
|
||||
if (!graceful_bind_failure)
|
||||
diag(rc);
|
||||
diag(ldap_sasl_bind_s(handle, dn.c_str(), LDAP_SASL_SIMPLE, &cred, nullptr, nullptr, nullptr));
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw Exception("Unknown SASL mechanism", ErrorCodes::LDAP_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void LDAPClient::closeConnection() noexcept
|
||||
@ -286,39 +275,16 @@ bool LDAPSimpleAuthClient::check()
|
||||
if (params.user.empty())
|
||||
throw Exception("LDAP authentication of a user with empty name is not allowed", ErrorCodes::BAD_ARGUMENTS);
|
||||
|
||||
// Silently reject authentication attempt if the password is empty as if it didn't match.
|
||||
if (params.password.empty())
|
||||
return false; // Silently reject authentication attempt if the password is empty as if it didn't match.
|
||||
return false;
|
||||
|
||||
SCOPE_EXIT({ closeConnection(); });
|
||||
|
||||
const bool graceful_bind_failure = true;
|
||||
const auto rc = openConnection(graceful_bind_failure);
|
||||
// Will throw on any error, including invalid credentials.
|
||||
openConnection();
|
||||
|
||||
bool result = false;
|
||||
|
||||
switch (rc)
|
||||
{
|
||||
case LDAP_SUCCESS:
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
|
||||
case LDAP_INVALID_CREDENTIALS:
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
result = false;
|
||||
diag(rc);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
return true;
|
||||
}
|
||||
|
||||
#else // USE_LDAP
|
||||
@ -333,11 +299,6 @@ void LDAPClient::openConnection()
|
||||
throw Exception("ClickHouse was built without LDAP support", ErrorCodes::FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME);
|
||||
}
|
||||
|
||||
int LDAPClient::openConnection(const bool)
|
||||
{
|
||||
throw Exception("ClickHouse was built without LDAP support", ErrorCodes::FEATURE_IS_NOT_ENABLED_AT_BUILD_TIME);
|
||||
}
|
||||
|
||||
void LDAPClient::closeConnection() noexcept
|
||||
{
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ public:
|
||||
protected:
|
||||
MAYBE_NORETURN void diag(const int rc);
|
||||
MAYBE_NORETURN void openConnection();
|
||||
int openConnection(const bool graceful_bind_failure = false);
|
||||
void closeConnection() noexcept;
|
||||
|
||||
protected:
|
||||
|
@ -42,6 +42,7 @@ struct LDAPServerParams
|
||||
|
||||
enum class SASLMechanism
|
||||
{
|
||||
UNKNOWN,
|
||||
SIMPLE
|
||||
};
|
||||
|
||||
|
@ -425,7 +425,7 @@ UUID MultipleAccessStorage::loginImpl(const String & user_name, const String & p
|
||||
throw;
|
||||
}
|
||||
}
|
||||
throwCannotAuthenticate(user_name);
|
||||
throwNotFound(EntityType::USER, user_name);
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user