diff --git a/auth/cache.cc b/auth/cache.cc index f3b5ca791d..e75a6e7164 100644 --- a/auth/cache.cc +++ b/auth/cache.cc @@ -8,6 +8,7 @@ #include "auth/cache.hh" #include "auth/common.hh" +#include "auth/role_or_anonymous.hh" #include "auth/roles-metadata.hh" #include "cql3/query_processor.hh" #include "cql3/untyped_result_set.hh" @@ -97,6 +98,23 @@ future<> cache::prune(const resource& r) { } } +future<> cache::reload_all_permissions() noexcept { + SCYLLA_ASSERT(_permission_loader); + auto units = co_await get_units(_loading_sem, 1, _as); + const role_or_anonymous anon; + for (auto& [res, perms] : _anonymous_permissions) { + perms = co_await _permission_loader(anon, res); + } + for (auto& [role, entry] : _roles) { + auto& perms_cache = entry->cached_permissions; + auto r = role_or_anonymous(role); + for (auto& [res, perms] : perms_cache) { + perms = co_await _permission_loader(r, res); + } + } + logger.debug("Reloaded auth cache with {} entries", _roles.size()); +} + future> cache::fetch_role(const role_name_t& role) const { auto rec = make_lw_shared(); rec->version = _current_version; diff --git a/auth/cache.hh b/auth/cache.hh index e245463924..f411245421 100644 --- a/auth/cache.hh +++ b/auth/cache.hh @@ -55,6 +55,7 @@ public: void set_permission_loader(permission_loader_func loader); future get_permissions(const role_or_anonymous& role, const resource& r); future<> prune(const resource& r); + future<> reload_all_permissions() noexcept; future<> load_all(); future<> load_roles(std::unordered_set roles); static bool includes_table(const table_id&) noexcept; diff --git a/auth/ldap_role_manager.cc b/auth/ldap_role_manager.cc index c01e830214..b667e8e6c5 100644 --- a/auth/ldap_role_manager.cc +++ b/auth/ldap_role_manager.cc @@ -88,10 +88,16 @@ static const class_registrator< ldap_role_manager::ldap_role_manager( std::string_view query_template, std::string_view target_attr, std::string_view bind_name, std::string_view bind_password, + uint32_t permissions_update_interval_in_ms, + utils::observer permissions_update_interval_in_ms_observer, cql3::query_processor& qp, ::service::raft_group0_client& rg0c, ::service::migration_manager& mm, cache& cache) : _std_mgr(qp, rg0c, mm, cache), _group0_client(rg0c), _query_template(query_template), _target_attr(target_attr), _bind_name(bind_name) , _bind_password(bind_password) - , _connection_factory(bind(std::mem_fn(&ldap_role_manager::reconnect), std::ref(*this))) { + , _permissions_update_interval_in_ms(permissions_update_interval_in_ms) + , _permissions_update_interval_in_ms_observer(std::move(permissions_update_interval_in_ms_observer)) + , _connection_factory(bind(std::mem_fn(&ldap_role_manager::reconnect), std::ref(*this))) + , _cache(cache) + , _cache_pruner(make_ready_future<>()) { } ldap_role_manager::ldap_role_manager(cql3::query_processor& qp, ::service::raft_group0_client& rg0c, ::service::migration_manager& mm, cache& cache) @@ -100,6 +106,8 @@ ldap_role_manager::ldap_role_manager(cql3::query_processor& qp, ::service::raft_ qp.db().get_config().ldap_attr_role(), qp.db().get_config().ldap_bind_dn(), qp.db().get_config().ldap_bind_passwd(), + qp.db().get_config().permissions_update_interval_in_ms(), + qp.db().get_config().permissions_update_interval_in_ms.observe([this] (const uint32_t& v) { _permissions_update_interval_in_ms = v; }), qp, rg0c, mm, @@ -119,6 +127,22 @@ future<> ldap_role_manager::start() { return make_exception_future( std::runtime_error(fmt::format("error getting LDAP server address from template {}", _query_template))); } + _cache_pruner = futurize_invoke([this] () -> future<> { + while (true) { + try { + co_await seastar::sleep_abortable(std::chrono::milliseconds(_permissions_update_interval_in_ms), _as); + } catch (const seastar::sleep_aborted&) { + co_return; // ignore + } + co_await _cache.container().invoke_on_all([] (cache& c) -> future<> { + try { + co_await c.reload_all_permissions(); + } catch (...) { + mylog.warn("Cache reload all permissions failed: {}", std::current_exception()); + } + }); + } + }); return _std_mgr.start(); } @@ -175,7 +199,11 @@ future ldap_role_manager::reconnect() { future<> ldap_role_manager::stop() { _as.request_abort(); - return _std_mgr.stop().then([this] { return _connection_factory.stop(); }); + return std::move(_cache_pruner).then([this] { + return _std_mgr.stop(); + }).then([this] { + return _connection_factory.stop(); + }); } future<> ldap_role_manager::create(std::string_view name, const role_config& config, ::service::group0_batch& mc) { diff --git a/auth/ldap_role_manager.hh b/auth/ldap_role_manager.hh index a68c9b96dc..e2f27d8fad 100644 --- a/auth/ldap_role_manager.hh +++ b/auth/ldap_role_manager.hh @@ -10,6 +10,7 @@ #pragma once #include +#include #include #include "ent/ldap/ldap_connection.hh" @@ -34,14 +35,22 @@ class ldap_role_manager : public role_manager { seastar::sstring _target_attr; ///< LDAP entry attribute containing the Scylla role name. seastar::sstring _bind_name; ///< Username for LDAP simple bind. seastar::sstring _bind_password; ///< Password for LDAP simple bind. + + uint32_t _permissions_update_interval_in_ms; + utils::observer _permissions_update_interval_in_ms_observer; + mutable ldap_reuser _connection_factory; // Potentially modified by query_granted(). seastar::abort_source _as; + cache& _cache; + seastar::future<> _cache_pruner; public: ldap_role_manager( std::string_view query_template, ///< LDAP query template as described in Scylla documentation. std::string_view target_attr, ///< LDAP entry attribute containing the Scylla role name. std::string_view bind_name, ///< LDAP bind credentials. std::string_view bind_password, ///< LDAP bind credentials. + uint32_t permissions_update_interval_in_ms, + utils::observer permissions_update_interval_in_ms_observer, cql3::query_processor& qp, ///< Passed to standard_role_manager. ::service::raft_group0_client& rg0c, ///< Passed to standard_role_manager. ::service::migration_manager& mm, ///< Passed to standard_role_manager. diff --git a/test/ldap/role_manager_test.cc b/test/ldap/role_manager_test.cc index bd2f51ab87..61ded11bc1 100644 --- a/test/ldap/role_manager_test.cc +++ b/test/ldap/role_manager_test.cc @@ -284,6 +284,7 @@ auto make_ldap_manager(cql_test_env& env, sstring query_template = default_query }; return std::unique_ptr( new auth::ldap_role_manager(query_template, /*target_attr=*/"cn", manager_dn, manager_password, + env.db_config().permissions_update_interval_in_ms(), env.db_config().permissions_update_interval_in_ms.observe([] (const uint32_t& v) {}), env.local_qp(), env.get_raft_group0_client(), env.migration_manager().local(), env.auth_cache().local()), std::move(stop_role_manager)); }