// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab ft=cpp #pragma once #include #include #include #include #include #include "rgw_common.h" #include "rgw_http_client.h" #include "common/ceph_mutex.h" #include "global/global_init.h" bool rgw_is_pki_token(const std::string& token); void rgw_get_token_id(const std::string& token, std::string& token_id); static inline std::string rgw_get_token_id(const std::string& token) { std::string token_id; rgw_get_token_id(token, token_id); return token_id; } namespace rgw { namespace keystone { enum class ApiVersion { VER_2, VER_3 }; class Config { protected: Config() = default; virtual ~Config() = default; public: virtual std::string get_endpoint_url() const noexcept = 0; virtual ApiVersion get_api_version() const noexcept = 0; virtual std::string get_admin_token() const noexcept = 0; virtual std::string_view get_admin_user() const noexcept = 0; virtual std::string get_admin_password() const noexcept = 0; virtual std::string_view get_admin_tenant() const noexcept = 0; virtual std::string_view get_admin_project() const noexcept = 0; virtual std::string_view get_admin_domain() const noexcept = 0; }; class CephCtxConfig : public Config { protected: CephCtxConfig() = default; virtual ~CephCtxConfig() = default; const static std::string empty; public: static CephCtxConfig& get_instance() { static CephCtxConfig instance; return instance; } std::string get_endpoint_url() const noexcept override; ApiVersion get_api_version() const noexcept override; std::string get_admin_token() const noexcept override; std::string_view get_admin_user() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_user; } std::string get_admin_password() const noexcept override; std::string_view get_admin_tenant() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_tenant; } std::string_view get_admin_project() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_project; } std::string_view get_admin_domain() const noexcept override { return g_ceph_context->_conf->rgw_keystone_admin_domain; } }; class TokenEnvelope; class TokenCache; class Service { public: class RGWKeystoneHTTPTransceiver : public RGWHTTPTransceiver { public: RGWKeystoneHTTPTransceiver(CephContext * const cct, const std::string& method, const std::string& url, bufferlist * const token_body_bl) : RGWHTTPTransceiver(cct, method, url, token_body_bl, cct->_conf->rgw_keystone_verify_ssl, { "X-Subject-Token" }) { } const header_value_t& get_subject_token() const { try { return get_header_value("X-Subject-Token"); } catch (std::out_of_range&) { static header_value_t empty_val; return empty_val; } } }; typedef RGWKeystoneHTTPTransceiver RGWValidateKeystoneToken; typedef RGWKeystoneHTTPTransceiver RGWGetKeystoneAdminToken; static int get_admin_token(const DoutPrefixProvider *dpp, CephContext* const cct, TokenCache& token_cache, const Config& config, std::string& token); static int issue_admin_token_request(const DoutPrefixProvider *dpp, CephContext* const cct, const Config& config, TokenEnvelope& token); static int get_keystone_barbican_token(const DoutPrefixProvider *dpp, CephContext * const cct, std::string& token); }; class TokenEnvelope { public: class Domain { public: std::string id; std::string name; void decode_json(JSONObj *obj); }; class Project { public: Domain domain; std::string id; std::string name; void decode_json(JSONObj *obj); }; class Token { public: Token() : expires(0) { } std::string id; time_t expires; Project tenant_v2; void decode_json(JSONObj *obj); }; class Role { public: std::string id; std::string name; void decode_json(JSONObj *obj); }; class User { public: std::string id; std::string name; Domain domain; std::list roles_v2; void decode_json(JSONObj *obj); }; Token token; Project project; User user; std::list roles; void decode_v3(JSONObj* obj); void decode_v2(JSONObj* obj); public: /* We really need the default ctor because of the internals of TokenCache. */ TokenEnvelope() = default; void set_expires(time_t expires) { token.expires = expires; } time_t get_expires() const { return token.expires; } const std::string& get_domain_id() const {return project.domain.id;}; const std::string& get_domain_name() const {return project.domain.name;}; const std::string& get_project_id() const {return project.id;}; const std::string& get_project_name() const {return project.name;}; const std::string& get_user_id() const {return user.id;}; const std::string& get_user_name() const {return user.name;}; bool has_role(const std::string& r) const; bool expired() const { const uint64_t now = ceph_clock_now().sec(); return std::cmp_greater_equal(now, get_expires()); } int parse(const DoutPrefixProvider *dpp, CephContext* cct, const std::string& token_str, ceph::buffer::list& bl /* in */, ApiVersion version); }; class TokenCache { struct token_entry { TokenEnvelope token; std::list::iterator lru_iter; }; std::atomic down_flag = { false }; const boost::intrusive_ptr cct; std::string admin_token_id; std::string barbican_token_id; std::map tokens; std::map service_tokens; std::list tokens_lru; std::list service_tokens_lru; ceph::mutex lock = ceph::make_mutex("rgw::keystone::TokenCache"); const size_t max; explicit TokenCache(const rgw::keystone::Config& config) : cct(g_ceph_context), max(cct->_conf->rgw_keystone_token_cache_size) { } ~TokenCache() { down_flag = true; } public: TokenCache(const TokenCache&) = delete; void operator=(const TokenCache&) = delete; template static TokenCache& get_instance() { static_assert(std::is_base_of::value, "ConfigT must be a subclass of rgw::keystone::Config"); /* In C++11 this is thread safe. */ static TokenCache instance(ConfigT::get_instance()); return instance; } bool find(const std::string& token_id, TokenEnvelope& token); bool find_service(const std::string& token_id, TokenEnvelope& token); boost::optional find(const std::string& token_id) { TokenEnvelope token_envlp; if (find(token_id, token_envlp)) { return token_envlp; } return boost::none; } boost::optional find_service(const std::string& token_id) { TokenEnvelope token_envlp; if (find_service(token_id, token_envlp)) { return token_envlp; } return boost::none; } bool find_admin(TokenEnvelope& token); bool find_barbican(TokenEnvelope& token); void add(const std::string& token_id, const TokenEnvelope& token); void add_service(const std::string& token_id, const TokenEnvelope& token); void add_admin(const TokenEnvelope& token); void add_barbican(const TokenEnvelope& token); void invalidate(const DoutPrefixProvider *dpp, const std::string& token_id); bool going_down() const; private: void add_locked(const std::string& token_id, const TokenEnvelope& token, std::map& tokens, std::list& tokens_lru); bool find_locked(const std::string& token_id, TokenEnvelope& token, std::map& tokens, std::list& tokens_lru); }; class AdminTokenRequest { public: virtual ~AdminTokenRequest() = default; virtual void dump(Formatter* f) const = 0; }; class AdminTokenRequestVer2 : public AdminTokenRequest { const Config& conf; public: explicit AdminTokenRequestVer2(const Config& conf) : conf(conf) { } void dump(Formatter *f) const override; }; class AdminTokenRequestVer3 : public AdminTokenRequest { const Config& conf; public: explicit AdminTokenRequestVer3(const Config& conf) : conf(conf) { } void dump(Formatter *f) const override; }; class BarbicanTokenRequestVer2 : public AdminTokenRequest { CephContext *cct; public: explicit BarbicanTokenRequestVer2(CephContext * const _cct) : cct(_cct) { } void dump(Formatter *f) const override; }; class BarbicanTokenRequestVer3 : public AdminTokenRequest { CephContext *cct; public: explicit BarbicanTokenRequestVer3(CephContext * const _cct) : cct(_cct) { } void dump(Formatter *f) const override; }; }; /* namespace keystone */ }; /* namespace rgw */