From: Radoslaw Zarzynski Date: Tue, 18 Oct 2016 14:10:07 +0000 (+0200) Subject: rgw: introduce and document new interfaces to the auth subsystem. X-Git-Tag: v12.0.2~305^2~47 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=b560d98431c7fa107da83b8ef5c85ea016bbdc43;p=ceph.git rgw: introduce and document new interfaces to the auth subsystem. Signed-off-by: Radoslaw Zarzynski --- diff --git a/src/rgw/rgw_auth.h b/src/rgw/rgw_auth.h index 52962b433fba..e20b1c62ef3d 100644 --- a/src/rgw/rgw_auth.h +++ b/src/rgw/rgw_auth.h @@ -8,6 +8,8 @@ #include #include #include +#include +#include #include "rgw_common.h" #include "rgw_keystone.h" @@ -348,4 +350,173 @@ public: RGWAuthApplier::aplptr_t authenticate() const override; }; + +namespace rgw { +namespace auth { + +using Exception = std::system_error; + + +/* Load information about identity that will be used by RGWOp to authorize + * any operation that comes from an authenticated user. */ +class Identity { +public: + typedef std::map aclspec_t; + + virtual ~Identity() = default; + + /* Translate the ACL provided in @aclspec into concrete permission set that + * can be used during the authorization phase (RGWOp::verify_permission). + * On error throws rgw::auth::Exception storing the reason. + * + * NOTE: an implementation is responsible for giving the real semantic to + * the items in @aclspec. That is, their meaning may depend on particular + * applier that is being used. */ + virtual uint32_t get_perms_from_aclspec(const aclspec_t& aclspec) const = 0; + + /* Verify whether a given identity *can be treated as* an admin of rgw_user + * (account in Swift's terminology) specified in @uid. On error throws + * rgw::auth::Exception storing the reason. */ + virtual bool is_admin_of(const rgw_user& uid) const = 0; + + /* Verify whether a given identity *is* the owner of the rgw_user (account + * in the Swift's terminology) specified in @uid. On internal error throws + * rgw::auth::Exception storing the reason. */ + virtual bool is_owner_of(const rgw_user& uid) const = 0; + + /* Return the permission mask that is used to narrow down the set of + * operations allowed for a given identity. This method reflects the idea + * of subuser tied to RGWUserInfo. On error throws rgw::auth::Exception + * with the reason. */ + virtual uint32_t get_perm_mask() const = 0; + + virtual bool is_anonymous() const final { + /* If the identity owns the anonymous account (rgw_user), it's considered + * the anonymous identity. On error throws rgw::auth::Exception storing + * the reason. */ + return is_owner_of(rgw_user(RGW_USER_ANON_ID)); + } + + virtual void to_str(std::ostream& out) const = 0; +}; + +inline std::ostream& operator<<(std::ostream& out, + const rgw::auth::Identity& id) { + id.to_str(out); + return out; +} + + +/* Interface class for authentication backends (auth engines) in RadosGW. + * + * An engine is supposed only to authenticate (not authorize!) requests + * basing on their req_state and provide an upper layer with: + * - rgw::auth::IdentityApplier to commit all changes to the request state as + * well as to the RADOS store (creating an account, synchronizing + * user-related information with external databases and so on). + * - rgw::auth::Completer (optionally) to finish the authentication + * of the request. Typical use case is verifying message integrity + * in AWS Auth v4 and browser uploads (RGWPostObj). + * + * The authentication process consists two steps: + * - Engine::authenticate() supposed to be called before *initiating* + * any modifications to RADOS store that are related to an operation + * a client wants to perform (RGWOp::execute). + * - Completer::complete() supposed to be called, if completer has been + * returned, after the authenticate() step but before *committing* + * those modifications or sending a response (RGWOp::complete). + * + * An engine outlives both Applier and Completer. It's + * intended to live since RadosGW's initialization and handle multiple + * requests till a reconfiguration. + * + * Auth engine MUST NOT make any changes to req_state nor RADOS store. + * This is solely an Applier's responsibility! + * + * Separation between authentication and global state modification has + * been introduced because many auth engines are orthogonal to appliers + * and thus they can be decoupled. Additional motivation is to clearly + * distinguish all portions of code modifying data structures. */ +class Engine { +public: + virtual ~Engine() = default; + + using result_t = std::pair, + std::unique_ptr>; + + /* Get name of the auth engine. */ + virtual const char* get_name() const noexcept = 0; + + /* Throwing method for identity verification. When the check is positive + * an implementation should return Engine::result_t containing: + * - a non-null pointer to an object conforming the Applier interface. + * Otherwise, the authentication is treated as failed. + * - a (potentially null) pointer to an object conforming the Completer + * interface. + * + * On error throws rgw::auth::Exception containing the reason. */ + virtual result_t authenticate(const req_state* s) const = 0; +}; + + +/* Interface class for completing the two-step authentication process. + * Completer provides the second step - the complete() method that should + * be called after Engine::authenticate() but before *committing* results + * of an RGWOp (or sending a response in the case of non-mutating ops). + * + * The motivation driving the interface is to address those authentication + * schemas that require message integrity verification *without* in-memory + * data buffering. Typical examples are AWS Auth v4 and the auth mechanism + * of browser uploads facilities both in S3 and Swift APIs (see RGWPostObj). + * The workflow of request from the authentication point-of-view does look + * like following one: + * A. authenticate (Engine::authenticate), + * B. authorize (see RGWOp::verify_permissions), + * C. execute-prepare (init potential data modifications), + * D. authenticate-complete - (Completer::complete), + * E. execute-commit - commit the modifications from point C. */ +class Completer { +public: + typedef std::unique_ptr cmplptr_t; + + virtual ~Completer() = default; + + /* Complete the authentication process. Return boolean indicating whether + * the completion succeeded. On error throws rgw::auth::Exception storing + * the reason. */ + virtual bool complete() = 0; +}; + + +/* Abstract class for stacking sub-engines to expose them as a single + * Engine. It is responsible for ordering its sub-engines and managing + * fall-backs between them. Derivatee is supposed to encapsulate engine + * instances and add them using the add_engine() method in the order it + * wants to be tried during the call to authenticate(). */ +class Strategy : public Engine { +public: + /* Specifiers controlling what happens when an associated engine fails. + * The names and semantic has been borrowed from libpam. */ + enum class Control { + /* Failure of an engine injected with the REQUISITE specifier aborts + * the whole authentication process immediately. No other engine will + * be tried. */ + REQUISITE, + + /* Success of an engine injected with the SUFFICIENT specifier ends + * the whole authentication process successfully. However, failure + * doesn't abort it - there will be fall-back to following engine + * it the one that failed wasn't the last. */ + SUFFICIENT, + }; + + Engine::result_t authenticate(const req_state* s) const override final; + +protected: + void add_engine(Control ctrl_flag, const Engine& engine) noexcept; +}; + +} /* namespace auth */ +} /* namespace rgw */ + #endif /* CEPH_RGW_AUTH_H */