mirror of
https://github.com/ClickHouse/ClickHouse.git
synced 2024-11-22 15:42:02 +00:00
Move event handling away from Context.
This commit is contained in:
parent
6cac4a919b
commit
4f85c148b5
@ -218,7 +218,7 @@ 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.
|
||||
auto user = connection_context.getAccessControlManager().getUser(user_name);
|
||||
auto user = connection_context.getAccessControlManager().read<User>(user_name);
|
||||
const DB::Authentication::Type user_auth_type = user->authentication.getType();
|
||||
if (user_auth_type != DB::Authentication::DOUBLE_SHA1_PASSWORD && user_auth_type != DB::Authentication::PLAINTEXT_PASSWORD && user_auth_type != DB::Authentication::NO_PASSWORD)
|
||||
{
|
||||
|
@ -2,10 +2,9 @@
|
||||
#include <Access/MultipleAccessStorage.h>
|
||||
#include <Access/MemoryAccessStorage.h>
|
||||
#include <Access/UsersConfigAccessStorage.h>
|
||||
#include <Access/User.h>
|
||||
#include <Access/QuotaContextFactory.h>
|
||||
#include <Access/AccessRightsContextFactory.h>
|
||||
#include <Access/RowPolicyContextFactory.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Access/QuotaContextFactory.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
@ -24,8 +23,9 @@ namespace
|
||||
|
||||
AccessControlManager::AccessControlManager()
|
||||
: MultipleAccessStorage(createStorages()),
|
||||
quota_context_factory(std::make_unique<QuotaContextFactory>(*this)),
|
||||
row_policy_context_factory(std::make_unique<RowPolicyContextFactory>(*this))
|
||||
access_rights_context_factory(std::make_unique<AccessRightsContextFactory>(*this)),
|
||||
row_policy_context_factory(std::make_unique<RowPolicyContextFactory>(*this)),
|
||||
quota_context_factory(std::make_unique<QuotaContextFactory>(*this))
|
||||
{
|
||||
}
|
||||
|
||||
@ -35,53 +35,6 @@ AccessControlManager::~AccessControlManager()
|
||||
}
|
||||
|
||||
|
||||
UserPtr AccessControlManager::getUser(
|
||||
const String & user_name, std::function<void(const UserPtr &)> on_change, ext::scope_guard * subscription) const
|
||||
{
|
||||
return getUser(getID<User>(user_name), std::move(on_change), subscription);
|
||||
}
|
||||
|
||||
|
||||
UserPtr AccessControlManager::getUser(
|
||||
const UUID & user_id, std::function<void(const UserPtr &)> on_change, ext::scope_guard * subscription) const
|
||||
{
|
||||
if (on_change && subscription)
|
||||
{
|
||||
*subscription = subscribeForChanges(user_id, [on_change](const UUID &, const AccessEntityPtr & user)
|
||||
{
|
||||
if (user)
|
||||
on_change(typeid_cast<UserPtr>(user));
|
||||
});
|
||||
}
|
||||
return read<User>(user_id);
|
||||
}
|
||||
|
||||
|
||||
UserPtr AccessControlManager::authorizeAndGetUser(
|
||||
const String & user_name,
|
||||
const String & password,
|
||||
const Poco::Net::IPAddress & address,
|
||||
std::function<void(const UserPtr &)> on_change,
|
||||
ext::scope_guard * subscription) const
|
||||
{
|
||||
return authorizeAndGetUser(getID<User>(user_name), password, address, std::move(on_change), subscription);
|
||||
}
|
||||
|
||||
|
||||
UserPtr AccessControlManager::authorizeAndGetUser(
|
||||
const UUID & user_id,
|
||||
const String & password,
|
||||
const Poco::Net::IPAddress & address,
|
||||
std::function<void(const UserPtr &)> on_change,
|
||||
ext::scope_guard * subscription) const
|
||||
{
|
||||
auto user = getUser(user_id, on_change, subscription);
|
||||
user->allowed_client_hosts.checkContains(address, user->getName());
|
||||
user->authentication.checkPassword(password, user->getName());
|
||||
return user;
|
||||
}
|
||||
|
||||
|
||||
void AccessControlManager::loadFromConfig(const Poco::Util::AbstractConfiguration & users_config)
|
||||
{
|
||||
auto & users_config_access_storage = dynamic_cast<UsersConfigAccessStorage &>(getStorageByIndex(1));
|
||||
@ -89,9 +42,10 @@ void AccessControlManager::loadFromConfig(const Poco::Util::AbstractConfiguratio
|
||||
}
|
||||
|
||||
|
||||
AccessRightsContextPtr AccessControlManager::getAccessRightsContext(const UserPtr & user, const ClientInfo & client_info, const Settings & settings, const String & current_database) const
|
||||
AccessRightsContextPtr AccessControlManager::getAccessRightsContext(
|
||||
const UUID & user_id, const Settings & settings, const String & current_database, const ClientInfo & client_info) const
|
||||
{
|
||||
return std::make_shared<AccessRightsContext>(user, client_info, settings, current_database);
|
||||
return access_rights_context_factory->createContext(user_id, settings, current_database, client_info);
|
||||
}
|
||||
|
||||
|
||||
|
@ -2,7 +2,6 @@
|
||||
|
||||
#include <Access/MultipleAccessStorage.h>
|
||||
#include <Poco/AutoPtr.h>
|
||||
#include <ext/scope_guard.h>
|
||||
#include <memory>
|
||||
|
||||
|
||||
@ -20,10 +19,9 @@ namespace Poco
|
||||
|
||||
namespace DB
|
||||
{
|
||||
struct User;
|
||||
using UserPtr = std::shared_ptr<const User>;
|
||||
class AccessRightsContext;
|
||||
using AccessRightsContextPtr = std::shared_ptr<const AccessRightsContext>;
|
||||
class AccessRightsContextFactory;
|
||||
class RowPolicyContext;
|
||||
using RowPolicyContextPtr = std::shared_ptr<const RowPolicyContext>;
|
||||
class RowPolicyContextFactory;
|
||||
@ -44,21 +42,20 @@ public:
|
||||
|
||||
void loadFromConfig(const Poco::Util::AbstractConfiguration & users_config);
|
||||
|
||||
UserPtr getUser(const String & user_name, std::function<void(const UserPtr &)> on_change = {}, ext::scope_guard * subscription = nullptr) const;
|
||||
UserPtr getUser(const UUID & user_id, std::function<void(const UserPtr &)> on_change = {}, ext::scope_guard * subscription = nullptr) const;
|
||||
UserPtr authorizeAndGetUser(const String & user_name, const String & password, const Poco::Net::IPAddress & address, std::function<void(const UserPtr &)> on_change = {}, ext::scope_guard * subscription = nullptr) const;
|
||||
UserPtr authorizeAndGetUser(const UUID & user_id, const String & password, const Poco::Net::IPAddress & address, std::function<void(const UserPtr &)> on_change = {}, ext::scope_guard * subscription = nullptr) const;
|
||||
|
||||
AccessRightsContextPtr getAccessRightsContext(const UserPtr & user, const ClientInfo & client_info, const Settings & settings, const String & current_database) const;
|
||||
AccessRightsContextPtr getAccessRightsContext(
|
||||
const UUID & user_id, const Settings & settings, const String & current_database, const ClientInfo & client_info) const;
|
||||
|
||||
RowPolicyContextPtr getRowPolicyContext(const UUID & user_id) const;
|
||||
|
||||
QuotaContextPtr getQuotaContext(const UUID & user_id, const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key) const;
|
||||
QuotaContextPtr getQuotaContext(
|
||||
const UUID & user_id, const String & user_name, const Poco::Net::IPAddress & address, const String & custom_quota_key) const;
|
||||
|
||||
std::vector<QuotaUsageInfo> getQuotaUsageInfo() const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<QuotaContextFactory> quota_context_factory;
|
||||
std::unique_ptr<AccessRightsContextFactory> access_rights_context_factory;
|
||||
std::unique_ptr<RowPolicyContextFactory> row_policy_context_factory;
|
||||
std::unique_ptr<QuotaContextFactory> quota_context_factory;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,7 @@
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Access/AccessControlManager.h>
|
||||
#include <Access/RowPolicyContext.h>
|
||||
#include <Access/QuotaContext.h>
|
||||
#include <Access/User.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/quoteString.h>
|
||||
@ -6,6 +9,7 @@
|
||||
#include <Poco/Logger.h>
|
||||
#include <common/logger_useful.h>
|
||||
#include <boost/smart_ptr/make_shared_object.hpp>
|
||||
#include <boost/range/algorithm/fill.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
|
||||
@ -17,6 +21,7 @@ namespace ErrorCodes
|
||||
extern const int READONLY;
|
||||
extern const int QUERY_IS_PROHIBITED;
|
||||
extern const int FUNCTION_NOT_ALLOWED;
|
||||
extern const int UNKNOWN_USER;
|
||||
}
|
||||
|
||||
|
||||
@ -85,25 +90,69 @@ AccessRightsContext::AccessRightsContext()
|
||||
{
|
||||
auto everything_granted = boost::make_shared<AccessRights>();
|
||||
everything_granted->grant(AccessType::ALL);
|
||||
result_access_cache[0] = std::move(everything_granted);
|
||||
boost::range::fill(result_access_cache, everything_granted);
|
||||
row_policy_context = std::make_shared<RowPolicyContext>();
|
||||
quota_context = std::make_shared<QuotaContext>();
|
||||
}
|
||||
|
||||
|
||||
AccessRightsContext::AccessRightsContext(const UserPtr & user_, const ClientInfo & client_info_, const Settings & settings, const String & current_database_)
|
||||
: user(user_)
|
||||
, readonly(settings.readonly)
|
||||
, allow_ddl(settings.allow_ddl)
|
||||
, allow_introspection(settings.allow_introspection_functions)
|
||||
, current_database(current_database_)
|
||||
, interface(client_info_.interface)
|
||||
, http_method(client_info_.http_method)
|
||||
, trace_log(&Poco::Logger::get("AccessRightsContext (" + user_->getName() + ")"))
|
||||
AccessRightsContext::AccessRightsContext(const AccessControlManager & manager_, const Params & params_)
|
||||
: manager(&manager_)
|
||||
, params(params_)
|
||||
{
|
||||
subscription_for_user_change = manager->subscribeForChanges(
|
||||
*params.user_id, [this](const UUID &, const AccessEntityPtr & entity)
|
||||
{
|
||||
UserPtr changed_user = entity ? typeid_cast<UserPtr>(entity) : nullptr;
|
||||
std::lock_guard lock{mutex};
|
||||
setUser(changed_user);
|
||||
});
|
||||
|
||||
setUser(manager->read<User>(*params.user_id));
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsContext::setUser(const UserPtr & user_) const
|
||||
{
|
||||
user = user_;
|
||||
if (!user)
|
||||
{
|
||||
/// User has been dropped.
|
||||
auto nothing_granted = boost::make_shared<AccessRights>();
|
||||
boost::range::fill(result_access_cache, nothing_granted);
|
||||
subscription_for_user_change = {};
|
||||
row_policy_context = std::make_shared<RowPolicyContext>();
|
||||
quota_context = std::make_shared<QuotaContext>();
|
||||
return;
|
||||
}
|
||||
|
||||
user_name = user->getName();
|
||||
trace_log = &Poco::Logger::get("AccessRightsContext (" + user_name + ")");
|
||||
boost::range::fill(result_access_cache, nullptr /* need recalculate */);
|
||||
row_policy_context = manager->getRowPolicyContext(*params.user_id);
|
||||
quota_context = manager->getQuotaContext(*params.user_id, user_name, params.address, params.quota_key);
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsContext::checkPassword(const String & password) const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (!user)
|
||||
throw Exception(user_name + ": User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
user->authentication.checkPassword(password, user_name);
|
||||
}
|
||||
|
||||
void AccessRightsContext::checkHostIsAllowed() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
if (!user)
|
||||
throw Exception(user_name + ": User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
user->allowed_client_hosts.checkContains(params.address, user_name);
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const
|
||||
bool AccessRightsContext::checkAccessImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const
|
||||
{
|
||||
auto result_access = calculateResultAccess(grant_option);
|
||||
bool is_granted = result_access->isGranted(access, args...);
|
||||
@ -126,12 +175,16 @@ bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & acc
|
||||
auto show_error = [&](const String & msg, [[maybe_unused]] int error_code)
|
||||
{
|
||||
if constexpr (mode == THROW_IF_ACCESS_DENIED)
|
||||
throw Exception(user->getName() + ": " + msg, error_code);
|
||||
throw Exception(user_name + ": " + msg, error_code);
|
||||
else if constexpr (mode == LOG_WARNING_IF_ACCESS_DENIED)
|
||||
LOG_WARNING(log_, user->getName() + ": " + msg + formatSkippedMessage(args...));
|
||||
LOG_WARNING(log_, user_name + ": " + msg + formatSkippedMessage(args...));
|
||||
};
|
||||
|
||||
if (grant_option && calculateResultAccess(false, readonly, allow_ddl, allow_introspection)->isGranted(access, args...))
|
||||
if (!user)
|
||||
{
|
||||
show_error("User has been dropped", ErrorCodes::UNKNOWN_USER);
|
||||
}
|
||||
else if (grant_option && calculateResultAccess(false, params.readonly, params.allow_ddl, params.allow_introspection)->isGranted(access, args...))
|
||||
{
|
||||
show_error(
|
||||
"Not enough privileges. "
|
||||
@ -140,9 +193,9 @@ bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & acc
|
||||
+ AccessRightsElement{access, args...}.toString() + " WITH GRANT OPTION",
|
||||
ErrorCodes::ACCESS_DENIED);
|
||||
}
|
||||
else if (readonly && calculateResultAccess(false, false, allow_ddl, allow_introspection)->isGranted(access, args...))
|
||||
else if (params.readonly && calculateResultAccess(false, false, params.allow_ddl, params.allow_introspection)->isGranted(access, args...))
|
||||
{
|
||||
if (interface == ClientInfo::Interface::HTTP && http_method == ClientInfo::HTTPMethod::GET)
|
||||
if (params.interface == ClientInfo::Interface::HTTP && params.http_method == ClientInfo::HTTPMethod::GET)
|
||||
show_error(
|
||||
"Cannot execute query in readonly mode. "
|
||||
"For queries over HTTP, method GET implies readonly. You should use method POST for modifying queries",
|
||||
@ -150,11 +203,11 @@ bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & acc
|
||||
else
|
||||
show_error("Cannot execute query in readonly mode", ErrorCodes::READONLY);
|
||||
}
|
||||
else if (!allow_ddl && calculateResultAccess(false, readonly, true, allow_introspection)->isGranted(access, args...))
|
||||
else if (!params.allow_ddl && calculateResultAccess(false, params.readonly, true, params.allow_introspection)->isGranted(access, args...))
|
||||
{
|
||||
show_error("Cannot execute query. DDL queries are prohibited for the user", ErrorCodes::QUERY_IS_PROHIBITED);
|
||||
}
|
||||
else if (!allow_introspection && calculateResultAccess(false, readonly, allow_ddl, true)->isGranted(access, args...))
|
||||
else if (!params.allow_introspection && calculateResultAccess(false, params.readonly, params.allow_ddl, true)->isGranted(access, args...))
|
||||
{
|
||||
show_error("Introspection functions are disabled, because setting 'allow_introspection_functions' is set to 0", ErrorCodes::FUNCTION_NOT_ALLOWED);
|
||||
}
|
||||
@ -171,94 +224,94 @@ bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessFlags & acc
|
||||
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessRightsElement & element) const
|
||||
bool AccessRightsContext::checkAccessImpl(Poco::Logger * log_, const AccessRightsElement & element) const
|
||||
{
|
||||
if (element.any_database)
|
||||
{
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags);
|
||||
}
|
||||
else if (element.any_table)
|
||||
{
|
||||
if (element.database.empty())
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, current_database);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, params.current_database);
|
||||
else
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, element.database);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database);
|
||||
}
|
||||
else if (element.any_column)
|
||||
{
|
||||
if (element.database.empty())
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, current_database, element.table);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, params.current_database, element.table);
|
||||
else
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (element.database.empty())
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, current_database, element.table, element.columns);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, params.current_database, element.table, element.columns);
|
||||
else
|
||||
return checkImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table, element.columns);
|
||||
return checkAccessImpl<mode, grant_option>(log_, element.access_flags, element.database, element.table, element.columns);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool AccessRightsContext::checkImpl(Poco::Logger * log_, const AccessRightsElements & elements) const
|
||||
bool AccessRightsContext::checkAccessImpl(Poco::Logger * log_, const AccessRightsElements & elements) const
|
||||
{
|
||||
for (const auto & element : elements)
|
||||
if (!checkImpl<mode, grant_option>(log_, element))
|
||||
if (!checkAccessImpl<mode, grant_option>(log_, element))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void AccessRightsContext::check(const AccessFlags & access) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database); }
|
||||
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table); }
|
||||
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, column); }
|
||||
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::check(const AccessRightsElement & access) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
void AccessRightsContext::check(const AccessRightsElements & access) const { checkImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access, const std::string_view & database) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, column); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkAccess(const AccessRightsElement & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
void AccessRightsContext::checkAccess(const AccessRightsElements & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, column); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(const AccessRightsElement & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
bool AccessRightsContext::isGranted(const AccessRightsElements & access) const { return checkImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, column); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(const AccessRightsElement & access) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
bool AccessRightsContext::isGranted(const AccessRightsElements & access) const { return checkAccessImpl<RETURN_FALSE_IF_ACCESS_DENIED, false>(nullptr, access); }
|
||||
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, column); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElement & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElements & access) const { return checkImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, column); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access, database, table, columns); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElement & access) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
bool AccessRightsContext::isGranted(Poco::Logger * log_, const AccessRightsElements & access) const { return checkAccessImpl<LOG_WARNING_IF_ACCESS_DENIED, false>(log_, access); }
|
||||
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, column); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessRightsElement & access) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessRightsElements & access) const { checkImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, column); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access, database, table, columns); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessRightsElement & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
void AccessRightsContext::checkGrantOption(const AccessRightsElements & access) const { checkAccessImpl<THROW_IF_ACCESS_DENIED, true>(nullptr, access); }
|
||||
|
||||
|
||||
boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess(bool grant_option) const
|
||||
{
|
||||
return calculateResultAccess(grant_option, readonly, allow_ddl, allow_introspection);
|
||||
return calculateResultAccess(grant_option, params.readonly, params.allow_ddl, params.allow_introspection);
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess(bool grant_option, UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const
|
||||
{
|
||||
size_t cache_index = static_cast<size_t>(readonly_ != readonly)
|
||||
+ static_cast<size_t>(allow_ddl_ != allow_ddl) * 2 +
|
||||
+ static_cast<size_t>(allow_introspection_ != allow_introspection) * 3
|
||||
size_t cache_index = static_cast<size_t>(readonly_ != params.readonly)
|
||||
+ static_cast<size_t>(allow_ddl_ != params.allow_ddl) * 2 +
|
||||
+ static_cast<size_t>(allow_introspection_ != params.allow_introspection) * 3
|
||||
+ static_cast<size_t>(grant_option) * 4;
|
||||
assert(cache_index < std::size(result_access_cache));
|
||||
auto cached = result_access_cache[cache_index].load();
|
||||
@ -306,10 +359,75 @@ boost::shared_ptr<const AccessRights> AccessRightsContext::calculateResultAccess
|
||||
|
||||
result_access_cache[cache_index].store(result_ptr);
|
||||
|
||||
if (trace_log && (readonly == readonly_) && (allow_ddl == allow_ddl_) && (allow_introspection == allow_introspection_))
|
||||
if (trace_log && (params.readonly == readonly_) && (params.allow_ddl == allow_ddl_) && (params.allow_introspection == allow_introspection_))
|
||||
LOG_TRACE(trace_log, "List of all grants: " << result_ptr->toString() << (grant_option ? " WITH GRANT OPTION" : ""));
|
||||
|
||||
return result_ptr;
|
||||
}
|
||||
|
||||
|
||||
UserPtr AccessRightsContext::getUser() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
return user;
|
||||
}
|
||||
|
||||
String AccessRightsContext::getUserName() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
return user_name;
|
||||
}
|
||||
|
||||
RowPolicyContextPtr AccessRightsContext::getRowPolicy() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
return row_policy_context;
|
||||
}
|
||||
|
||||
QuotaContextPtr AccessRightsContext::getQuota() const
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
return quota_context;
|
||||
}
|
||||
|
||||
|
||||
bool operator <(const AccessRightsContext::Params & lhs, const AccessRightsContext::Params & rhs)
|
||||
{
|
||||
#define ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(field) \
|
||||
if (lhs.field < rhs.field) \
|
||||
return true; \
|
||||
if (lhs.field > rhs.field) \
|
||||
return false
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(user_id);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(address);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(quota_key);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(current_database);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(readonly);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(allow_ddl);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(allow_introspection);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(interface);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(http_method);
|
||||
return false;
|
||||
#undef ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER
|
||||
}
|
||||
|
||||
|
||||
bool operator ==(const AccessRightsContext::Params & lhs, const AccessRightsContext::Params & rhs)
|
||||
{
|
||||
#define ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(field) \
|
||||
if (lhs.field != rhs.field) \
|
||||
return false
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(user_id);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(address);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(quota_key);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(current_database);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(readonly);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(allow_ddl);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(allow_introspection);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(interface);
|
||||
ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER(http_method);
|
||||
return true;
|
||||
#undef ACCESS_RIGHTS_CONTEXT_PARAMS_COMPARE_HELPER
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
|
||||
#include <Access/AccessRights.h>
|
||||
#include <Interpreters/ClientInfo.h>
|
||||
#include <Core/UUID.h>
|
||||
#include <ext/scope_guard.h>
|
||||
#include <ext/shared_ptr_helper.h>
|
||||
#include <boost/smart_ptr/atomic_shared_ptr.hpp>
|
||||
#include <mutex>
|
||||
|
||||
@ -10,29 +13,62 @@ namespace Poco { class Logger; }
|
||||
|
||||
namespace DB
|
||||
{
|
||||
struct Settings;
|
||||
struct User;
|
||||
using UserPtr = std::shared_ptr<const User>;
|
||||
struct RowPolicyContext;
|
||||
using RowPolicyContextPtr = std::shared_ptr<const RowPolicyContext>;
|
||||
struct QuotaContext;
|
||||
using QuotaContextPtr = std::shared_ptr<const QuotaContext>;
|
||||
struct Settings;
|
||||
class AccessControlManager;
|
||||
|
||||
|
||||
class AccessRightsContext
|
||||
{
|
||||
public:
|
||||
struct Params
|
||||
{
|
||||
std::optional<UUID> user_id;
|
||||
UInt64 readonly = 0;
|
||||
bool allow_ddl = false;
|
||||
bool allow_introspection = false;
|
||||
String current_database;
|
||||
ClientInfo::Interface interface = ClientInfo::Interface::TCP;
|
||||
ClientInfo::HTTPMethod http_method = ClientInfo::HTTPMethod::UNKNOWN;
|
||||
Poco::Net::IPAddress address;
|
||||
String quota_key;
|
||||
|
||||
friend bool operator ==(const Params & lhs, const Params & rhs);
|
||||
friend bool operator !=(const Params & lhs, const Params & rhs) { return !(lhs == rhs); }
|
||||
friend bool operator <(const Params & lhs, const Params & rhs);
|
||||
friend bool operator >(const Params & lhs, const Params & rhs) { return rhs < lhs; }
|
||||
friend bool operator <=(const Params & lhs, const Params & rhs) { return !(rhs < lhs); }
|
||||
friend bool operator >=(const Params & lhs, const Params & rhs) { return !(lhs < rhs); }
|
||||
};
|
||||
|
||||
/// Default constructor creates access rights' context which allows everything.
|
||||
AccessRightsContext();
|
||||
|
||||
AccessRightsContext(const UserPtr & user_, const ClientInfo & client_info_, const Settings & settings, const String & current_database_);
|
||||
const Params & getParams() const { return params; }
|
||||
UserPtr getUser() const;
|
||||
String getUserName() const;
|
||||
|
||||
void checkPassword(const String & password) const;
|
||||
void checkHostIsAllowed() const;
|
||||
|
||||
RowPolicyContextPtr getRowPolicy() const;
|
||||
QuotaContextPtr getQuota() const;
|
||||
|
||||
/// Checks if a specified access granted, and throws an exception if not.
|
||||
/// Empty database means the current database.
|
||||
void check(const AccessFlags & access) const;
|
||||
void check(const AccessFlags & access, const std::string_view & database) const;
|
||||
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
|
||||
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
void check(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
void check(const AccessRightsElement & access) const;
|
||||
void check(const AccessRightsElements & access) const;
|
||||
void checkAccess(const AccessFlags & access) const;
|
||||
void checkAccess(const AccessFlags & access, const std::string_view & database) const;
|
||||
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const;
|
||||
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const;
|
||||
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const;
|
||||
void checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const;
|
||||
void checkAccess(const AccessRightsElement & access) const;
|
||||
void checkAccess(const AccessRightsElements & access) const;
|
||||
|
||||
/// Checks if a specified access granted.
|
||||
bool isGranted(const AccessFlags & access) const;
|
||||
@ -65,27 +101,33 @@ public:
|
||||
void checkGrantOption(const AccessRightsElements & access) const;
|
||||
|
||||
private:
|
||||
friend class AccessRightsContextFactory;
|
||||
friend struct ext::shared_ptr_helper<AccessRightsContext>;
|
||||
AccessRightsContext(const AccessControlManager & manager_, const Params & params_); /// AccessRightsContext should be created by AccessRightsContextFactory.
|
||||
|
||||
void setUser(const UserPtr & user_) const;
|
||||
|
||||
template <int mode, bool grant_option, typename... Args>
|
||||
bool checkImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const;
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessFlags & access, const Args &... args) const;
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool checkImpl(Poco::Logger * log_, const AccessRightsElement & access) const;
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessRightsElement & access) const;
|
||||
|
||||
template <int mode, bool grant_option>
|
||||
bool checkImpl(Poco::Logger * log_, const AccessRightsElements & access) const;
|
||||
bool checkAccessImpl(Poco::Logger * log_, const AccessRightsElements & access) const;
|
||||
|
||||
boost::shared_ptr<const AccessRights> calculateResultAccess(bool grant_option) const;
|
||||
boost::shared_ptr<const AccessRights> calculateResultAccess(bool grant_option, UInt64 readonly_, bool allow_ddl_, bool allow_introspection_) const;
|
||||
|
||||
const UserPtr user;
|
||||
const UInt64 readonly = 0;
|
||||
const bool allow_ddl = true;
|
||||
const bool allow_introspection = true;
|
||||
const String current_database;
|
||||
const ClientInfo::Interface interface = ClientInfo::Interface::TCP;
|
||||
const ClientInfo::HTTPMethod http_method = ClientInfo::HTTPMethod::UNKNOWN;
|
||||
Poco::Logger * const trace_log = nullptr;
|
||||
const AccessControlManager * manager = nullptr;
|
||||
const Params params;
|
||||
mutable Poco::Logger * trace_log = nullptr;
|
||||
mutable UserPtr user;
|
||||
mutable String user_name;
|
||||
mutable ext::scope_guard subscription_for_user_change;
|
||||
mutable boost::atomic_shared_ptr<const AccessRights> result_access_cache[7];
|
||||
mutable RowPolicyContextPtr row_policy_context;
|
||||
mutable QuotaContextPtr quota_context;
|
||||
mutable std::mutex mutex;
|
||||
};
|
||||
|
||||
|
44
dbms/src/Access/AccessRightsContextFactory.cpp
Normal file
44
dbms/src/Access/AccessRightsContextFactory.cpp
Normal file
@ -0,0 +1,44 @@
|
||||
#include <Access/AccessRightsContextFactory.h>
|
||||
#include <Access/AccessControlManager.h>
|
||||
#include <Core/Settings.h>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
AccessRightsContextFactory::AccessRightsContextFactory(const AccessControlManager & manager_)
|
||||
: manager(manager_), cache(600000 /* 10 minutes */) {}
|
||||
|
||||
AccessRightsContextFactory::~AccessRightsContextFactory() = default;
|
||||
|
||||
|
||||
AccessRightsContextPtr AccessRightsContextFactory::createContext(const Params & params)
|
||||
{
|
||||
std::lock_guard lock{mutex};
|
||||
auto x = cache.get(params);
|
||||
if (x)
|
||||
return *x;
|
||||
auto res = ext::shared_ptr_helper<AccessRightsContext>::create(manager, params);
|
||||
cache.add(params, res);
|
||||
return res;
|
||||
}
|
||||
|
||||
AccessRightsContextPtr AccessRightsContextFactory::createContext(
|
||||
const UUID & user_id,
|
||||
const Settings & settings,
|
||||
const String & current_database,
|
||||
const ClientInfo & client_info)
|
||||
{
|
||||
Params params;
|
||||
params.user_id = user_id;
|
||||
params.current_database = current_database;
|
||||
params.readonly = settings.readonly;
|
||||
params.allow_ddl = settings.allow_ddl;
|
||||
params.allow_introspection = settings.allow_introspection_functions;
|
||||
params.interface = client_info.interface;
|
||||
params.http_method = client_info.http_method;
|
||||
params.address = client_info.current_address.host();
|
||||
params.quota_key = client_info.quota_key;
|
||||
return createContext(params);
|
||||
}
|
||||
|
||||
}
|
29
dbms/src/Access/AccessRightsContextFactory.h
Normal file
29
dbms/src/Access/AccessRightsContextFactory.h
Normal file
@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Poco/ExpireCache.h>
|
||||
#include <mutex>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class AccessControlManager;
|
||||
|
||||
|
||||
class AccessRightsContextFactory
|
||||
{
|
||||
public:
|
||||
AccessRightsContextFactory(const AccessControlManager & manager_);
|
||||
~AccessRightsContextFactory();
|
||||
|
||||
using Params = AccessRightsContext::Params;
|
||||
AccessRightsContextPtr createContext(const Params & params);
|
||||
AccessRightsContextPtr createContext(const UUID & user_id, const Settings & settings, const String & current_database, const ClientInfo & client_info);
|
||||
|
||||
private:
|
||||
const AccessControlManager & manager;
|
||||
Poco::ExpireCache<Params, AccessRightsContextPtr> cache;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
#include <Access/IAccessStorage.h>
|
||||
#include <Access/User.h>
|
||||
#include <Common/Exception.h>
|
||||
#include <Common/quoteString.h>
|
||||
#include <IO/WriteHelpers.h>
|
||||
@ -15,6 +16,7 @@ namespace ErrorCodes
|
||||
extern const int ACCESS_ENTITY_ALREADY_EXISTS;
|
||||
extern const int ACCESS_ENTITY_FOUND_DUPLICATES;
|
||||
extern const int ACCESS_ENTITY_STORAGE_READONLY;
|
||||
extern const int UNKNOWN_USER;
|
||||
}
|
||||
|
||||
|
||||
@ -365,8 +367,13 @@ void IAccessStorage::throwNotFound(const UUID & id) const
|
||||
|
||||
void IAccessStorage::throwNotFound(std::type_index type, const String & name) const
|
||||
{
|
||||
throw Exception(
|
||||
getTypeName(type) + " " + backQuote(name) + " not found in " + getStorageName(), ErrorCodes::ACCESS_ENTITY_NOT_FOUND);
|
||||
int error_code;
|
||||
if (type == typeid(User))
|
||||
error_code = ErrorCodes::UNKNOWN_USER;
|
||||
else
|
||||
error_code = ErrorCodes::ACCESS_ENTITY_NOT_FOUND;
|
||||
|
||||
throw Exception(getTypeName(type) + " " + backQuote(name) + " not found in " + getStorageName(), error_code);
|
||||
}
|
||||
|
||||
|
||||
|
@ -4,14 +4,12 @@
|
||||
#include <ext/scope_guard.h>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
|
||||
namespace DB
|
||||
{
|
||||
class AccessControlManager;
|
||||
|
||||
|
||||
/// Stores read and parsed row policies.
|
||||
class RowPolicyContextFactory
|
||||
{
|
||||
|
@ -953,7 +953,7 @@ public:
|
||||
throw Exception("Wrong size of auth response. Expected: " + std::to_string(Poco::SHA1Engine::DIGEST_SIZE) + " bytes, received: " + std::to_string(auth_response->size()) + " bytes.",
|
||||
ErrorCodes::UNKNOWN_EXCEPTION);
|
||||
|
||||
auto user = context.getAccessControlManager().getUser(user_name);
|
||||
auto user = context.getAccessControlManager().read<User>(user_name);
|
||||
|
||||
Poco::SHA1Engine::Digest double_sha1_value = user->authentication.getPasswordDoubleSHA1();
|
||||
assert(double_sha1_value.size() == Poco::SHA1Engine::DIGEST_SIZE);
|
||||
|
@ -27,11 +27,10 @@
|
||||
#include <Interpreters/ActionLocksManager.h>
|
||||
#include <Core/Settings.h>
|
||||
#include <Access/AccessControlManager.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Access/RowPolicyContext.h>
|
||||
#include <Access/User.h>
|
||||
#include <Access/SettingsConstraints.h>
|
||||
#include <Access/QuotaContext.h>
|
||||
#include <Access/RowPolicyContext.h>
|
||||
#include <Access/AccessRightsContext.h>
|
||||
#include <Interpreters/ExpressionJIT.h>
|
||||
#include <Dictionaries/Embedded/GeoDictionariesLoader.h>
|
||||
#include <Interpreters/EmbeddedDictionaries.h>
|
||||
@ -326,10 +325,8 @@ Context & Context::operator=(const Context &) = default;
|
||||
Context Context::createGlobal()
|
||||
{
|
||||
Context res;
|
||||
res.quota = std::make_shared<QuotaContext>();
|
||||
res.row_policy = std::make_shared<RowPolicyContext>();
|
||||
res.initial_row_policy = std::make_shared<RowPolicyContext>();
|
||||
res.access_rights = std::make_shared<AccessRightsContext>();
|
||||
res.initial_row_policy = std::make_shared<RowPolicyContext>();
|
||||
res.shared = std::make_shared<ContextShared>();
|
||||
return res;
|
||||
}
|
||||
@ -624,39 +621,17 @@ const Poco::Util::AbstractConfiguration & Context::getConfigRef() const
|
||||
return shared->config ? *shared->config : Poco::Util::Application::instance().config();
|
||||
}
|
||||
|
||||
|
||||
AccessControlManager & Context::getAccessControlManager()
|
||||
{
|
||||
auto lock = getLock();
|
||||
return shared->access_control_manager;
|
||||
}
|
||||
|
||||
const AccessControlManager & Context::getAccessControlManager() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
return shared->access_control_manager;
|
||||
}
|
||||
|
||||
template <typename... Args>
|
||||
void Context::checkAccessImpl(const Args &... args) const
|
||||
{
|
||||
getAccessRights()->check(args...);
|
||||
}
|
||||
|
||||
void Context::checkAccess(const AccessFlags & access) const { return checkAccessImpl(access); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database) const { return checkAccessImpl(access, database); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl(access, database, table); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl(access, database, table, column); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl(access, database, table, columns); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl(access, database, table, columns); }
|
||||
void Context::checkAccess(const AccessRightsElement & access) const { return checkAccessImpl(access); }
|
||||
void Context::checkAccess(const AccessRightsElements & access) const { return checkAccessImpl(access); }
|
||||
|
||||
void Context::setInitialRowPolicy()
|
||||
{
|
||||
auto initial_user_id = getAccessControlManager().find<User>(client_info.initial_user);
|
||||
if (initial_user_id)
|
||||
initial_row_policy = getAccessControlManager().getRowPolicyContext(*initial_user_id);
|
||||
}
|
||||
|
||||
void Context::setUsersConfig(const ConfigurationPtr & config)
|
||||
{
|
||||
@ -671,10 +646,112 @@ ConfigurationPtr Context::getUsersConfig()
|
||||
return shared->users_config;
|
||||
}
|
||||
|
||||
|
||||
void Context::setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key)
|
||||
{
|
||||
auto lock = getLock();
|
||||
|
||||
client_info.current_user = name;
|
||||
client_info.current_password = password;
|
||||
client_info.current_address = address;
|
||||
if (!quota_key.empty())
|
||||
client_info.quota_key = quota_key;
|
||||
|
||||
auto new_user_id = getAccessControlManager().getID<User>(name);
|
||||
auto new_access_rights = getAccessControlManager().getAccessRightsContext(new_user_id, settings, current_database, client_info);
|
||||
new_access_rights->checkHostIsAllowed();
|
||||
new_access_rights->checkPassword(password);
|
||||
|
||||
user_id = new_user_id;
|
||||
access_rights = std::move(new_access_rights);
|
||||
|
||||
calculateUserSettings();
|
||||
}
|
||||
|
||||
std::shared_ptr<const User> Context::getUser() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
return access_rights->getUser();
|
||||
}
|
||||
|
||||
String Context::getUserName() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
return access_rights->getUserName();
|
||||
}
|
||||
|
||||
UUID Context::getUserID() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (!user_id)
|
||||
throw Exception("No current user", ErrorCodes::LOGICAL_ERROR);
|
||||
return *user_id;
|
||||
}
|
||||
|
||||
|
||||
void Context::calculateAccessRights()
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (user_id)
|
||||
access_rights = getAccessControlManager().getAccessRightsContext(*user_id, settings, current_database, client_info);
|
||||
}
|
||||
|
||||
|
||||
template <typename... Args>
|
||||
void Context::checkAccessImpl(const Args &... args) const
|
||||
{
|
||||
getAccessRights()->checkAccess(args...);
|
||||
}
|
||||
|
||||
void Context::checkAccess(const AccessFlags & access) const { return checkAccessImpl(access); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database) const { return checkAccessImpl(access, database); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table) const { return checkAccessImpl(access, database, table); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::string_view & column) const { return checkAccessImpl(access, database, table, column); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const std::vector<std::string_view> & columns) const { return checkAccessImpl(access, database, table, columns); }
|
||||
void Context::checkAccess(const AccessFlags & access, const std::string_view & database, const std::string_view & table, const Strings & columns) const { return checkAccessImpl(access, database, table, columns); }
|
||||
void Context::checkAccess(const AccessRightsElement & access) const { return checkAccessImpl(access); }
|
||||
void Context::checkAccess(const AccessRightsElements & access) const { return checkAccessImpl(access); }
|
||||
|
||||
AccessRightsContextPtr Context::getAccessRights() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
return access_rights;
|
||||
}
|
||||
|
||||
RowPolicyContextPtr Context::getRowPolicy() const
|
||||
{
|
||||
return getAccessRights()->getRowPolicy();
|
||||
}
|
||||
|
||||
void Context::setInitialRowPolicy()
|
||||
{
|
||||
auto lock = getLock();
|
||||
auto initial_user_id = getAccessControlManager().find<User>(client_info.initial_user);
|
||||
if (initial_user_id)
|
||||
initial_row_policy = getAccessControlManager().getRowPolicyContext(*initial_user_id);
|
||||
}
|
||||
|
||||
RowPolicyContextPtr Context::getInitialRowPolicy() const
|
||||
{
|
||||
auto lock = getLock();
|
||||
return initial_row_policy;
|
||||
}
|
||||
|
||||
|
||||
QuotaContextPtr Context::getQuota() const
|
||||
{
|
||||
return getAccessRights()->getQuota();
|
||||
}
|
||||
|
||||
|
||||
void Context::calculateUserSettings()
|
||||
{
|
||||
auto lock = getLock();
|
||||
String profile = user->profile;
|
||||
String profile = getUser()->profile;
|
||||
|
||||
bool old_readonly = settings.readonly;
|
||||
bool old_allow_ddl = settings.allow_ddl;
|
||||
bool old_allow_introspection_functions = settings.allow_introspection_functions;
|
||||
|
||||
/// 1) Set default settings (hardcoded values)
|
||||
/// NOTE: we ignore global_context settings (from which it is usually copied)
|
||||
@ -689,13 +766,10 @@ void Context::calculateUserSettings()
|
||||
|
||||
/// 3) Apply settings from current user
|
||||
setProfile(profile);
|
||||
}
|
||||
|
||||
void Context::calculateAccessRights()
|
||||
{
|
||||
auto lock = getLock();
|
||||
if (user)
|
||||
std::atomic_store(&access_rights, getAccessControlManager().getAccessRightsContext(user, client_info, settings, current_database));
|
||||
/// 4) Recalculate access rights if it's necessary.
|
||||
if ((settings.readonly != old_readonly) || (settings.allow_ddl != old_allow_ddl) || (settings.allow_introspection_functions != old_allow_introspection_functions))
|
||||
calculateAccessRights();
|
||||
}
|
||||
|
||||
void Context::setProfile(const String & profile)
|
||||
@ -708,50 +782,6 @@ void Context::setProfile(const String & profile)
|
||||
settings_constraints = std::move(new_constraints);
|
||||
}
|
||||
|
||||
std::shared_ptr<const User> Context::getUser() const
|
||||
{
|
||||
if (!user)
|
||||
throw Exception("No current user", ErrorCodes::LOGICAL_ERROR);
|
||||
return user;
|
||||
}
|
||||
|
||||
UUID Context::getUserID() const
|
||||
{
|
||||
if (!user)
|
||||
throw Exception("No current user", ErrorCodes::LOGICAL_ERROR);
|
||||
return user_id;
|
||||
}
|
||||
|
||||
void Context::setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key)
|
||||
{
|
||||
auto lock = getLock();
|
||||
|
||||
client_info.current_user = name;
|
||||
client_info.current_address = address;
|
||||
client_info.current_password = password;
|
||||
|
||||
if (!quota_key.empty())
|
||||
client_info.quota_key = quota_key;
|
||||
|
||||
user_id = shared->access_control_manager.getID<User>(name);
|
||||
user = shared->access_control_manager.authorizeAndGetUser(
|
||||
user_id,
|
||||
password,
|
||||
address.host(),
|
||||
[this](const UserPtr & changed_user)
|
||||
{
|
||||
user = changed_user;
|
||||
calculateAccessRights();
|
||||
},
|
||||
&subscription_for_user_change.subscription);
|
||||
|
||||
quota = getAccessControlManager().getQuotaContext(user_id, name, address.host(), quota_key);
|
||||
row_policy = getAccessControlManager().getRowPolicyContext(user_id);
|
||||
|
||||
calculateUserSettings();
|
||||
calculateAccessRights();
|
||||
}
|
||||
|
||||
|
||||
void Context::addDependencyUnsafe(const StorageID & from, const StorageID & where)
|
||||
{
|
||||
|
@ -13,7 +13,6 @@
|
||||
#include <Common/ThreadPool.h>
|
||||
#include "config_core.h"
|
||||
#include <Storages/IStorage_fwd.h>
|
||||
#include <ext/scope_guard.h>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
@ -44,9 +43,10 @@ namespace DB
|
||||
|
||||
struct ContextShared;
|
||||
class Context;
|
||||
struct User;
|
||||
class AccessRightsContext;
|
||||
using AccessRightsContextPtr = std::shared_ptr<const AccessRightsContext>;
|
||||
struct User;
|
||||
using UserPtr = std::shared_ptr<const User>;
|
||||
class RowPolicyContext;
|
||||
using RowPolicyContextPtr = std::shared_ptr<const RowPolicyContext>;
|
||||
class QuotaContext;
|
||||
@ -136,15 +136,6 @@ struct IHostContext
|
||||
|
||||
using IHostContextPtr = std::shared_ptr<IHostContext>;
|
||||
|
||||
/// Subscription for user's change. This subscription cannot be copied with the context,
|
||||
/// that's why we had to move it into a separate structure.
|
||||
struct SubscriptionForUserChange
|
||||
{
|
||||
ext::scope_guard subscription;
|
||||
SubscriptionForUserChange() {}
|
||||
SubscriptionForUserChange(const SubscriptionForUserChange &) {}
|
||||
SubscriptionForUserChange & operator =(const SubscriptionForUserChange &) { subscription = {}; return *this; }
|
||||
};
|
||||
|
||||
/** A set of known objects that can be used in the query.
|
||||
* Consists of a shared part (always common to all sessions and queries)
|
||||
@ -164,12 +155,8 @@ private:
|
||||
InputInitializer input_initializer_callback;
|
||||
InputBlocksReader input_blocks_reader;
|
||||
|
||||
std::shared_ptr<const User> user;
|
||||
UUID user_id;
|
||||
SubscriptionForUserChange subscription_for_user_change;
|
||||
std::optional<UUID> user_id;
|
||||
AccessRightsContextPtr access_rights;
|
||||
QuotaContextPtr quota; /// Current quota. By default - empty quota, that have no limits.
|
||||
RowPolicyContextPtr row_policy;
|
||||
RowPolicyContextPtr initial_row_policy;
|
||||
String current_database;
|
||||
Settings settings; /// Setting for query execution.
|
||||
@ -241,7 +228,21 @@ public:
|
||||
|
||||
AccessControlManager & getAccessControlManager();
|
||||
const AccessControlManager & getAccessControlManager() const;
|
||||
AccessRightsContextPtr getAccessRights() const { return std::atomic_load(&access_rights); }
|
||||
|
||||
/** Take the list of users, quotas and configuration profiles from this config.
|
||||
* The list of users is completely replaced.
|
||||
* The accumulated quota values are not reset if the quota is not deleted.
|
||||
*/
|
||||
void setUsersConfig(const ConfigurationPtr & config);
|
||||
ConfigurationPtr getUsersConfig();
|
||||
|
||||
/// Sets the current user, checks the password and that the specified host is allowed.
|
||||
/// Must be called before getClientInfo.
|
||||
void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key);
|
||||
|
||||
UserPtr getUser() const;
|
||||
String getUserName() const;
|
||||
UUID getUserID() const;
|
||||
|
||||
/// Checks access rights.
|
||||
/// Empty database means the current database.
|
||||
@ -254,26 +255,17 @@ public:
|
||||
void checkAccess(const AccessRightsElement & access) const;
|
||||
void checkAccess(const AccessRightsElements & access) const;
|
||||
|
||||
QuotaContextPtr getQuota() const { return quota; }
|
||||
RowPolicyContextPtr getRowPolicy() const { return row_policy; }
|
||||
AccessRightsContextPtr getAccessRights() const;
|
||||
|
||||
RowPolicyContextPtr getRowPolicy() const;
|
||||
|
||||
/// Sets an extra row policy based on `client_info.initial_user`, if it exists.
|
||||
/// TODO: we need a better solution here. It seems we should pass the initial row policy
|
||||
/// because a shard is allowed to don't have the initial user or it may be another user with the same name.
|
||||
void setInitialRowPolicy();
|
||||
RowPolicyContextPtr getInitialRowPolicy() const { return initial_row_policy; }
|
||||
RowPolicyContextPtr getInitialRowPolicy() const;
|
||||
|
||||
/** Take the list of users, quotas and configuration profiles from this config.
|
||||
* The list of users is completely replaced.
|
||||
* The accumulated quota values are not reset if the quota is not deleted.
|
||||
*/
|
||||
void setUsersConfig(const ConfigurationPtr & config);
|
||||
ConfigurationPtr getUsersConfig();
|
||||
|
||||
/// Must be called before getClientInfo.
|
||||
void setUser(const String & name, const String & password, const Poco::Net::SocketAddress & address, const String & quota_key);
|
||||
std::shared_ptr<const User> getUser() const;
|
||||
UUID getUserID() const;
|
||||
QuotaContextPtr getQuota() const;
|
||||
|
||||
/// We have to copy external tables inside executeQuery() to track limits. Therefore, set callback for it. Must set once.
|
||||
void setExternalTablesInitializer(ExternalTablesInitializer && initializer);
|
||||
@ -618,12 +610,6 @@ private:
|
||||
void calculateUserSettings();
|
||||
void calculateAccessRights();
|
||||
|
||||
/** Check if the current client has access to the specified database.
|
||||
* If access is denied, throw an exception.
|
||||
* NOTE: This method should always be called when the `shared->mutex` mutex is acquired.
|
||||
*/
|
||||
void checkDatabaseAccessRightsImpl(const std::string & database_name) const;
|
||||
|
||||
template <typename... Args>
|
||||
void checkAccessImpl(const Args &... args) const;
|
||||
|
||||
|
@ -74,7 +74,7 @@ ASTPtr InterpreterShowCreateAccessEntityQuery::getCreateUserQuery(const ASTShowC
|
||||
if (show_query.current_user)
|
||||
user = context.getUser();
|
||||
else
|
||||
user = context.getAccessControlManager().getUser(show_query.name);
|
||||
user = context.getAccessControlManager().read<User>(show_query.name);
|
||||
|
||||
auto create_query = std::make_shared<ASTCreateUserQuery>();
|
||||
create_query->name = user->getName();
|
||||
|
@ -218,7 +218,7 @@ void runOneTest(const TestDescriptor & test_descriptor)
|
||||
|
||||
try
|
||||
{
|
||||
res = acl_manager.getUser(entry.user_name)->access.isGranted(DB::AccessType::ALL, entry.database_name);
|
||||
res = acl_manager.read<DB::User>(entry.user_name)->access.isGranted(DB::AccessType::ALL, entry.database_name);
|
||||
}
|
||||
catch (const Poco::Exception &)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user