From: Radoslaw Zarzynski Date: Wed, 26 Oct 2016 16:09:07 +0000 (+0200) Subject: rgw: introduce the rgw::auth::RemoteApplier interface. X-Git-Tag: v12.0.2~305^2~44 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=41403c2092d22580926ab1b5a0660f25e78f3722;p=ceph-ci.git rgw: introduce the rgw::auth::RemoteApplier interface. Signed-off-by: Radoslaw Zarzynski --- diff --git a/src/rgw/rgw_auth.cc b/src/rgw/rgw_auth.cc index 75ba0c329ca..7f1458b10a2 100644 --- a/src/rgw/rgw_auth.cc +++ b/src/rgw/rgw_auth.cc @@ -556,3 +556,122 @@ rgw::auth::Strategy::add_engine(const Control ctrl_flag, { auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag)); } + + +/* rgw::auth::RemoteAuthApplier */ +uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const aclspec_t& aclspec) const +{ + uint32_t perm = 0; + + /* For backward compatibility with ACLOwner. */ + perm |= rgw_perms_from_aclspec_default_strategy(info.acct_user, + aclspec); + + /* We also need to cover cases where rgw_keystone_implicit_tenants + * was enabled. */ + if (info.acct_user.tenant.empty()) { + const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); + + perm |= rgw_perms_from_aclspec_default_strategy(tenanted_acct_user, + aclspec); + } + + /* Now it's a time for invoking additional strategy that was supplied by + * a specific auth engine. */ + if (extra_acl_strategy) { + perm |= extra_acl_strategy(aclspec); + } + + ldout(cct, 20) << "from ACL got perm=" << perm << dendl; + return perm; +} + +bool rgw::auth::RemoteApplier::is_admin_of(const rgw_user& uid) const +{ + return info.is_admin; +} + +bool rgw::auth::RemoteApplier::is_owner_of(const rgw_user& uid) const +{ + if (info.acct_user.tenant.empty()) { + const rgw_user tenanted_acct_user(info.acct_user.id, info.acct_user.id); + + if (tenanted_acct_user == uid) { + return true; + } + } + + return info.acct_user == uid; +} + +void rgw::auth::RemoteApplier::to_str(std::ostream& out) const +{ + out << "rgw::auth::RemoteApplier(acct_user=" << info.acct_user + << ", acct_name=" << info.acct_name + << ", perm_mask=" << info.perm_mask + << ", is_admin=" << info.is_admin << ")"; +} + +void rgw::auth::RemoteApplier::create_account(const rgw_user& acct_user, + RGWUserInfo& user_info) const /* out */ +{ + rgw_user new_acct_user = acct_user; + + if (info.acct_type) { + //ldap/keystone for s3 users + user_info.type = info.acct_type; + } + + /* Administrator may enforce creating new accounts within their own tenants. + * The config parameter name is kept due to legacy. */ + if (new_acct_user.tenant.empty() && g_conf->rgw_keystone_implicit_tenants) { + new_acct_user.tenant = new_acct_user.id; + } + + user_info.user_id = new_acct_user; + user_info.display_name = info.acct_name; + + int ret = rgw_store_user_info(store, user_info, nullptr, nullptr, + real_time(), true); + if (ret < 0) { + ldout(cct, 0) << "ERROR: failed to store new user info: user=" + << user_info.user_id << " ret=" << ret << dendl; + throw ret; + } +} + +/* TODO(rzarzynski): we need to handle display_name changes. */ +void rgw::auth::RemoteApplier::load_acct_info(RGWUserInfo& user_info) const /* out */ +{ + /* It's supposed that RGWRemoteAuthApplier tries to load account info + * that belongs to the authenticated identity. Another policy may be + * applied by using a RGWThirdPartyAccountAuthApplier decorator. */ + const rgw_user& acct_user = info.acct_user; + + /* Normally, empty "tenant" field of acct_user means the authenticated + * identity has the legacy, global tenant. However, due to inclusion + * of multi-tenancy, we got some special compatibility kludge for remote + * backends like Keystone. + * If the global tenant is the requested one, we try the same tenant as + * the user name first. If that RGWUserInfo exists, we use it. This way, + * migrated OpenStack users can get their namespaced containers and nobody's + * the wiser. + * If that fails, we look up in the requested (possibly empty) tenant. + * If that fails too, we create the account within the global or separated + * namespace depending on rgw_keystone_implicit_tenants. */ + if (acct_user.tenant.empty()) { + const rgw_user tenanted_uid(acct_user.id, acct_user.id); + + if (rgw_get_user_info_by_uid(store, tenanted_uid, user_info) >= 0) { + /* Succeeded. */ + return; + } + } + + if (rgw_get_user_info_by_uid(store, acct_user, user_info) < 0) { + ldout(cct, 0) << "NOTICE: couldn't map swift user " << acct_user << dendl; + create_account(acct_user, user_info); + } + + /* Succeeded if we are here (create_account() hasn't throwed). */ +} diff --git a/src/rgw/rgw_auth.h b/src/rgw/rgw_auth.h index be35fbaa03e..1eece8b80c1 100644 --- a/src/rgw/rgw_auth.h +++ b/src/rgw/rgw_auth.h @@ -551,6 +551,92 @@ protected: void add_engine(Control ctrl_flag, const Engine& engine) noexcept; }; + +/* rgw::auth::RemoteApplier targets those authentication engines which don't + * need to ask the RADOS store while performing the auth process. Instead, + * they obtain credentials from an external source like Keystone or LDAP. + * + * As the authenticated user may not have an account yet, RGWRemoteAuthApplier + * must be able to create it basing on data passed by an auth engine. Those + * data will be used to fill RGWUserInfo structure. */ +class RemoteApplier : public IdentityApplier { +public: + class AuthInfo { + friend class RemoteApplier; + protected: + const rgw_user acct_user; + const std::string acct_name; + const uint32_t perm_mask; + const bool is_admin; + const uint32_t acct_type; + + public: + enum class acct_privilege_t { + IS_ADMIN_ACCT, + IS_PLAIN_ACCT + }; + + AuthInfo(const rgw_user& acct_user, + const std::string& acct_name, + const uint32_t perm_mask, + const acct_privilege_t level, + const uint32_t acct_type=TYPE_NONE) + : acct_user(acct_user), + acct_name(acct_name), + perm_mask(perm_mask), + is_admin(acct_privilege_t::IS_ADMIN_ACCT == level), + acct_type(acct_type) { + } + }; + + using aclspec_t = RGWIdentityApplier::aclspec_t; + typedef std::function acl_strategy_t; + +protected: + CephContext* const cct; + + /* Read-write is intensional here due to RGWUserInfo creation process. */ + RGWRados* const store; + + /* Supplemental strategy for extracting permissions from ACLs. Its results + * will be combined (ORed) with a default strategy that is responsible for + * handling backward compatibility. */ + const acl_strategy_t extra_acl_strategy; + + const AuthInfo info; + + virtual void create_account(const rgw_user& acct_user, + RGWUserInfo& user_info) const; /* out */ + +public: + RemoteApplier(CephContext* const cct, + RGWRados* const store, + acl_strategy_t&& extra_acl_strategy, + const AuthInfo& info) + : cct(cct), + store(store), + extra_acl_strategy(std::move(extra_acl_strategy)), + info(info) { + } + + uint32_t get_perms_from_aclspec(const aclspec_t& aclspec) const override; + bool is_admin_of(const rgw_user& uid) const override; + bool is_owner_of(const rgw_user& uid) const override; + uint32_t get_perm_mask() const override { return info.perm_mask; } + void to_str(std::ostream& out) const override; + void load_acct_info(RGWUserInfo& user_info) const override; /* out */ + + struct Factory { + virtual ~Factory() {} + /* Providing r-value reference here is required intensionally. Callee is + * thus disallowed to handle std::function in a way that could inhibit + * the move behaviour (like forgetting about std::moving a l-value). */ + virtual aplptr_t create_apl_remote(CephContext * const cct, + acl_strategy_t&& extra_acl_strategy, + const AuthInfo info) const = 0; + }; +}; + } /* namespace auth */ } /* namespace rgw */