]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: implement descriptive authentication failure reasons.
authorRadoslaw Zarzynski <rzarzynski@mirantis.com>
Wed, 18 Jan 2017 19:08:08 +0000 (20:08 +0100)
committerRadoslaw Zarzynski <rzarzynski@mirantis.com>
Fri, 24 Mar 2017 15:55:42 +0000 (16:55 +0100)
Signed-off-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
src/rgw/rgw_auth.cc
src/rgw/rgw_auth.h
src/rgw/rgw_auth_keystone.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_rest_swift.cc
src/rgw/rgw_swift_auth.cc

index 8768a53433041900f7428caa57b2c2d71d7b6e87..20087519bd20e511134f4645406b6db3fff00598 100644 (file)
@@ -104,47 +104,123 @@ static inline const std::string make_spec_item(const std::string& tenant,
 }
 
 
+static inline std::pair<bool, rgw::auth::Engine::result_t>
+strategy_handle_rejected(rgw::auth::Engine::result_t&& engine_result,
+                         const rgw::auth::Strategy::Control policy,
+                         rgw::auth::Engine::result_t&& strategy_result)
+{
+  using Control = rgw::auth::Strategy::Control;
+  switch (policy) {
+    case Control::REQUISITE:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(engine_result));
+
+    case Control::SUFFICIENT:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(engine_result));
+
+    case Control::FALLBACK:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(strategy_result));
+
+    default:
+      /* Huh, memory corruption? */
+      abort();
+  }
+}
+
+static inline std::pair<bool, rgw::auth::Engine::result_t>
+strategy_handle_denied(rgw::auth::Engine::result_t&& engine_result,
+                       const rgw::auth::Strategy::Control policy,
+                       rgw::auth::Engine::result_t&& strategy_result)
+{
+  using Control = rgw::auth::Strategy::Control;
+  switch (policy) {
+    case Control::REQUISITE:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(engine_result));
+
+    case Control::SUFFICIENT:
+      /* Just try next. */
+      return std::make_pair(true, std::move(engine_result));
+
+    case Control::FALLBACK:
+      return std::make_pair(true, std::move(strategy_result));
+
+    default:
+      /* Huh, memory corruption? */
+      abort();
+  }
+}
+
+static inline std::pair<bool, rgw::auth::Engine::result_t>
+strategy_handle_granted(rgw::auth::Engine::result_t&& engine_result,
+                        const rgw::auth::Strategy::Control policy,
+                        rgw::auth::Engine::result_t&& strategy_result)
+{
+  using Control = rgw::auth::Strategy::Control;
+  switch (policy) {
+    case Control::REQUISITE:
+      /* Try next. */
+      return std::make_pair(true, std::move(engine_result));
+
+    case Control::SUFFICIENT:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(engine_result));
+
+    case Control::FALLBACK:
+      /* Don't try next. */
+      return std::make_pair(false, std::move(engine_result));
+
+    default:
+      /* Huh, memory corruption? */
+      abort();
+  }
+}
+
 rgw::auth::Engine::result_t
 rgw::auth::Strategy::authenticate(const req_state* const s) const
 {
-  int previous_error = 0;
+  result_t strategy_result = result_t::deny();
+
   for (const stack_item_t& kv : auth_stack) {
     const rgw::auth::Engine& engine = kv.first;
     const auto& policy = kv.second;
 
-    rgw::auth::Engine::result_t res;
+    result_t engine_result = result_t::deny();
     try {
-      res = engine.authenticate(s);
+      engine_result = engine.authenticate(s);
     } catch (const int err) {
-      previous_error = err;
+      engine_result = result_t::deny(err);
     }
 
-    const auto& applier = res.first;
-    if (! applier) {
-      /* The current auth engine denied authenticate the request returning
-       * a null rgw::auth::Applier. As it has been included into strategy
-       * as an obligatory one, we quite immediately. */
-      switch (policy) {
-        case Control::REQUISITE:
-          goto auth_fail;
-        case Control::SUFFICIENT:
-          /* Just try next. */
-          continue;
-        case Control::FALLBACK:
-          throw previous_error;
-        default:
-          /* Huh, memory corruption? */
-          abort();
-      }
-    } else {
-      /* Success. */
-      return std::move(res);
+    bool try_next = true;
+    switch (engine_result.get_status()) {
+      case result_t::Status::REJECTED:
+        std::tie(try_next, strategy_result) = \
+          strategy_handle_rejected(std::move(engine_result), policy,
+                                   std::move(strategy_result));
+          break;
+      case result_t::Status::DENIED:
+        std::tie(try_next, strategy_result) = \
+          strategy_handle_denied(std::move(engine_result), policy,
+                                 std::move(strategy_result));
+          break;
+      case result_t::Status::GRANTED:
+        std::tie(try_next, strategy_result) = \
+          strategy_handle_granted(std::move(engine_result), policy,
+                                  std::move(strategy_result));
+          break;
+      default:
+        abort();
+    }
+
+    if (! try_next) {
+      break;
     }
   }
 
-auth_fail:
-  /* Returning nullptr as the rgw::auth::Applier means access denied. */
-  return Engine::result_t(nullptr, nullptr);
+  return strategy_result;
 }
 
 void
@@ -332,7 +408,7 @@ rgw::auth::Engine::result_t
 rgw::auth::AnonymousEngine::authenticate(const req_state* const s) const
 {
   if (! is_applicable()) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   } else {
     RGWUserInfo user_info;
     rgw_get_anon_user(user_info);
@@ -340,6 +416,6 @@ rgw::auth::AnonymousEngine::authenticate(const req_state* const s) const
     // FIXME: over 80 columns
     auto apl = apl_factory->create_apl_local(cct, s, user_info,
                                              rgw::auth::LocalApplier::NO_SUBUSER);
-    return std::make_pair(std::move(apl), nullptr);
+    return result_t::grant(std::move(apl));
   }
 }
index ca7ce6fafd4a09dab4d4a4da9cb990508e0e3c40..458f0ccae63a90e88f9a54300c1ef0a3ce9dab61 100644 (file)
@@ -104,10 +104,40 @@ public:
 };
 
 
+/* 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;
+};
+
+
 /* 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:
+ * basing on their req_state and - if access has been granted - 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).
@@ -115,17 +145,19 @@ public:
  *    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*
+ * Both of them are supposed to be wrapped in Engine::AuthResult.
+ *
+ * The authentication process consists of two steps:
+ *  - Engine::authenticate() which should 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.
+ * 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!
@@ -138,8 +170,87 @@ class Engine {
 public:
   virtual ~Engine() = default;
 
-  using result_t = std::pair<std::unique_ptr<class IdentityApplier>,
-                             std::unique_ptr<class Completer>>;
+  class AuthResult {
+    struct rejection_mark_t {};
+    bool is_rejected = false;
+    int reason = 0;
+
+    std::pair<IdentityApplier::aplptr_t, Completer::cmplptr_t> result_pair;
+
+    AuthResult(const int reason)
+      : reason(reason) {
+    }
+
+    AuthResult(rejection_mark_t&&, const int reason)
+      : is_rejected(true),
+        reason(reason) {
+    }
+
+    /* Allow only the reasonable combintations - returning just Completer
+     * without accompanying IdentityApplier is strictly prohibited! */
+    AuthResult(IdentityApplier::aplptr_t&& applier)
+      : result_pair(std::move(applier), nullptr) {
+    }
+
+    AuthResult(IdentityApplier::aplptr_t&& applier,
+               Completer::cmplptr_t&& completer)
+      : result_pair(std::move(applier), std::move(completer)) {
+    }
+
+  public:
+    enum class Status {
+      /* Engine doesn't grant the access but also doesn't reject it. */
+      DENIED,
+
+      /* Engine successfully authenticated requester. */
+      GRANTED,
+
+      /* Engine strictly indicates that a request should be rejected
+       * without trying any further engine. */
+      REJECTED
+    };
+
+    Status get_status() const {
+      if (is_rejected) {
+        return Status::REJECTED;
+      } else if (! result_pair.first) {
+        return Status::DENIED;
+      } else {
+        return Status::GRANTED;
+      }
+    }
+
+    int get_reason() const {
+      return reason;
+    }
+
+    IdentityApplier::aplptr_t get_applier() {
+      return std::move(result_pair.first);
+    }
+
+    Completer::cmplptr_t get_completer() {
+      return std::move(result_pair.second);
+    }
+
+    static AuthResult reject(const int reason = -EACCES) {
+      return AuthResult(rejection_mark_t(), reason);
+    }
+
+    static AuthResult deny(const int reason = -EACCES) {
+      return AuthResult(reason);
+    }
+
+    static AuthResult grant(IdentityApplier::aplptr_t&& applier) {
+      return AuthResult(std::move(applier));
+    }
+
+    static AuthResult grant(IdentityApplier::aplptr_t&& applier,
+                            Completer::cmplptr_t&& completer) {
+      return AuthResult(std::move(applier), std::move(completer));
+    }
+  };
+
+  using result_t = AuthResult;
 
   /* Get name of the auth engine. */
   virtual const char* get_name() const noexcept = 0;
@@ -156,35 +267,6 @@ public:
 };
 
 
-/* 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;
-};
-
-
 /* Interface for extracting a token basing from data carried by req_state. */
 class TokenExtractor {
 public:
@@ -204,14 +286,14 @@ public:
    * The names and semantic has been borrowed mostly 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. */
+     * the strategy's 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. */
+     * strategy's authentication process successfully. However, denying
+     * doesn't abort it -- there will be fall-back to following engine
+     * if the one that failed wasn't the last one. */
     SUFFICIENT,
 
     /* Like SUFFICIENT with the exception that on failure the reason code
index b333be9f7e2cdf1dc1ee77a9201a60bf8a5091fd..1c6a9cc41ae0a00789011201f67f664ba9d471f3 100644 (file)
@@ -234,7 +234,7 @@ TokenEngine::authenticate(const std::string& token,
                    << dendl;
     auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(t),
                                               get_creds_info(t, roles.admin));
-    return std::make_pair(std::move(apl), nullptr);
+    return result_t::grant(std::move(apl));
   }
 
   /* Retrieve token. */
@@ -255,7 +255,7 @@ TokenEngine::authenticate(const std::string& token,
     ldout(cct, 0) << "got expired token: " << t.get_project_name()
                   << ":" << t.get_user_name()
                   << " expired: " << t.get_expires() << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   /* Check for necessary roles. */
@@ -267,14 +267,14 @@ TokenEngine::authenticate(const std::string& token,
       token_cache.add(token_id, t);
       auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(t),
                                             get_creds_info(t, roles.admin));
-      return std::make_pair(std::move(apl), nullptr);
+      return result_t::grant(std::move(apl));
     }
   }
 
   ldout(cct, 0) << "user does not hold a matching role; required roles: "
                 << g_conf->rgw_keystone_accepted_roles << dendl;
 
-  return std::make_pair(nullptr, nullptr);
+  return result_t::deny();
 }
 
 
@@ -344,7 +344,7 @@ EC2Engine::get_from_keystone(const std::string& access_key_id,
   if (ret < 0) {
     ldout(cct, 2) << "s3 keystone: token validation ERROR: "
                   << token_body_bl.c_str() << dendl;
-    throw -EPERM;
+    throw ret;
   }
 
   /* if the supplied signature is wrong, we will get 401 from Keystone */
@@ -429,7 +429,7 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(const std::string& access_ke
     ldout(cct, 0) << "got expired token: " << t.get_project_name()
                   << ":" << t.get_user_name()
                   << " expired: " << t.get_expires() << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   /* check if we have a valid role */
@@ -445,7 +445,7 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(const std::string& access_ke
     ldout(cct, 5) << "s3 keystone: user does not hold a matching role;"
                      " required roles: "
                   << cct->_conf->rgw_keystone_accepted_roles << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   } else {
     /* everything seems fine, continue with this user */
     ldout(cct, 5) << "s3 keystone: validated token: " << t.get_project_name()
@@ -454,7 +454,7 @@ rgw::auth::Engine::result_t EC2Engine::authenticate(const std::string& access_ke
 
     auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(t),
                                               get_creds_info(t, accepted_roles.admin));
-    return std::make_pair(std::move(apl), nullptr);
+    return result_t::grant(std::move(apl));
   }
 }
 
index ea4d1f34b0c2df0fb7eb57cee2623a787108bc3b..63bd0b99cdda7f6d22f9f17ff148402eee64d234 100644 (file)
@@ -1833,12 +1833,13 @@ int RGWPostObj_ObjStore_S3::get_policy()
     const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store, &extr);
     try {
       auto result = strategy.authenticate(s);
-      auto& applier = result.first;
-      if (! applier) {
+      if (result.get_status() != decltype(result)::Status::GRANTED) {
         return -EACCES;
       }
 
       try {
+        auto applier = result.get_applier();
+
         applier->load_acct_info(*s->user);
         s->perm_mask = applier->get_perm_mask();
         applier->modify_request_state(s);
@@ -3898,11 +3899,14 @@ int RGW_Auth_S3::authorize_v2(RGWRados *store, struct req_state *s)
   static const rgw::auth::s3::AWSv2AuthStrategy strategy(g_ceph_context, store);
   try {
     auto result = strategy.authenticate(s);
-    auto& applier = result.first;
-    if (! applier) {
-      return -EPERM;
+    if (result.get_status() != decltype(result)::Status::GRANTED) {
+      ldout(s->cct, 5) << "Failed the S3 auth strategy, reason="
+                       << result.get_reason() << dendl;
+      return result.get_reason();
     }
     try {
+      auto applier = result.get_applier();
+
       applier->load_acct_info(*s->user);
       s->perm_mask = applier->get_perm_mask();
       applier->modify_request_state(s);
@@ -4348,7 +4352,7 @@ rgw::auth::s3::LDAPEngine::authenticate(const std::string& access_key_id,
   }
 
   if (! base64_token.valid()) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   //TODO: Uncomment, when we have a migration plan in place.
@@ -4364,12 +4368,12 @@ rgw::auth::s3::LDAPEngine::authenticate(const std::string& access_key_id,
   }*/
 
   if (ldh->auth(base64_token.id, base64_token.key) != 0) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   auto apl = apl_factory->create_apl_remote(cct, s, get_acl_strategy(),
                                             get_creds_info(base64_token));
-  return std::make_pair(std::move(apl), nullptr);
+  return result_t::grant(std::move(apl));
 }
 
 
@@ -4380,17 +4384,12 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(const std::string& access_key
                                                    const std::string& string_to_sign,
                                                    const req_state* const s) const
 {
-  if (access_key_id.empty() || signature.empty()) {
-    ldout(cct, 5) << "access_key_id or signature is empty" << dendl;
-    throw -EINVAL;
-  }
-
   /* get the user info */
   RGWUserInfo user_info;
   if (rgw_get_user_info_by_access_key(store, access_key_id, user_info) < 0) {
       ldout(cct, 5) << "error reading user info, uid=" << access_key_id
               << " can't authenticate" << dendl;
-      return std::make_pair(nullptr, nullptr);
+      return result_t::deny(-ERR_INVALID_ACCESS_KEY);
   }
   //TODO: Uncomment, when we have a migration plan in place.
   /*else {
@@ -4404,14 +4403,14 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(const std::string& access_key
   const auto iter = user_info.access_keys.find(access_key_id);
   if (iter == std::end(user_info.access_keys)) {
     ldout(cct, 0) << "ERROR: access key not encoded in user info" << dendl;
-    throw -EPERM;
+    return result_t::deny(-EPERM);
   }
   const RGWAccessKey& k = iter->second;
 
   std::string digest;
   int ret = rgw_get_s3_header_digest(string_to_sign, k.key, digest);
   if (ret < 0) {
-    throw -EPERM;
+    return result_t::deny(-EPERM);
   }
 
   ldout(cct, 15) << "string_to_sign=" << string_to_sign << dendl;
@@ -4420,9 +4419,9 @@ rgw::auth::s3::LocalVersion2ndEngine::authenticate(const std::string& access_key
   ldout(cct, 15) << "compare=" << signature.compare(digest) << dendl;
 
   if (signature != digest) {
-    throw -ERR_SIGNATURE_NO_MATCH;
+    return result_t::deny(-ERR_SIGNATURE_NO_MATCH);
   }
 
   auto apl = apl_factory->create_apl_local(cct, s, user_info, k.subuser);
-  return std::make_pair(std::move(apl), nullptr);
+  return result_t::grant(std::move(apl));
 }
index 7450dfe3d34ea870cae1c371179443912558caa3..6ca8c899a6d3b78258b65247c1c4bbc6afee794b 100644 (file)
@@ -659,7 +659,7 @@ public:
       extractor.get_auth_data(s);
 
     if (access_key_id.empty() || signature.empty()) {
-      return std::make_pair(nullptr, nullptr);
+      return result_t::deny(-EINVAL);
     } else {
       return authenticate(access_key_id, signature, string_to_sign, s);
     }
index e1a116999fdbbd8cc754eb66d5f4943d7da70854..9f06fa461877c54811fb8ead73a715f8081f00b3 100644 (file)
@@ -2033,12 +2033,10 @@ RGWOp *RGWHandler_REST_Obj_SWIFT::op_options()
 
 int RGWHandler_REST_SWIFT::authorize()
 {
-  rgw::auth::IdentityApplier::aplptr_t applier;
-  rgw::auth::Completer::cmplptr_t completer;
-  std::tie(applier, completer) = auth_strategy.authenticate(s);
-
   try {
-    if (! applier) {
+    auto result = auth_strategy.authenticate(s);
+
+    if (result.get_status() != decltype(result)::Status::GRANTED) {
       /* Access denied is acknowledged by returning a std::unique_ptr with
        * nullptr inside. */
       ldout(s->cct, 5) << "auth engine refused to authenicate" << dendl;
@@ -2046,6 +2044,8 @@ int RGWHandler_REST_SWIFT::authorize()
     }
 
     try {
+      rgw::auth::IdentityApplier::aplptr_t applier = result.get_applier();
+
       /* Account used by a given RGWOp is decoupled from identity employed
        * in the authorization phase (RGWOp::verify_permissions). */
       applier->load_acct_info(*s->user);
@@ -2057,7 +2057,7 @@ int RGWHandler_REST_SWIFT::authorize()
       applier->modify_request_state(s);
 
       s->auth.identity = std::move(applier);
-      s->auth.completer = std::move(completer);
+      s->auth.completer = std::move(result.get_completer());
 
       return 0;
     } catch (int err) {
index 005d839045abfd2524d7f31f2ebc8cb3889a2711..cc26db4fcce50526f3a2f725d1931a1716c6af6a 100644 (file)
@@ -194,14 +194,14 @@ TempURLEngine::result_t
 TempURLEngine::authenticate(const req_state* const s) const
 {
   if (! is_applicable(s)) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   const string& temp_url_sig = s->info.args.get("temp_url_sig");
   const string& temp_url_expires = s->info.args.get("temp_url_expires");
 
   if (temp_url_sig.empty() || temp_url_expires.empty()) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   RGWUserInfo owner_info;
@@ -209,17 +209,17 @@ TempURLEngine::authenticate(const req_state* const s) const
     get_owner_info(s, owner_info);
   } catch (...) {
     ldout(cct, 5) << "cannot get user_info of account's owner" << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::reject();
   }
 
   if (owner_info.temp_url_keys.empty()) {
     ldout(cct, 5) << "user does not have temp url key set, aborting" << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::reject();
   }
 
   if (is_expired(temp_url_expires)) {
     ldout(cct, 5) << "temp url link expired" << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::reject();
   }
 
   /* We need to verify two paths because of compliance with Swift, Tempest
@@ -270,7 +270,7 @@ TempURLEngine::authenticate(const req_state* const s) const
 
         if (sig_helper.is_equal_to(temp_url_sig)) {
           auto apl = apl_factory->create_apl_turl(cct, s, owner_info);
-          return std::make_pair(std::move(apl), nullptr);
+          return result_t::grant(std::move(apl));
         } else {
           ldout(s->cct,  5) << "temp url signature mismatch: " << local_sig
                             << " != " << temp_url_sig  << dendl;
@@ -279,7 +279,7 @@ TempURLEngine::authenticate(const req_state* const s) const
     }
   }
 
-  return std::make_pair(nullptr, nullptr);
+  return result_t::reject();
 }
 
 
@@ -300,7 +300,7 @@ ExternalTokenEngine::authenticate(const std::string& token,
                                   const req_state* const s) const
 {
   if (! is_applicable(token)) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   std::string auth_url = g_conf->rgw_swift_auth_url;
@@ -328,17 +328,17 @@ ExternalTokenEngine::authenticate(const std::string& token,
                 ",", swift_groups);
 
     if (0 == swift_groups.size()) {
-      return std::make_pair(nullptr, nullptr);
+      return result_t::deny();
     } else {
       swift_user = std::move(swift_groups[0]);
     }
   } catch (std::out_of_range) {
     /* The X-Auth-Groups header isn't present in the response. */
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   if (swift_user.empty()) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   ldout(cct, 10) << "swift user=" << swift_user << dendl;
@@ -352,7 +352,7 @@ ExternalTokenEngine::authenticate(const std::string& token,
 
   auto apl = apl_factory->create_apl_local(cct, s, tmp_uinfo,
                                            extract_swift_subuser(swift_user));
-  return std::make_pair(std::move(apl), nullptr);
+  return result_t::grant(std::move(apl));
 }
 
 static int build_token(const string& swift_user,
@@ -416,7 +416,7 @@ SignedTokenEngine::authenticate(const std::string& token,
                                 const req_state* const s) const
 {
   if (! is_applicable(token)) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   /* Effective token string is the part after the prefix. */
@@ -458,7 +458,7 @@ SignedTokenEngine::authenticate(const std::string& token,
     ldout(cct, 0) << "NOTICE: old timed out token was used now=" << now
                  << " token.expiration=" << expiration
                   << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   RGWUserInfo user_info;
@@ -471,7 +471,7 @@ SignedTokenEngine::authenticate(const std::string& token,
 
   const auto siter = user_info.swift_keys.find(swift_user);
   if (siter == std::end(user_info.swift_keys)) {
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   const auto swift_key = siter->second;
@@ -487,7 +487,7 @@ SignedTokenEngine::authenticate(const std::string& token,
                   << " tok_bl.length()=" << tok_bl.length()
                  << " local_tok_bl.length()=" << local_tok_bl.length()
                   << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   if (memcmp(local_tok_bl.c_str(), tok_bl.c_str(),
@@ -498,12 +498,12 @@ SignedTokenEngine::authenticate(const std::string& token,
                local_tok_bl.length(), buf);
 
     ldout(cct, 0) << "NOTICE: tokens mismatch tok=" << buf << dendl;
-    return std::make_pair(nullptr, nullptr);
+    return result_t::deny();
   }
 
   auto apl = apl_factory->create_apl_local(cct, s, user_info,
                                            extract_swift_subuser(swift_user));
-  return std::make_pair(std::move(apl), nullptr);
+  return result_t::grant(std::move(apl));
 }
 
 } /* namespace swift */