]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: introduce and document new interfaces to the auth subsystem.
authorRadoslaw Zarzynski <rzarzynski@mirantis.com>
Tue, 18 Oct 2016 14:10:07 +0000 (16:10 +0200)
committerRadoslaw Zarzynski <rzarzynski@mirantis.com>
Fri, 24 Mar 2017 14:57:24 +0000 (15:57 +0100)
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
src/rgw/rgw_auth.h

index 52962b433fbacccccb3025b9975f6902a10d7fea..e20b1c62ef3dfa3994c5df7bb58e94105f4ae5ae 100644 (file)
@@ -8,6 +8,8 @@
 #include <functional>
 #include <ostream>
 #include <type_traits>
+#include <system_error>
+#include <utility>
 
 #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<std::string, int> 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<class IdentityApplier>,
+                             std::unique_ptr<class Completer>>;
+
+  /* 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<Completer> 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 */