]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Initial commit for AssumeRoleWithWebIdentity. 26002/head
authorPritha Srivastava <prsrivas@redhat.com>
Mon, 31 Dec 2018 06:12:49 +0000 (11:42 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Tue, 29 Jan 2019 08:51:12 +0000 (14:21 +0530)
Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
23 files changed:
src/common/legacy_config_opts.h
src/common/options.cc
src/rgw/rgw_auth.cc
src/rgw/rgw_auth.h
src/rgw/rgw_auth_registry.h
src/rgw/rgw_auth_s3.cc
src/rgw/rgw_auth_s3.h
src/rgw/rgw_basic_types.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_iam_policy.cc
src/rgw/rgw_iam_policy.h
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rest.cc
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_rest_sts.cc
src/rgw/rgw_rest_sts.h
src/rgw/rgw_sts.cc
src/rgw/rgw_sts.h
src/rgw/rgw_web_idp.h [new file with mode: 0644]
src/test/rgw/test_rgw_iam_policy.cc

index a8fd2ac4eaf454dff6e6f29e35008e68d81ec2fd..e16fc0a23c333eb8ca7e4700a458618eeb4b1ce9 100644 (file)
@@ -1548,3 +1548,6 @@ OPTION(rgw_sts_key, OPT_STR)
 OPTION(rgw_s3_auth_use_sts, OPT_BOOL)  // should we try to use sts for s3?
 OPTION(rgw_sts_max_session_duration, OPT_U64) // Max duration in seconds for which the session token is valid.
 OPTION(fake_statfs_for_testing, OPT_INT) // Set a value for kb and compute kb_used from total of num_bytes
+OPTION(rgw_sts_token_introspection_url, OPT_STR)  // url for introspecting web tokens
+OPTION(rgw_sts_client_id, OPT_STR) // Client Id
+OPTION(rgw_sts_client_secret, OPT_STR) // Client Secret
index 71bb8ff1c6b7f4d5eb39fb261c1d9c143d5d98a8..7f16aa5dbae03da496d0e9cfb925e7386853c50c 100644 (file)
@@ -6692,6 +6692,21 @@ std::vector<Option> get_rgw_options() {
                          "ListBucketVersions(max-keys), "
                          "ListBucketMultiPartUploads(max-uploads), "
                          "ListMultipartUploadParts(max-parts)"),
+
+    Option("rgw_sts_token_introspection_url", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_description("STS Web Token introspection URL")
+    .set_long_description("URL for introspecting an STS Web Token."),
+
+    Option("rgw_sts_client_id", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_description("Client Id")
+    .set_long_description("Client Id needed for introspecting a Web Token."),
+
+    Option("rgw_sts_client_secret", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+    .set_default("")
+    .set_description("Client Secret")
+    .set_long_description("Client Secret needed for introspecting a Web Token."),
   });
 }
 
index 11de9a7791ed5dc6e4a25e9ded48373d3e498f7c..3cdc7d360af6d63db63372b53bb46c17bc8edbff 100644 (file)
@@ -320,6 +320,54 @@ rgw::auth::Strategy::add_engine(const Control ctrl_flag,
   auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag));
 }
 
+void rgw::auth::WebIdentityApplier::to_str(std::ostream& out) const
+{
+  out << "rgw::auth::WebIdentityApplier(sub =" << token_claims.sub
+      << ", user_name=" << token_claims.user_name
+      << ", aud =" << token_claims.aud
+      << ", provider_id =" << token_claims.iss << ")";
+}
+
+string rgw::auth::WebIdentityApplier::get_idp_url() const
+{
+  string idp_url = token_claims.iss;
+  auto pos = idp_url.find("http://");
+  if (pos == std::string::npos) {
+      pos = idp_url.find("https://");
+      if (pos != std::string::npos) {
+        idp_url.erase(pos, 8);
+    }
+  } else {
+    idp_url.erase(pos, 7);
+  }
+  return idp_url;
+}
+
+void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const
+{
+  s->info.args.append("sub", token_claims.sub);
+  s->info.args.append("aud", token_claims.aud);
+  s->info.args.append("provider_id", token_claims.iss);
+
+  string idp_url = get_idp_url();
+  string condition = idp_url + ":app_id";
+  s->env.emplace(condition, token_claims.aud);
+}
+
+bool rgw::auth::WebIdentityApplier::is_identity(const idset_t& ids) const
+{
+  if (ids.size() > 1) {
+    return false;
+  }
+
+  for (auto id : ids) {
+    string idp_url = get_idp_url();
+    if (id.is_oidc_provider() && id.get_idp_url() == idp_url) {
+      return true;
+    }
+  }
+    return false;
+}
 
 /* rgw::auth::RemoteAuthApplier */
 uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const
index d141479a767b77ecd22196c7d6a2c20cb9aab80f..630cc252454c8636958a34bf64f0b2b48241f177 100644 (file)
@@ -13,6 +13,7 @@
 
 #include "rgw_common.h"
 #include "rgw_keystone.h"
+#include "rgw_web_idp.h"
 
 #define RGW_USER_ANON_ID "anonymous"
 
@@ -350,6 +351,67 @@ protected:
  * Each new Strategy should be exposed to it. */
 class StrategyRegistry;
 
+class WebIdentityApplier : public IdentityApplier {
+protected:
+  CephContext* const cct;
+  RGWRados* const store;
+  rgw::web_idp::WebTokenClaims token_claims;
+
+  string get_idp_url() const;
+
+public:
+  WebIdentityApplier( CephContext* const cct,
+                      RGWRados* const store,
+                      const rgw::web_idp::WebTokenClaims& token_claims)
+    : cct(cct),
+      store(store),
+      token_claims(token_claims) {
+  }
+
+  void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override {
+    user_info.user_id = rgw_user(token_claims.sub);
+    user_info.display_name = token_claims.user_name;
+  }
+
+  void modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const override;
+
+  uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const  override {
+    return RGW_PERM_NONE;
+  }
+
+  bool is_admin_of(const rgw_user& uid) const override {
+    return false;
+  }
+
+  bool is_owner_of(const rgw_user& uid) const override {
+    return false;
+  }
+
+  uint32_t get_perm_mask() const override {
+    return RGW_PERM_NONE;
+  }
+
+  void to_str(std::ostream& out) const override;
+
+  bool is_identity(const idset_t& ids) const override;
+
+  uint32_t get_identity_type() const override {
+    return TYPE_WEB;
+  }
+
+  string get_acct_name() const override {
+    return token_claims.user_name;
+  }
+
+  struct Factory {
+    virtual ~Factory() {}
+
+    virtual aplptr_t create_apl_web_identity( CephContext* cct,
+                                              const req_state* s,
+                                              const rgw::web_idp::WebTokenClaims& token) const = 0;
+  };
+};
+
 /* 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.
index 80a6ed6f05f80cc914b56f2341fbb7716cc6bce0..9316e8ec4ebd8ab09de5a70685f14137f2049699 100644 (file)
@@ -14,6 +14,7 @@
 #include "rgw_auth.h"
 #include "rgw_auth_s3.h"
 #include "rgw_swift_auth.h"
+#include "rgw_rest_sts.h"
 
 namespace rgw {
 namespace auth {
@@ -53,12 +54,15 @@ class StrategyRegistry {
 
   rgw::auth::swift::DefaultStrategy swift_strategy;
 
+  rgw::auth::sts::DefaultStrategy sts_strategy;
+
 public:
   StrategyRegistry(CephContext* const cct,
                    RGWRados* const store)
     : s3_main_strategy(cct, store),
       s3_post_strategy(cct, store),
-      swift_strategy(cct, store) {
+      swift_strategy(cct, store),
+      sts_strategy(cct, store) {
   }
 
   const s3_main_strategy_t& get_s3_main() const {
@@ -73,6 +77,10 @@ public:
     return swift_strategy;
   }
 
+  const rgw::auth::sts::DefaultStrategy& get_sts() const {
+    return sts_strategy;
+  }
+
   static std::shared_ptr<StrategyRegistry>
   create(CephContext* const cct,
          RGWRados* const store) {
index 36a96e7b7e19e38be2d8919163a8f80dc1489efa..3cf063663dd4e29108f48216f8bb909b93f3a091 100644 (file)
@@ -9,6 +9,7 @@
 
 #include "common/armor.h"
 #include "common/utf8.h"
+#include "rgw_rest_s3.h"
 #include "rgw_auth_s3.h"
 #include "rgw_common.h"
 #include "rgw_client_io.h"
index d848d917136203b657c7cb2f116ab990962be170..cc7e994d0073c57cf6eaacc4dbf75cabce43b44c 100644 (file)
@@ -17,7 +17,6 @@
 #include "common/sstring.hh"
 #include "rgw_common.h"
 #include "rgw_rest_s3.h"
-
 #include "rgw_auth.h"
 #include "rgw_auth_filters.h"
 #include "rgw_auth_keystone.h"
@@ -71,6 +70,7 @@ class STSAuthStrategy : public rgw::auth::Strategy,
       rgw::auth::RoleApplier(cct, role_name, user_id, role_policies));
     return aplptr_t(new decltype(apl)(std::move(apl)));
   }
+
 public:
   STSAuthStrategy(CephContext* const cct,
                        RGWRados* const store,
index 772aecfa55c0c4e92c6d1eb6c099d284fc16cc23..b2b9b7b4ec75c13342d3cd41fa451c86cc44078c 100644 (file)
@@ -116,9 +116,10 @@ WRITE_CLASS_ENCODER(rgw_user)
 namespace rgw {
 namespace auth {
 class Principal {
-  enum types { User, Role, Tenant, Wildcard };
+  enum types { User, Role, Tenant, Wildcard, OidcProvider };
   types t;
   rgw_user u;
+  string idp_url;
 
   explicit Principal(types t)
     : t(t) {}
@@ -126,6 +127,9 @@ class Principal {
   Principal(types t, std::string&& n, std::string i)
     : t(t), u(std::move(n), std::move(i)) {}
 
+  Principal(string&& idp_url)
+    : t(OidcProvider), idp_url(std::move(idp_url)) {}
+
 public:
 
   static Principal wildcard() {
@@ -144,6 +148,10 @@ public:
     return Principal(Tenant, std::move(t), {});
   }
 
+  static Principal oidc_provider(string&& idp_url) {
+    return Principal(std::move(idp_url));
+  }
+
   bool is_wildcard() const {
     return t == Wildcard;
   }
@@ -160,6 +168,10 @@ public:
     return t == Tenant;
   }
 
+  bool is_oidc_provider() const {
+    return t == OidcProvider;
+  }
+
   const std::string& get_tenant() const {
     return u.tenant;
   }
@@ -168,6 +180,10 @@ public:
     return u.id;
   }
 
+  const string& get_idp_url() const {
+    return idp_url;
+  }
+
   bool operator ==(const Principal& o) const {
     return (t == o.t) && (u == o.u);
   }
index a23eb418edd0a1ca835078a63c2cc8da601402ab..08cd257b65706fb1edee52894b6a6f780ab3b927 100644 (file)
@@ -140,6 +140,7 @@ rgw_http_errors rgw_http_swift_errors({
 
 rgw_http_errors rgw_http_sts_errors({
     { ERR_PACKED_POLICY_TOO_LARGE, {400, "PackedPolicyTooLarge" }},
+    { ERR_INVALID_IDENTITY_TOKEN, {400, "InvalidIdentityToken" }},
 });
 
 int rgw_perf_start(CephContext *cct)
index 890e746ce32cd0be60857de12a25864045b7be81..0ddc16578173c5f23f4c7d455aa1b7fcc23140d9 100644 (file)
@@ -230,6 +230,7 @@ using ceph::crypto::MD5;
 
 // STS Errors
 #define ERR_PACKED_POLICY_TOO_LARGE 2400
+#define ERR_INVALID_IDENTITY_TOKEN  2401
 
 #ifndef UINT32_MAX
 #define UINT32_MAX (0xffffffffu)
@@ -525,6 +526,7 @@ enum RGWOpType {
   /* sts specific*/
   RGW_STS_ASSUME_ROLE,
   RGW_STS_GET_SESSION_TOKEN,
+  RGW_STS_ASSUME_ROLE_WEB_IDENTITY,
   /* pubsub */
   RGW_OP_PUBSUB_TOPIC_CREATE,
   RGW_OP_PUBSUB_TOPICS_LIST,
@@ -647,7 +649,8 @@ enum RGWIdentityType
   TYPE_RGW=1,
   TYPE_KEYSTONE=2,
   TYPE_LDAP=3,
-  TYPE_ROLE=4
+  TYPE_ROLE=4,
+  TYPE_WEB=5,
 };
 
 static string RGW_STORAGE_CLASS_STANDARD = "STANDARD";
index 5d38031d0f65b8ae3d41f0dd174c70d4bd7f3cd2..6084122e5a6e3ef282edcdc8052f0eedb429dfe3 100644 (file)
@@ -224,7 +224,7 @@ boost::optional<ARN> ARN::parse(const string& s, bool wildcards) {
                             std::regex_constants::ECMAScript |
                             std::regex_constants::optimize);
   static const regex rx_no_wild(
-    "arn:([^:*]*):([^:*]*):([^:*]*):([^:*]*):([^:*]*)",
+    "arn:([^:*]*):([^:*]*):([^:*]*):([^:*]*):(.*)",
     std::regex_constants::ECMAScript |
     std::regex_constants::optimize);
 
@@ -469,6 +469,7 @@ static const actpair actpairs[] =
  { "iam:ListRolePolicies", iamListRolePolicies},
  { "iam:DeleteRolePolicy", iamDeleteRolePolicy},
  { "sts:AssumeRole", stsAssumeRole},
+ { "sts:AssumeRoleWithWebIdentity", stsAssumeRoleWithWebIdentity},
 };
 
 struct PolicyParser;
@@ -757,8 +758,8 @@ static boost::optional<Principal> parse_principal(CephContext* cct, TokenID t,
     // Do nothing for now.
   } else if (t == TokenID::CanonicalUser) {
 
-    // AWS ARNs
-  } else if (t == TokenID::AWS) {
+  }  // AWS and Federated ARNs
+   else if (t == TokenID::AWS || t == TokenID::Federated) {
     if (auto a = ARN::parse(s)) {
       if (a->resource == "root") {
        return Principal::tenant(std::move(a->account));
@@ -779,6 +780,10 @@ static boost::optional<Principal> parse_principal(CephContext* cct, TokenID t,
          return Principal::role(std::move(a->account),
                                 match[2]);
        }
+
+        if (match[1] == "oidc-provider") {
+                return Principal::oidc_provider(std::move(match[2]));
+        }
       }
     } else {
       if (std::none_of(s.begin(), s.end(),
@@ -1326,12 +1331,28 @@ Effect Statement::eval(const Environment& e,
 
 Effect Statement::eval_principal(const Environment& e,
                       boost::optional<const rgw::auth::Identity&> ida) const {
-  if (ida && (!ida->is_identity(princ) || ida->is_identity(noprinc))) {
-    return Effect::Deny;
+  if (ida) {
+    if (princ.empty() && noprinc.empty()) {
+      return Effect::Deny;
+    }
+    if (!princ.empty() && !ida->is_identity(princ)) {
+      return Effect::Deny;
+    } else if (!noprinc.empty() && ida->is_identity(noprinc)) {
+      return Effect::Deny;
+    }
   }
   return Effect::Allow;
 }
 
+Effect Statement::eval_conditions(const Environment& e) const {
+  if (std::all_of(conditions.begin(),
+                 conditions.end(),
+                 [&e](const Condition& c) { return c.eval(e);})) {
+    return Effect::Allow;
+  }
+  return Effect::Deny;
+}
+
 namespace {
 const char* action_bit_string(uint64_t action) {
   switch (action) {
@@ -1537,6 +1558,9 @@ const char* action_bit_string(uint64_t action) {
 
   case stsAssumeRole:
     return "sts:AssumeRole";
+
+  case stsAssumeRoleWithWebIdentity:
+    return "sts:AssumeRoleWithWebIdentity";
   }
   return "s3Invalid";
 }
@@ -1676,6 +1700,19 @@ Effect Policy::eval_principal(const Environment& e,
   return allowed ? Effect::Allow : Effect::Deny;
 }
 
+Effect Policy::eval_conditions(const Environment& e) const {
+  auto allowed = false;
+  for (auto& s : statements) {
+    auto g = s.eval_conditions(e);
+    if (g == Effect::Deny) {
+      return g;
+    } else if (g == Effect::Allow) {
+      allowed = true;
+    }
+  }
+  return allowed ? Effect::Allow : Effect::Deny;
+}
+
 ostream& operator <<(ostream& m, const Policy& p) {
   m << "{ Version: "
     << (p.version == Version::v2008_10_17 ? "2008-10-17" : "2012-10-17");
index f15d215a8a492e51c8ae4b25592a503ed89bc349..7911a97de800188768597fbe538f342f7b1d08e3 100644 (file)
@@ -113,7 +113,8 @@ static constexpr std::uint64_t iamListRolePolicies = 66;
 static constexpr std::uint64_t iamDeleteRolePolicy = 67;
 static constexpr std::uint64_t iamAll = 68;
 static constexpr std::uint64_t stsAssumeRole = 69;
-static constexpr std::uint64_t stsAll = 70;
+static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = 70;
+static constexpr std::uint64_t stsAll = 71;
 
 static constexpr std::uint64_t s3Count = s3DeleteObjectVersionTagging + 1;
 static constexpr std::uint64_t allCount = stsAll + 1;
@@ -124,9 +125,9 @@ using NotAction_t = Action_t;
 static const Action_t None(0);
 static const Action_t s3AllValue("111111111111111111111111111111111111111111111111111111");
 static const Action_t iamAllValue("11111111111110000000000000000000000000000000000000000000000000000000");
-static const Action_t stsAllValue("1000000000000000000000000000000000000000000000000000000000000000000000");
+static const Action_t stsAllValue("11000000000000000000000000000000000000000000000000000000000000000000000");
 //Modify allValue if more Actions are added
-static const Action_t allValue("11111111111111111111111111111111111111111111111111111111111111111111111");
+static const Action_t allValue("111111111111111111111111111111111111111111111111111111111111111111111111");
 
 namespace {
 inline int op_to_perm(std::uint64_t op) {
@@ -465,6 +466,8 @@ struct Statement {
 
   Effect eval_principal(const Environment& e,
                       boost::optional<const rgw::auth::Identity&> ida) const;
+
+  Effect eval_conditions(const Environment& e) const;
 };
 
 std::ostream& operator <<(ostream& m, const Statement& s);
@@ -496,6 +499,8 @@ struct Policy {
   Effect eval_principal(const Environment& e,
              boost::optional<const rgw::auth::Identity&> ida) const;
 
+  Effect eval_conditions(const Environment& e) const;
+
   template <typename F>
   bool has_conditional(const string& conditional, F p) const {
     for (const auto&s: statements){
index 180b9f3f4d9fbd640288373bb9520092aae46672..3b12fffa8b6b2d659ab9114a48cfe2b445efd724 100644 (file)
@@ -836,26 +836,25 @@ static void rgw_add_grant_to_iam_environment(rgw::IAM::Environment& e, struct re
   }
 }
 
-rgw::IAM::Environment rgw_build_iam_environment(RGWRados* store,
-                                               struct req_state* s)
+void rgw_build_iam_environment(RGWRados* store,
+                                     struct req_state* s)
 {
-  rgw::IAM::Environment e;
   const auto& m = s->info.env->get_map();
   auto t = ceph::real_clock::now();
-  e.emplace("aws:CurrentTime", std::to_string(ceph::real_clock::to_time_t(t)));
-  e.emplace("aws:EpochTime", ceph::to_iso_8601(t));
+  s->env.emplace("aws:CurrentTime", std::to_string(ceph::real_clock::to_time_t(t)));
+  s->env.emplace("aws:EpochTime", ceph::to_iso_8601(t));
   // TODO: This is fine for now, but once we have STS we'll need to
   // look and see. Also this won't work with the IdentityApplier
   // model, since we need to know the actual credential.
-  e.emplace("aws:PrincipalType", "User");
+  s->env.emplace("aws:PrincipalType", "User");
 
   auto i = m.find("HTTP_REFERER");
   if (i != m.end()) {
-    e.emplace("aws:Referer", i->second);
+    s->env.emplace("aws:Referer", i->second);
   }
 
   if (rgw_transport_is_secure(s->cct, *s->info.env)) {
-    e.emplace("aws:SecureTransport", "true");
+    s->env.emplace("aws:SecureTransport", "true");
   }
 
   const auto remote_addr_param = s->cct->_conf->rgw_remote_addr_param;
@@ -874,21 +873,20 @@ rgw::IAM::Environment rgw_build_iam_environment(RGWRados* store,
        ip = &temp;
       }
     }
-    e.emplace("aws:SourceIp", *ip);
+    s->env.emplace("aws:SourceIp", *ip);
   }
 
   i = m.find("HTTP_USER_AGENT"); {
   if (i != m.end())
-    e.emplace("aws:UserAgent", i->second);
+    s->env.emplace("aws:UserAgent", i->second);
   }
 
   if (s->user) {
     // What to do about aws::userid? One can have multiple access
     // keys so that isn't really suitable. Do we have a durable
     // identifier that can persist through name changes?
-    e.emplace("aws:username", s->user->user_id.id);
+    s->env.emplace("aws:username", s->user->user_id.id);
   }
-  return e;
 }
 
 void rgw_bucket_object_pre_exec(struct req_state *s)
@@ -7100,7 +7098,7 @@ int RGWHandler::do_init_permissions()
     return ret==-ENODATA ? -EACCES : ret;
   }
 
-  s->env = rgw_build_iam_environment(store, s);
+  rgw_build_iam_environment(store, s);
   return ret;
 }
 
index 45c9266f71d0bad9fa5be974dfffe02b88bafc41..e0cc3ba3779096abbccb014ee2e2321e27d1093d 100644 (file)
@@ -1835,8 +1835,8 @@ public:
 extern int rgw_build_bucket_policies(RGWRados* store, struct req_state* s);
 extern int rgw_build_object_policies(RGWRados *store, struct req_state *s,
                                     bool prefetch_data);
-extern rgw::IAM::Environment rgw_build_iam_environment(RGWRados* store,
-                                                      struct req_state* s);
+extern void rgw_build_iam_environment(RGWRados* store,
+                                                                         struct req_state* s);
 extern vector<rgw::IAM::Policy> get_iam_user_policy_from_attr(CephContext* cct,
                         RGWRados* store,
                         map<string, bufferlist>& attrs,
index 0ea69f660a16560f314bce19219dd34fc2153862..df43d1da06c205f478f311d23e95fe3ca73e5495 100644 (file)
@@ -1817,7 +1817,7 @@ int RGWHandler_REST::init_permissions(RGWOp* op)
         lderr(s->cct) << "Error reading IAM User Policy: " << e.what() << dendl;
       }
     }
-    s->env = rgw_build_iam_environment(store, s);
+    rgw_build_iam_environment(store, s);
     return 0;
   }
 
index cd58900ee3c274ff437b25f73645937d3a42b2ba..4fb3df7637a49e8b6fd3ffbc0a71f09947592882 100644 (file)
@@ -3470,6 +3470,14 @@ int RGWHandler_REST_S3::init(RGWRados *store, struct req_state *s,
   return RGWHandler_REST::init(store, s, cio);
 }
 
+int RGWHandler_REST_S3::authorize(const DoutPrefixProvider *dpp)
+{
+  if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+    return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
+  }
+  return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
+}
+
 enum class AwsVersion {
   UNKNOWN,
   V2,
index 0eaff8f7855b56031284224a8d22ad524f9db681..274fe0e093b5e70fa4560f25c92981f46cecdfa9 100644 (file)
@@ -509,15 +509,13 @@ public:
   explicit RGWHandler_REST_S3(const rgw::auth::StrategyRegistry& auth_registry)
     : RGWHandler_REST(),
       auth_registry(auth_registry) {
-  }
+    }
   ~RGWHandler_REST_S3() override = default;
 
   int init(RGWRados *store,
            struct req_state *s,
            rgw::io::BasicClient *cio) override;
-  int authorize(const DoutPrefixProvider *dpp) override {
-    return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
-  }
+  int authorize(const DoutPrefixProvider *dpp) override;
   int postauth_init() override;
 };
 
index 4a7b6b6b732e9da18a5d3a270cbfa515cd384fa1..bcbd9008c6b79028ad2819f537130ad54c6559a9 100644 (file)
 
 #include "rgw_rest.h"
 #include "rgw_auth.h"
-#include "rgw_auth_s3.h"
+#include "rgw_auth_registry.h"
 #include "rgw_rest_sts.h"
+#include "rgw_auth_s3.h"
+
 #include "rgw_formats.h"
 #include "rgw_client_io.h"
 
 #define dout_context g_ceph_context
 #define dout_subsys ceph_subsys_rgw
 
+namespace rgw {
+namespace auth {
+namespace sts {
+
+bool
+WebTokenEngine::is_applicable(const std::string& token) const noexcept
+{
+  return ! token.empty();
+}
+
+boost::optional<WebTokenEngine::token_t>
+WebTokenEngine::get_from_idp(const DoutPrefixProvider* dpp, const std::string& token) const
+{
+  //Access token conforming to OAuth2.0
+  if (! cct->_conf->rgw_sts_token_introspection_url.empty()) {
+    bufferlist introspect_resp;
+    RGWHTTPTransceiver introspect_req(cct, "POST", cct->_conf->rgw_sts_token_introspection_url, &introspect_resp);
+    //Headers
+    introspect_req.append_header("Content-Type", "application/x-www-form-urlencoded");
+    string base64_creds = "Basic " + rgw::to_base64(cct->_conf->rgw_sts_client_id + ":" + cct->_conf->rgw_sts_client_secret);
+    introspect_req.append_header("Authorization", base64_creds);
+    // POST data
+    string post_data = "token=" + token;
+    introspect_req.set_post_data(post_data);
+    introspect_req.set_send_length(post_data.length());
+
+    int res = introspect_req.process();
+    if (res < 0) {
+      ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
+      throw -EINVAL;
+    }
+    //Debug only
+    ldpp_dout(dpp, 20) << "HTTP status: " << introspect_req.get_http_status() << dendl;
+    ldpp_dout(dpp, 20) << "JSON Response is: " << introspect_resp.c_str() << dendl;
+
+    JSONParser parser;
+    WebTokenEngine::token_t token;
+    if (!parser.parse(introspect_resp.c_str(), introspect_resp.length())) {
+      ldpp_dout(dpp, 2) << "Malformed json" << dendl;
+      throw -EINVAL;
+    } else {
+      bool is_active;
+      JSONDecoder::decode_json("active", is_active, &parser);
+      if (! is_active) {
+        ldpp_dout(dpp, 0) << "Active state is false"  << dendl;
+        throw -ERR_INVALID_IDENTITY_TOKEN;
+      }
+      JSONDecoder::decode_json("iss", token.iss, &parser);
+      JSONDecoder::decode_json("aud", token.aud, &parser);
+      JSONDecoder::decode_json("sub", token.sub, &parser);
+      JSONDecoder::decode_json("user_name", token.user_name, &parser);
+    }
+    return token;
+  }
+  return boost::none;
+}
+
+WebTokenEngine::result_t
+WebTokenEngine::authenticate( const DoutPrefixProvider* dpp,
+                              const std::string& token,
+                              const req_state* const s) const
+{
+  boost::optional<WebTokenEngine::token_t> t;
+
+  if (! is_applicable(token)) {
+    return result_t::deny();
+  }
+
+  try {
+    t = get_from_idp(dpp, token);
+  } catch(...) {
+    return result_t::deny(-EACCES);
+  }
+
+  if (t) {
+    auto apl = apl_factory->create_apl_web_identity(cct, s, *t);
+    return result_t::grant(std::move(apl));
+  }
+  return result_t::deny(-EACCES);
+}
+
+}; /* namespace sts */
+}; /* namespace auth */
+}; /* namespace rgw */
+
 int RGWREST_STS::verify_permission()
 {
   STS::STSService _sts(s->cct, store, s->user->user_id, s->auth.identity.get());
@@ -54,9 +141,13 @@ int RGWREST_STS::verify_permission()
   try {
     const rgw::IAM::Policy p(s->cct, s->user->user_id.tenant, bl);
     //Check if the input role arn is there as one of the Principals in the policy,
-  // If yes, then return 0, else -EPERM
-    auto res = p.eval_principal(s->env, *s->auth.identity);
-    if (res == rgw::IAM::Effect::Deny) {
+    // If yes, then return 0, else -EPERM
+    auto p_res = p.eval_principal(s->env, *s->auth.identity);
+    if (p_res == rgw::IAM::Effect::Deny) {
+      return -EPERM;
+    }
+    auto c_res = p.eval_conditions(s->env);
+    if (c_res == rgw::IAM::Effect::Deny) {
       return -EPERM;
     }
   } catch (rgw::IAM::PolicyParseException& e) {
@@ -120,6 +211,66 @@ void RGWSTSGetSessionToken::execute()
   }
 }
 
+int RGWSTSAssumeRoleWithWebIdentity::get_params()
+{
+  duration = s->info.args.get("DurationSeconds");
+  providerId = s->info.args.get("ProviderId");
+  policy = s->info.args.get("Policy");
+  roleArn = s->info.args.get("RoleArn");
+  roleSessionName = s->info.args.get("RoleSessionName");
+  iss = s->info.args.get("provider_id");
+  sub = s->info.args.get("sub");
+  aud = s->info.args.get("aud");
+
+  if (roleArn.empty() || roleSessionName.empty() || sub.empty() || aud.empty()) {
+    ldout(s->cct, 20) << "ERROR: one of role arn or role session name or token is empty" << dendl;
+    return -EINVAL;
+  }
+
+  if (! policy.empty()) {
+    bufferlist bl = bufferlist::static_from_string(policy);
+    try {
+      const rgw::IAM::Policy p(s->cct, s->user->user_id.tenant, bl);
+    }
+    catch (rgw::IAM::PolicyParseException& e) {
+      ldout(s->cct, 20) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
+      return -ERR_MALFORMED_DOC;
+    }
+  }
+
+  return 0;
+}
+
+void RGWSTSAssumeRoleWithWebIdentity::execute()
+{
+  if (op_ret = get_params(); op_ret < 0) {
+    return;
+  }
+
+  STS::AssumeRoleWithWebIdentityRequest req(duration, providerId, policy, roleArn,
+                        roleSessionName, iss, sub, aud);
+  STS::AssumeRoleWithWebIdentityResponse response = sts.assumeRoleWithWebIdentity(req);
+  op_ret = std::move(response.assumeRoleResp.retCode);
+
+  //Dump the output
+  if (op_ret == 0) {
+    s->formatter->open_object_section("AssumeRoleWithWebIdentityResponse");
+    s->formatter->open_object_section("AssumeRoleWithWebIdentityResult");
+    encode_json("SubjectFromWebIdentityToken", response.sub , s->formatter);
+    encode_json("Audience", response.aud , s->formatter);
+    s->formatter->open_object_section("AssumedRoleUser");
+    response.assumeRoleResp.user.dump(s->formatter);
+    s->formatter->close_section();
+    s->formatter->open_object_section("Credentials");
+    response.assumeRoleResp.creds.dump(s->formatter);
+    s->formatter->close_section();
+    encode_json("Provider", response.providerId , s->formatter);
+    encode_json("PackedPolicySize", response.assumeRoleResp.packedPolicySize , s->formatter);
+    s->formatter->close_section();
+    s->formatter->close_section();
+  }
+}
+
 int RGWSTSAssumeRole::get_params()
 {
   duration = s->info.args.get("DurationSeconds");
@@ -157,25 +308,33 @@ void RGWSTSAssumeRole::execute()
 
   STS::AssumeRoleRequest req(duration, externalId, policy, roleArn,
                         roleSessionName, serialNumber, tokenCode);
-  const auto& [ret, assumedRoleUser, creds, packedPolicySize] = sts.assumeRole(req);
-  op_ret = std::move(ret);
+  STS::AssumeRoleResponse response = sts.assumeRole(req);
+  op_ret = std::move(response.retCode);
   //Dump the output
   if (op_ret == 0) {
     s->formatter->open_object_section("AssumeRoleResponse");
     s->formatter->open_object_section("AssumeRoleResult");
     s->formatter->open_object_section("Credentials");
-    creds.dump(s->formatter);
+    response.creds.dump(s->formatter);
     s->formatter->close_section();
     s->formatter->open_object_section("AssumedRoleUser");
-    assumedRoleUser.dump(s->formatter);
+    response.user.dump(s->formatter);
     s->formatter->close_section();
-    encode_json("PackedPolicySize", packedPolicySize , s->formatter);
+    encode_json("PackedPolicySize", response.packedPolicySize , s->formatter);
     s->formatter->close_section();
     s->formatter->close_section();
   }
 }
 
-RGWOp *RGWHandler_REST_STS::op_post()
+int RGW_Auth_STS::authorize(const DoutPrefixProvider *dpp,
+                            RGWRados *store,
+                            const rgw::auth::StrategyRegistry& auth_registry,
+                            struct req_state *s)
+{
+    return rgw::auth::Strategy::apply(dpp, auth_registry.get_sts(), s);
+}
+
+void RGWHandler_REST_STS::rgw_sts_parse_input()
 {
   const auto max_size = s->cct->_conf->rgw_max_put_param_size;
 
@@ -203,6 +362,11 @@ RGWOp *RGWHandler_REST_STS::op_post()
        }
     }
   }
+}
+
+RGWOp *RGWHandler_REST_STS::op_post()
+{
+  rgw_sts_parse_input();
 
   if (s->info.args.exists("Action"))    {
     string action = s->info.args.get("Action");
@@ -210,6 +374,8 @@ RGWOp *RGWHandler_REST_STS::op_post()
       return new RGWSTSAssumeRole;
     } else if (action == "GetSessionToken") {
       return new RGWSTSGetSessionToken;
+    } else if (action == "AssumeRoleWithWebIdentity") {
+      return new RGWSTSAssumeRoleWithWebIdentity;
     }
   }
 
@@ -230,6 +396,14 @@ int RGWHandler_REST_STS::init(RGWRados *store,
   return RGWHandler_REST::init(store, s, cio);
 }
 
+int RGWHandler_REST_STS::authorize(const DoutPrefixProvider* dpp)
+{
+  if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+    return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
+  }
+  return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
+}
+
 int RGWHandler_REST_STS::init_from_header(struct req_state* s,
                                           int default_formatter,
                                           bool configurable_format)
index 6721c398e2e1c308cdb990231397d88f18850fd1..a7ec7ba26430b4069e7ec67239e703120970bcd9 100644 (file)
@@ -4,7 +4,95 @@
 #ifndef CEPH_RGW_REST_STS_H
 #define CEPH_RGW_REST_STS_H
 
+#include "rgw_auth.h"
+#include "rgw_auth_filters.h"
 #include "rgw_sts.h"
+#include "rgw_web_idp.h"
+
+namespace rgw {
+namespace auth {
+namespace sts   {
+
+class WebTokenEngine : public rgw::auth::Engine {
+  CephContext* const cct;
+
+  using result_t = rgw::auth::Engine::result_t;
+  using token_t = rgw::web_idp::WebTokenClaims;
+
+  const rgw::auth::TokenExtractor* const extractor;
+  const rgw::auth::WebIdentityApplier::Factory* const apl_factory;
+
+  bool is_applicable(const std::string& token) const noexcept;
+
+  boost::optional<token_t>
+  get_from_idp(const DoutPrefixProvider* dpp, const std::string& token) const;
+
+  result_t authenticate(const DoutPrefixProvider* dpp,
+                        const std::string& token,
+                        const req_state* s) const;
+
+public:
+  WebTokenEngine(CephContext* const cct,
+                    const rgw::auth::TokenExtractor* const extractor,
+                    const rgw::auth::WebIdentityApplier::Factory* const apl_factory)
+    : cct(cct),
+      extractor(extractor),
+      apl_factory(apl_factory) {
+  }
+
+  const char* get_name() const noexcept override {
+    return "rgw::auth::sts::WebTokenEngine";
+  }
+
+  result_t authenticate(const DoutPrefixProvider* dpp, const req_state* const s) const override {
+    return authenticate(dpp, extractor->get_token(s), s);
+  }
+}; /* class WebTokenEngine */
+
+class DefaultStrategy : public rgw::auth::Strategy,
+                        public rgw::auth::TokenExtractor,
+                        public rgw::auth::WebIdentityApplier::Factory {
+  RGWRados* const store;
+
+  /* The engine. */
+  const WebTokenEngine web_token_engine;
+
+  using aplptr_t = rgw::auth::IdentityApplier::aplptr_t;
+
+  /* The method implements TokenExtractor for Web Token in req_state. */
+  std::string get_token(const req_state* const s) const override {
+    return s->info.args.get("WebIdentityToken");
+  }
+
+  aplptr_t create_apl_web_identity( CephContext* cct,
+                                    const req_state* s,
+                                    const rgw::web_idp::WebTokenClaims& token) const override {
+    auto apl = rgw::auth::add_sysreq(cct, store, s,
+      rgw::auth::WebIdentityApplier(cct, store, token));
+    return aplptr_t(new decltype(apl)(std::move(apl)));
+  }
+
+public:
+  DefaultStrategy(CephContext* const cct,
+                  RGWRados* const store)
+    : store(store),
+      web_token_engine(cct,
+                        static_cast<rgw::auth::TokenExtractor*>(this),
+                        static_cast<rgw::auth::WebIdentityApplier::Factory*>(this)) {
+    /* When the constructor's body is being executed, all member engines
+     * should be initialized. Thus, we can safely add them. */
+    using Control = rgw::auth::Strategy::Control;
+    add_engine(Control::SUFFICIENT, web_token_engine);
+  }
+
+  const char* get_name() const noexcept override {
+    return "rgw::auth::sts::DefaultStrategy";
+  }
+};
+
+}; /* namespace sts */
+}; /* namespace auth */
+};
 
 class RGWREST_STS : public RGWRESTOp {
 protected:
@@ -15,6 +103,24 @@ public:
   void send_response() override;
 };
 
+class RGWSTSAssumeRoleWithWebIdentity : public RGWREST_STS {
+protected:
+  string duration;
+  string providerId;
+  string policy;
+  string roleArn;
+  string roleSessionName;
+  string sub;
+  string aud;
+  string iss;
+public:
+  RGWSTSAssumeRoleWithWebIdentity() = default;
+  void execute() override;
+  int get_params();
+  const char* name() const override { return "assume_role_web_identity"; }
+  RGWOpType get_type() override { return RGW_STS_ASSUME_ROLE_WEB_IDENTITY; }
+};
+
 class RGWSTSAssumeRole : public RGWREST_STS {
 protected:
   string duration;
@@ -46,10 +152,20 @@ public:
   RGWOpType get_type() override { return RGW_STS_GET_SESSION_TOKEN; }
 };
 
+class RGW_Auth_STS {
+public:
+  static int authorize(const DoutPrefixProvider *dpp,
+                       RGWRados *store,
+                       const rgw::auth::StrategyRegistry& auth_registry,
+                       struct req_state *s);
+};
+
 class RGWHandler_REST_STS : public RGWHandler_REST {
   const rgw::auth::StrategyRegistry& auth_registry;
   RGWOp *op_post() override;
+  void rgw_sts_parse_input();
 public:
+
   static int init_from_header(struct req_state *s, int default_formatter, bool configurable_format);
 
   RGWHandler_REST_STS(const rgw::auth::StrategyRegistry& auth_registry)
@@ -60,9 +176,7 @@ public:
   int init(RGWRados *store,
            struct req_state *s,
            rgw::io::BasicClient *cio) override;
-  int authorize(const DoutPrefixProvider* dpp) override {
-    return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
-  }
+  int authorize(const DoutPrefixProvider* dpp) override;
   int postauth_init() override { return 0; }
 };
 
index d4679983363a5e5f92218e077b0a27df7e639cba..a06ae29a235a4782eadfcce3c7f5ebd8c04d7dd0 100644 (file)
@@ -161,38 +161,26 @@ int AssumedRoleUser::generateAssumedRoleUser(CephContext* cct,
   return 0;
 }
 
-AssumeRoleRequest::AssumeRoleRequest(string& _duration, string& _externalId, string& _iamPolicy,
-                    string& _roleArn, string& _roleSessionName, string& _serialNumber,
-                    string& _tokenCode)
-    : externalId(_externalId), iamPolicy(_iamPolicy),
-      roleArn(_roleArn), roleSessionName(_roleSessionName),
-      serialNumber(_serialNumber), tokenCode(_tokenCode)
+AssumeRoleRequestBase::AssumeRoleRequestBase( const string& duration,
+                                              const string& iamPolicy,
+                                              const string& roleArn,
+                                              const string& roleSessionName)
+  : iamPolicy(iamPolicy), roleArn(roleArn), roleSessionName(roleSessionName)
 {
-  if (_duration.empty()) {
-    duration = DEFAULT_DURATION_IN_SECS;
+  if (duration.empty()) {
+    this->duration = DEFAULT_DURATION_IN_SECS;
   } else {
-    duration = std::stoull(_duration);
+    this->duration = std::stoull(duration);
   }
 }
 
-int AssumeRoleRequest::validate_input() const
+int AssumeRoleRequestBase::validate_input() const
 {
   if (duration < MIN_DURATION_IN_SECS ||
           duration > MAX_DURATION_IN_SECS) {
     return -EINVAL;
   }
 
-  if (! externalId.empty()) {
-    if (externalId.length() < MIN_EXTERNAL_ID_LEN ||
-          externalId.length() > MAX_EXTERNAL_ID_LEN) {
-      return -EINVAL;
-    }
-
-    std::regex regex_externalId("[A-Za-z0-9_=,.@:/-]+");
-    if (! std::regex_match(externalId, regex_externalId)) {
-      return -EINVAL;
-    }
-  }
   if (! iamPolicy.empty() &&
           (iamPolicy.size() < MIN_POLICY_SIZE || iamPolicy.size() > MAX_POLICY_SIZE)) {
     return -ERR_PACKED_POLICY_TOO_LARGE;
@@ -213,6 +201,34 @@ int AssumeRoleRequest::validate_input() const
       return -EINVAL;
     }
   }
+
+  return 0;
+}
+
+int AssumeRoleWithWebIdentityRequest::validate_input() const
+{
+  if (! providerId.empty()) {
+    if (providerId.length() < MIN_PROVIDER_ID_LEN ||
+          providerId.length() > MAX_PROVIDER_ID_LEN) {
+      return -EINVAL;
+    }
+  }
+  return AssumeRoleRequestBase::validate_input();
+}
+
+int AssumeRoleRequest::validate_input() const
+{
+  if (! externalId.empty()) {
+    if (externalId.length() < MIN_EXTERNAL_ID_LEN ||
+          externalId.length() > MAX_EXTERNAL_ID_LEN) {
+      return -EINVAL;
+    }
+
+    std::regex regex_externalId("[A-Za-z0-9_=,.@:/-]+");
+    if (! std::regex_match(externalId, regex_externalId)) {
+      return -EINVAL;
+    }
+  }
   if (! serialNumber.empty()){
     if (serialNumber.size() < MIN_SERIAL_NUMBER_SIZE || serialNumber.size() > MAX_SERIAL_NUMBER_SIZE) {
       return -EINVAL;
@@ -227,7 +243,7 @@ int AssumeRoleRequest::validate_input() const
     return -EINVAL;
   }
 
-  return 0;
+  return AssumeRoleRequestBase::validate_input();
 }
 
 std::tuple<int, RGWRole> STSService::getRoleInfo(const string& arn)
@@ -268,56 +284,114 @@ int STSService::storeARN(string& arn)
   return ret;
 }
 
+AssumeRoleWithWebIdentityResponse STSService::assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest& req)
+{
+  AssumeRoleWithWebIdentityResponse response;
+  response.assumeRoleResp.packedPolicySize = 0;
+
+  if (req.getProviderId().empty()) {
+    response.providerId = req.getIss();
+  }
+  response.aud = req.getAud();
+  response.sub = req.getSub();
+
+  //Get the role info which is being assumed
+  boost::optional<rgw::IAM::ARN> r_arn = rgw::IAM::ARN::parse(req.getRoleARN());
+  if (r_arn == boost::none) {
+    response.assumeRoleResp.retCode = -EINVAL;
+    return response;
+  }
+
+  string roleId = role.get_id();
+  uint64_t roleMaxSessionDuration = role.get_max_session_duration();
+  req.setMaxDuration(roleMaxSessionDuration);
+
+  //Validate input
+  response.assumeRoleResp.retCode = req.validate_input();
+  if (response.assumeRoleResp.retCode < 0) {
+    return response;
+  }
+
+  //Calculate PackedPolicySize
+  string policy = req.getPolicy();
+  response.assumeRoleResp.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
+
+  //Generate Assumed Role User
+  response.assumeRoleResp.retCode = response.assumeRoleResp.user.generateAssumedRoleUser(cct,
+                                                                                          store,
+                                                                                          roleId,
+                                                                                          r_arn.get(),
+                                                                                          req.getRoleSessionName());
+  if (response.assumeRoleResp.retCode < 0) {
+    return response;
+  }
+
+  //Generate Credentials
+  //Role and Policy provide the authorization info, user id and applier info are not needed
+  response.assumeRoleResp.retCode = response.assumeRoleResp.creds.generateCredentials(cct, req.getDuration(),
+                                                                                      req.getPolicy(), roleId,
+                                                                                      user_id, nullptr);
+  if (response.assumeRoleResp.retCode < 0) {
+    return response;
+  }
+
+  response.assumeRoleResp.retCode = 0;
+  return response;
+}
+
 AssumeRoleResponse STSService::assumeRole(AssumeRoleRequest& req)
 {
-  uint64_t packedPolicySize = 0, roleMaxSessionDuration = 0;
-  AssumedRoleUser user;
-  Credentials cred;
-  string roleId;
+  AssumeRoleResponse response;
+  response.packedPolicySize = 0;
 
   //Get the role info which is being assumed
-  boost::optional<rgw::IAM::ARN> r_arn;
-  if (r_arn = rgw::IAM::ARN::parse(req.getRoleARN()); r_arn == boost::none) {
-    return make_tuple(-EINVAL, user, cred, packedPolicySize);
+  boost::optional<rgw::IAM::ARN> r_arn = rgw::IAM::ARN::parse(req.getRoleARN());
+  if (r_arn == boost::none) {
+    response.retCode = -EINVAL;
+    return response;
   }
 
-  roleId = role.get_id();
-  roleMaxSessionDuration = role.get_max_session_duration();
+  string roleId = role.get_id();
+  uint64_t roleMaxSessionDuration = role.get_max_session_duration();
   req.setMaxDuration(roleMaxSessionDuration);
 
   //Validate input
-  int ret = 0;
-  if (ret = req.validate_input(); ret < 0) {
-    return make_tuple(ret, user, cred, packedPolicySize);
+  response.retCode = req.validate_input();
+  if (response.retCode < 0) {
+    return response;
   }
 
   //Calculate PackedPolicySize
   string policy = req.getPolicy();
-  packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
+  response.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
 
   //Generate Assumed Role User
-  if (ret = user.generateAssumedRoleUser(cct, store, roleId, r_arn.get(), req.getRoleSessionName()); ret < 0) {
-    return make_tuple(ret, user, cred, packedPolicySize);
+  response.retCode = response.user.generateAssumedRoleUser(cct, store, roleId, r_arn.get(), req.getRoleSessionName());
+  if (response.retCode < 0) {
+    return response;
   }
 
   //Generate Credentials
   //Role and Policy provide the authorization info, user id and applier info are not needed
-  if (ret = cred.generateCredentials(cct, req.getDuration(),
-                                      req.getPolicy(), roleId,
-                                      user_id, nullptr); ret < 0) {
-    return make_tuple(ret, user, cred, packedPolicySize);
+  response.retCode = response.creds.generateCredentials(cct, req.getDuration(),
+                                              req.getPolicy(), roleId,
+                                              user_id, nullptr);
+  if (response.retCode < 0) {
+    return response;
   }
 
   //Save ARN with the user
-  string arn = user.getARN();
-  if (ret = storeARN(arn); ret < 0) {
-    return make_tuple(ret, user, cred, packedPolicySize);
+  string arn = response.user.getARN();
+  response.retCode = storeARN(arn);
+  if (response.retCode < 0) {
+    return response;
   }
 
-  return make_tuple(0, user, cred, packedPolicySize);
+  response.retCode = 0;
+  return response;
 }
 
-GetSessionTokenRequest::GetSessionTokenRequest(string& duration, string& serialNumber, string& tokenCode)
+GetSessionTokenRequest::GetSessionTokenRequest(const string& duration, const string& serialNumber, const string& tokenCode)
 {
   if (duration.empty()) {
     this->duration = DEFAULT_DURATION_IN_SECS;
index 2c46b64e49e1ae0a639d8d6a67b42aa0b73c5ed8..89d66e2d15eb8325af5bde6fbd3409c98c05f81e 100644 (file)
@@ -6,45 +6,84 @@
 
 #include "rgw_role.h"
 #include "rgw_auth.h"
+#include "rgw_web_idp.h"
 
 namespace STS {
 
-class AssumeRoleRequest {
+class AssumeRoleRequestBase {
+protected:
   static constexpr uint64_t MIN_POLICY_SIZE = 1;
   static constexpr uint64_t MAX_POLICY_SIZE = 2048;
   static constexpr uint64_t DEFAULT_DURATION_IN_SECS = 3600;
   static constexpr uint64_t MIN_DURATION_IN_SECS = 900;
-  static constexpr uint64_t MIN_EXTERNAL_ID_LEN = 2;
-  static constexpr uint64_t MAX_EXTERNAL_ID_LEN = 1224;
   static constexpr uint64_t MIN_ROLE_ARN_SIZE = 2;
   static constexpr uint64_t MAX_ROLE_ARN_SIZE = 2048;
   static constexpr uint64_t MIN_ROLE_SESSION_SIZE = 2;
   static constexpr uint64_t MAX_ROLE_SESSION_SIZE = 64;
-  static constexpr uint64_t MIN_SERIAL_NUMBER_SIZE = 9;
-  static constexpr uint64_t MAX_SERIAL_NUMBER_SIZE = 256;
-  static constexpr uint64_t TOKEN_CODE_SIZE = 6;
   uint64_t MAX_DURATION_IN_SECS;
   uint64_t duration;
-  string externalId;
   string iamPolicy;
   string roleArn;
   string roleSessionName;
-  string serialNumber;
-  string tokenCode;
 public:
-  AssumeRoleRequest( string& _duration,
-                      string& _externalId,
-                      string& _iamPolicy,
-                      string& _roleArn,
-                      string& _roleSessionName,
-                      string& _serialNumber,
-                      string& _tokenCode);
+  AssumeRoleRequestBase(const string& duration,
+                        const string& iamPolicy,
+                        const string& roleArn,
+                        const string& roleSessionName);
   const string& getRoleARN() const { return roleArn; }
   const string& getRoleSessionName() const { return roleSessionName; }
   const string& getPolicy() const {return iamPolicy; }
   static const uint64_t& getMaxPolicySize() { return MAX_POLICY_SIZE; }
   void setMaxDuration(const uint64_t& maxDuration) { MAX_DURATION_IN_SECS = maxDuration; }
-  uint64_t& getDuration() { return duration; }
+  const uint64_t& getDuration() const { return duration; }
+  int validate_input() const;
+};
+
+class AssumeRoleWithWebIdentityRequest : public AssumeRoleRequestBase {
+  static constexpr uint64_t MIN_PROVIDER_ID_LEN = 4;
+  static constexpr uint64_t MAX_PROVIDER_ID_LEN = 2048;
+  string providerId;
+  string iamPolicy;
+  string iss;
+  string sub;
+  string aud;
+public:
+  AssumeRoleWithWebIdentityRequest( const string& duration,
+                      const string& providerId,
+                      const string& iamPolicy,
+                      const string& roleArn,
+                      const string& roleSessionName,
+                      const string& iss,
+                      const string& sub,
+                      const string& aud)
+    : AssumeRoleRequestBase(duration, iamPolicy, roleArn, roleSessionName),
+      providerId(providerId), iss(iss), sub(sub), aud(aud) {}
+  const string& getProviderId() const { return providerId; }
+  const string& getIss() const { return iss; }
+  const string& getAud() const { return aud; }
+  const string& getSub() const { return sub; }
+  int validate_input() const;
+};
+
+class AssumeRoleRequest : public AssumeRoleRequestBase {
+  static constexpr uint64_t MIN_EXTERNAL_ID_LEN = 2;
+  static constexpr uint64_t MAX_EXTERNAL_ID_LEN = 1224;
+  static constexpr uint64_t MIN_SERIAL_NUMBER_SIZE = 9;
+  static constexpr uint64_t MAX_SERIAL_NUMBER_SIZE = 256;
+  static constexpr uint64_t TOKEN_CODE_SIZE = 6;
+  string externalId;
+  string serialNumber;
+  string tokenCode;
+public:
+  AssumeRoleRequest(const string& duration,
+                    const string& externalId,
+                    const string& iamPolicy,
+                    const string& roleArn,
+                    const string& roleSessionName,
+                    const string& serialNumber,
+                    const string& tokenCode)
+    : AssumeRoleRequestBase(duration, iamPolicy, roleArn, roleSessionName),
+      externalId(externalId), serialNumber(serialNumber), tokenCode(tokenCode){}
   int validate_input() const;
 };
 
@@ -57,7 +96,7 @@ protected:
   string tokenCode;
 
 public:
-  GetSessionTokenRequest(string& duration, string& serialNumber, string& tokenCode);
+  GetSessionTokenRequest(const string& duration, const string& serialNumber, const string& tokenCode);
 
   const uint64_t& getDuration() const { return duration; }
   static const uint64_t& getMinDuration() { return MIN_DURATION_IN_SECS; }
@@ -144,9 +183,23 @@ public:
   void dump(Formatter *f) const;
 };
 
-//AssumedRoleUser, Credentials, PackedpolicySize
-using AssumeRoleResponse = std::tuple<int, AssumedRoleUser, Credentials, uint64_t> ;
+struct AssumeRoleResponse {
+  int retCode;
+  AssumedRoleUser user;
+  Credentials creds;
+  uint64_t packedPolicySize;
+};
+
+struct AssumeRoleWithWebIdentityResponse {
+  AssumeRoleResponse assumeRoleResp;
+  string aud;
+  string providerId;
+  string sub;
+};
+
+using AssumeRoleResponse = struct AssumeRoleResponse ;
 using GetSessionTokenResponse = std::tuple<int, Credentials>;
+using AssumeRoleWithWebIdentityResponse = struct AssumeRoleWithWebIdentityResponse;
 
 class STSService {
   CephContext* cct;
@@ -161,6 +214,7 @@ public:
   std::tuple<int, RGWRole> getRoleInfo(const string& arn);
   AssumeRoleResponse assumeRole(AssumeRoleRequest& req);
   GetSessionTokenResponse getSessionToken(GetSessionTokenRequest& req);
+  AssumeRoleWithWebIdentityResponse assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest& req);
 };
 }
 #endif /* CEPH_RGW_STS_H */
diff --git a/src/rgw/rgw_web_idp.h b/src/rgw/rgw_web_idp.h
new file mode 100644 (file)
index 0000000..b357338
--- /dev/null
@@ -0,0 +1,29 @@
+#ifndef CEPH_RGW_WEB_IDP_H
+#define CEPH_RGW_WEB_IDP_H
+
+#include <utility>
+#include <boost/optional.hpp>
+#include <boost/utility/string_view.hpp>
+
+#include "rgw_auth.h"
+#include "rgw_common.h"
+
+namespace rgw {
+namespace web_idp {
+
+//WebToken contains some claims from the decoded token which are of interest to us.
+struct WebTokenClaims {
+    //Subject of the token
+    string sub;
+    //Intended audience for this token
+    string aud;
+    //Issuer of this token
+    string iss;
+    //Human-readable id for the resource owner
+    string user_name;
+};
+
+}; /* namespace web_idp */
+}; /* namespace rgw */
+
+#endif /* CEPH_RGW_WEB_IDP_H */
index d629baf5936e1806a0e0bf2091f63f59fa1ff87b..a42360030ab795e84cf9581eb2df28837e1699b0 100644 (file)
@@ -815,34 +815,38 @@ TEST_F(IPPolicyTest, IPEnvironment) {
   rgw_env.set("REMOTE_ADDR", "192.168.1.1");
   rgw_env.set("HTTP_HOST", "1.2.3.4");
   req_state rgw_req_state(cct.get(), &rgw_env, &user, 0);
-  Environment iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
-  auto ip = iam_env.find("aws:SourceIp");
-  ASSERT_NE(ip, iam_env.end());
+  rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+  auto ip = rgw_req_state.env.find("aws:SourceIp");
+  ASSERT_NE(ip, rgw_req_state.env.end());
   EXPECT_EQ(ip->second, "192.168.1.1");
 
   ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "SOME_VAR"), 0);
   EXPECT_EQ(cct.get()->_conf->rgw_remote_addr_param, "SOME_VAR");
-  iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
-  ip = iam_env.find("aws:SourceIp");
-  EXPECT_EQ(ip, iam_env.end());
+  rgw_req_state.env.clear();
+  rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+  ip = rgw_req_state.env.find("aws:SourceIp");
+  EXPECT_EQ(ip, rgw_req_state.env.end());
 
   rgw_env.set("SOME_VAR", "192.168.1.2");
-  iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
-  ip = iam_env.find("aws:SourceIp");
-  ASSERT_NE(ip, iam_env.end());
+  rgw_req_state.env.clear();
+  rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+  ip = rgw_req_state.env.find("aws:SourceIp");
+  ASSERT_NE(ip, rgw_req_state.env.end());
   EXPECT_EQ(ip->second, "192.168.1.2");
 
   ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "HTTP_X_FORWARDED_FOR"), 0);
   rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.3");
-  iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
-  ip = iam_env.find("aws:SourceIp");
-  ASSERT_NE(ip, iam_env.end());
+  rgw_req_state.env.clear();
+  rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+  ip = rgw_req_state.env.find("aws:SourceIp");
+  ASSERT_NE(ip, rgw_req_state.env.end());
   EXPECT_EQ(ip->second, "192.168.1.3");
 
   rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.4, 4.3.2.1, 2001:db8:85a3:8d3:1319:8a2e:370:7348");
-  iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
-  ip = iam_env.find("aws:SourceIp");
-  ASSERT_NE(ip, iam_env.end());
+  rgw_req_state.env.clear();
+  rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+  ip = rgw_req_state.env.find("aws:SourceIp");
+  ASSERT_NE(ip, rgw_req_state.env.end());
   EXPECT_EQ(ip->second, "192.168.1.4");
 }