]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: refactor service handlers
authorYuval Lifshitz <ylifshit@redhat.com>
Thu, 8 Dec 2022 18:22:59 +0000 (18:22 +0000)
committerYuval Lifshitz <ylifshit@redhat.com>
Wed, 25 Jan 2023 06:05:21 +0000 (08:05 +0200)
seperate between the different non-bucket handler operations:
iam, sts, sns (topic) and non bucket s3 ops

Signed-off-by: Yuval Lifshitz <ylifshit@redhat.com>
13 files changed:
src/rgw/driver/rados/rgw_rest_pubsub.cc
src/rgw/driver/rados/rgw_rest_pubsub.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rest.cc
src/rgw/rgw_rest_iam.cc
src/rgw/rgw_rest_iam.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h
src/rgw/rgw_rest_sts.cc
src/rgw/rgw_rest_sts.h

index 23d56615ac953297a61b12898ea526d7b56830a8..86c18bf743975b3e3c786ea1709b7785f59f5460 100644 (file)
@@ -473,135 +473,39 @@ void RGWPSDeleteTopicOp::execute(optional_yield y) {
   ldpp_dout(this, 1) << "successfully removed topic '" << topic_name << "'" << dendl;
 }
 
-namespace {
-// utility classes and functions for handling parameters with the following format:
-// Attributes.entry.{N}.{key|value}={VALUE}
-// N - any unsigned number
-// VALUE - url encoded string
-
-// and Attribute is holding key and value
-// ctor and set are done according to the "type" argument
-// if type is not "key" or "value" its a no-op
-class Attribute {
-  std::string key;
-  std::string value;
-public:
-  Attribute(const std::string& type, const std::string& key_or_value) {
-    set(type, key_or_value);
-  }
-  void set(const std::string& type, const std::string& key_or_value) {
-    if (type == "key") {
-      key = key_or_value;
-    } else if (type == "value") {
-      value = key_or_value;
-    }
-  }
-  const std::string& get_key() const { return key; }
-  const std::string& get_value() const { return value; }
+using op_generator = RGWOp*(*)();
+static const std::unordered_map<std::string, op_generator> op_generators = {
+  {"CreateTopic", []() -> RGWOp* {return new RGWPSCreateTopicOp;}},
+  {"DeleteTopic", []() -> RGWOp* {return new RGWPSDeleteTopicOp;}},
+  {"ListTopics", []() -> RGWOp* {return new RGWPSListTopicsOp;}},
+  {"GetTopic", []() -> RGWOp* {return new RGWPSGetTopicOp;}},
+  {"GetTopicAttributes", []() -> RGWOp* {return new RGWPSGetTopicAttributesOp;}}
 };
 
-using AttributeMap = std::map<unsigned, Attribute>;
-
-// aggregate the attributes into a map
-// the key and value are associated by the index (N)
-// no assumptions are made on the order in which these parameters are added
-void update_attribute_map(const std::string& input, AttributeMap& map) {
-  const boost::char_separator<char> sep(".");
-  const boost::tokenizer tokens(input, sep);
-  auto token = tokens.begin();
-  if (*token != "Attributes") {
-      return;
-  }
-  ++token;
-
-  if (*token != "entry") {
-      return;
-  }
-  ++token;
-
-  unsigned idx;
-  try {
-    idx = std::stoul(*token);
-  } catch (const std::invalid_argument&) {
-    return;
-  }
-  ++token;
-
-  std::string key_or_value = "";
-  // get the rest of the string regardless of dots
-  // this is to allow dots in the value
-  while (token != tokens.end()) {
-    key_or_value.append(*token+".");
-    ++token;
-  }
-  // remove last separator
-  key_or_value.pop_back();
-
-  auto pos = key_or_value.find("=");
-  if (pos != std::string::npos) {
-    const auto key_or_value_lhs = key_or_value.substr(0, pos);
-    const auto key_or_value_rhs = url_decode(key_or_value.substr(pos + 1, key_or_value.size() - 1));
-    const auto map_it = map.find(idx);
-    if (map_it == map.end()) {
-      // new entry
-      map.emplace(std::make_pair(idx, Attribute(key_or_value_lhs, key_or_value_rhs)));
-    } else {
-      // existing entry
-      map_it->second.set(key_or_value_lhs, key_or_value_rhs);
-    }
-  }
-}
-}
-
-void RGWHandler_REST_PSTopic_AWS::rgw_topic_parse_input() {
-  if (post_body.size() > 0) {
-    ldpp_dout(s, 10) << "Content of POST: " << post_body << dendl;
-
-    if (post_body.find("Action") != std::string::npos) {
-      const boost::char_separator<char> sep("&");
-      const boost::tokenizer<boost::char_separator<char>> tokens(post_body, sep);
-      AttributeMap map;
-      for (const auto& t : tokens) {
-        auto pos = t.find("=");
-        if (pos != std::string::npos) {
-          const auto key = t.substr(0, pos);
-          if (key == "Action") {
-            s->info.args.append(key, t.substr(pos + 1, t.size() - 1));
-          } else if (key == "Name" || key == "TopicArn") {
-            const auto value = url_decode(t.substr(pos + 1, t.size() - 1));
-            s->info.args.append(key, value);
-          } else {
-            update_attribute_map(t, map);
-          }
-        }
-      }
-      // update the regular args with the content of the attribute map
-      for (const auto& attr : map) {
-          s->info.args.append(attr.second.get_key(), attr.second.get_value());
-      }
-    }
-    const auto payload_hash = rgw::auth::s3::calc_v4_payload_hash(post_body);
-    s->info.args.append("PayloadHash", payload_hash);
+bool RGWHandler_REST_PSTopic_AWS::action_exists(const req_state* s) 
+{
+  if (s->info.args.exists("Action")) {
+    const std::string action_name = s->info.args.get("Action");
+    return op_generators.contains(action_name);
   }
+  return false;
 }
 
-RGWOp* RGWHandler_REST_PSTopic_AWS::op_post() {
-  rgw_topic_parse_input();
+RGWOp *RGWHandler_REST_PSTopic_AWS::op_post()
+{
+  s->dialect = "sns";
+  s->prot_flags = RGW_REST_STS;
 
   if (s->info.args.exists("Action")) {
-    const auto action = s->info.args.get("Action");
-    if (action.compare("CreateTopic") == 0)
-      return new RGWPSCreateTopicOp();
-    if (action.compare("DeleteTopic") == 0)
-      return new RGWPSDeleteTopicOp;
-    if (action.compare("ListTopics") == 0)
-      return new RGWPSListTopicsOp();
-    if (action.compare("GetTopic") == 0)
-      return new RGWPSGetTopicOp();
-    if (action.compare("GetTopicAttributes") == 0)
-      return new RGWPSGetTopicAttributesOp();
+    const std::string action_name = s->info.args.get("Action");
+    const auto action_it = op_generators.find(action_name);
+    if (action_it != op_generators.end()) {
+      return action_it->second();
+    }
+    ldpp_dout(s, 10) << "unknown action '" << action_name << "' for Topic handler" << dendl;
+  } else {
+    ldpp_dout(s, 10) << "missing action argument in Topic handler" << dendl;
   }
-
   return nullptr;
 }
 
index 3b1a1bc9670b8e92d3bf24a5c18d28badd0dbf12..27bde7a95d5af781c8be28fb9b7f8162fc408c5b 100644 (file)
@@ -25,15 +25,14 @@ public:
 // AWS compliant topics handler factory
 class RGWHandler_REST_PSTopic_AWS : public RGWHandler_REST {
   const rgw::auth::StrategyRegistry& auth_registry;
-  const std::string& post_body;
-  void rgw_topic_parse_input();
 protected:
   RGWOp* op_post() override;
 public:
-  RGWHandler_REST_PSTopic_AWS(const rgw::auth::StrategyRegistry& _auth_registry, const std::string& _post_body) : 
-      auth_registry(_auth_registry),
-      post_body(_post_body) {}
+  RGWHandler_REST_PSTopic_AWS(const rgw::auth::StrategyRegistry& _auth_registry) : 
+      auth_registry(_auth_registry) {}
   virtual ~RGWHandler_REST_PSTopic_AWS() = default;
   int postauth_init(optional_yield) override { return 0; }
   int authorize(const DoutPrefixProvider* dpp, optional_yield y) override;
+  static bool action_exists(const req_state* s);
 };
+
index 31aa6f92a188d27cbcded4488cdedb21bf4e666c..c9a1c0fcdc7811c92d3844fee11aa37eceba9459 100644 (file)
@@ -1144,7 +1144,8 @@ bool verify_user_permission(const DoutPrefixProvider* dpp,
                             const vector<rgw::IAM::Policy>& user_policies,
                             const vector<rgw::IAM::Policy>& session_policies,
                             const rgw::ARN& res,
-                            const uint64_t op)
+                            const uint64_t op,
+                            bool mandatory_policy)
 {
   auto identity_policy_res = eval_identity_or_session_policies(dpp, user_policies, s->env, op, res);
   if (identity_policy_res == Effect::Deny) {
@@ -1167,13 +1168,15 @@ bool verify_user_permission(const DoutPrefixProvider* dpp,
     return true;
   }
 
-  if (op == rgw::IAM::s3CreateBucket || op == rgw::IAM::s3ListAllMyBuckets) {
-    auto perm = op_to_perm(op);
-
-    return verify_user_permission_no_policy(dpp, s, user_acl, perm);
+  if (mandatory_policy) {
+    // no policies, and policy is mandatory
+    ldpp_dout(dpp, 20) << "no policies for a policy mandatory op " << op << dendl;
+    return false;
   }
 
-  return false;
+  auto perm = op_to_perm(op);
+
+  return verify_user_permission_no_policy(dpp, s, user_acl, perm);
 }
 
 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
@@ -1197,10 +1200,11 @@ bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
 bool verify_user_permission(const DoutPrefixProvider* dpp,
                             req_state * const s,
                             const rgw::ARN& res,
-                            const uint64_t op)
+                            const uint64_t op,
+                            bool mandatory_policy)
 {
   perm_state_from_req_state ps(s);
-  return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, s->session_policies, res, op);
+  return verify_user_permission(dpp, &ps, s->user_acl.get(), s->iam_user_policies, s->session_policies, res, op, mandatory_policy);
 }
 
 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp, 
index 3a5f2cd8824ded45575c4e65dc25973ddcb1c983..a8851f2ebb70fb0ffb04d45e90356fe9868c75f3 100644 (file)
@@ -198,6 +198,7 @@ static inline const char* to_mime_type(const RGWFormat f)
 #define RGW_REST_WEBSITE     0x8
 #define RGW_REST_STS            0x10
 #define RGW_REST_IAM            0x20
+#define RGW_REST_SNS            0x30
 
 #define RGW_SUSPENDED_USER_AUID (uint64_t)-2
 
@@ -1582,7 +1583,8 @@ bool verify_user_permission(const DoutPrefixProvider* dpp,
                             const std::vector<rgw::IAM::Policy>& user_policies,
                             const std::vector<rgw::IAM::Policy>& session_policies,
                             const rgw::ARN& res,
-                            const uint64_t op);
+                            const uint64_t op,
+                            bool mandatory_policy=true);
 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
                                       req_state * const s,
                                       RGWAccessControlPolicy * const user_acl,
@@ -1590,7 +1592,8 @@ bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
 bool verify_user_permission(const DoutPrefixProvider* dpp,
                             req_state * const s,
                             const rgw::ARN& res,
-                            const uint64_t op);
+                            const uint64_t op,
+                            bool mandatory_policy=true);
 bool verify_user_permission_no_policy(const DoutPrefixProvider* dpp,
                                       req_state * const s,
                                       int perm);
index 5bb0d398822546d97a5176f7e359cb59e9d57d27..5527c1c371a7948faf5d60b99f4639454b3f0c8d 100644 (file)
@@ -2396,7 +2396,7 @@ int RGWListBuckets::verify_permission(optional_yield y)
     tenant = s->user->get_tenant();
   }
 
-  if (!verify_user_permission(this, s, ARN(partition, service, "", tenant, "*"), rgw::IAM::s3ListAllMyBuckets)) {
+  if (!verify_user_permission(this, s, ARN(partition, service, "", tenant, "*"), rgw::IAM::s3ListAllMyBuckets, false)) {
     return -EACCES;
   }
 
@@ -3015,7 +3015,7 @@ int RGWCreateBucket::verify_permission(optional_yield y)
   bucket.name = s->bucket_name;
   bucket.tenant = s->bucket_tenant;
   ARN arn = ARN(bucket);
-  if (!verify_user_permission(this, s, arn, rgw::IAM::s3CreateBucket)) {
+  if (!verify_user_permission(this, s, arn, rgw::IAM::s3CreateBucket, false)) {
     return -EACCES;
   }
 
index d0ff70b3132dd34646a8c62d01b8fa8cfe219273..941f176234b10d1ad69a73ae932c5a30857e6e71 100644 (file)
@@ -183,6 +183,7 @@ protected:
   RGWQuota quota;
   int op_ret;
   int do_aws4_auth_completion();
+  bool init_called = false;
 
   virtual int init_quota();
 
@@ -234,7 +235,9 @@ public:
   }
 
   virtual void init(rgw::sal::Driver* driver, req_state *s, RGWHandler *dialect_handler) {
+    if (init_called) return;
     this->driver = driver;
+    init_called = true;
     this->s = s;
     this->dialect_handler = dialect_handler;
   }
index af5a40cc47186e577711ca2c4a03ab243e1b7f68..54a39a9a549a7b7464d4c66534cc809f304b1060 100644 (file)
@@ -2324,6 +2324,9 @@ RGWHandler_REST* RGWREST::get_handler(
     *init_error = -ERR_METHOD_NOT_ALLOWED;
     return NULL;
   }
+
+  ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name() << dendl;
+  
   *init_error = handler->init(driver, s, rio);
   if (*init_error < 0) {
     m->put_handler(handler);
index 9560e04c82bbd79ff1edcc6112a61aa1405dbf46..b9e8779c10a472c80eee12c5692468976c5f9241 100644 (file)
 
 using namespace std;
 
-void RGWHandler_REST_IAM::rgw_iam_parse_input()
+using op_generator = RGWOp*(*)(const bufferlist&);
+static const std::unordered_map<std::string_view, op_generator> op_generators = {
+  {"CreateRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWCreateRole(bl_post_body);}},
+  {"DeleteRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWDeleteRole(bl_post_body);}},
+  {"GetRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWGetRole;}},
+  {"UpdateAssumeRolePolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWModifyRoleTrustPolicy(bl_post_body);}},
+  {"ListRoles", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListRoles;}},
+  {"PutRolePolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWPutRolePolicy(bl_post_body);}},
+  {"GetRolePolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWGetRolePolicy;}},
+  {"ListRolePolicies", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListRolePolicies;}},
+  {"DeleteRolePolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWDeleteRolePolicy(bl_post_body);}},
+  {"PutUserPolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWPutUserPolicy;}},
+  {"GetUserPolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWGetUserPolicy;}},
+  {"ListUserPolicies", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListUserPolicies;}},
+  {"DeleteUserPolicy", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWDeleteUserPolicy;}},
+  {"CreateOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWCreateOIDCProvider;}},
+  {"ListOpenIDConnectProviders", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListOIDCProviders;}},
+  {"GetOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWGetOIDCProvider;}},
+  {"DeleteOpenIDConnectProvider", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWDeleteOIDCProvider;}},
+  {"TagRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWTagRole(bl_post_body);}},
+  {"ListRoleTags", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWListRoleTags;}},
+  {"UntagRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWUntagRole(bl_post_body);}},
+  {"UpdateRole", [](const bufferlist& bl_post_body) -> RGWOp* {return new RGWUpdateRole(bl_post_body);}}
+};
+
+bool RGWHandler_REST_IAM::action_exists(const req_state* s) 
 {
-  std::string post_body = bl_post_body.to_str();
-  if (post_body.size() > 0) {
-    ldpp_dout(s, 10) << "Content of POST: " << post_body << dendl;
-
-    if (post_body.find("Action") != string::npos) {
-      boost::char_separator<char> sep("&");
-      boost::tokenizer<boost::char_separator<char>> tokens(post_body, sep);
-      for (const auto& t : tokens) {
-        auto pos = t.find("=");
-        if (pos != string::npos) {
-          s->info.args.append(t.substr(0,pos),
-                              url_decode(t.substr(pos+1, t.size() -1)));
-        }
-      }
-    }
+  if (s->info.args.exists("Action")) {
+    const std::string action_name = s->info.args.get("Action");
+    return op_generators.contains(action_name);
   }
-  auto payload_hash = rgw::auth::s3::calc_v4_payload_hash(post_body);
-  s->info.args.append("PayloadHash", payload_hash);
+  return false;
 }
 
 RGWOp *RGWHandler_REST_IAM::op_post()
 {
-  rgw_iam_parse_input();
-
   if (s->info.args.exists("Action")) {
-    string action = s->info.args.get("Action");
-    if (action.compare("CreateRole") == 0)
-      return new RGWCreateRole(this->bl_post_body);
-    if (action.compare("DeleteRole") == 0)
-      return new RGWDeleteRole(this->bl_post_body);
-    if (action.compare("GetRole") == 0)
-      return new RGWGetRole;
-    if (action.compare("UpdateAssumeRolePolicy") == 0)
-      return new RGWModifyRoleTrustPolicy(this->bl_post_body);
-    if (action.compare("ListRoles") == 0)
-      return new RGWListRoles;
-    if (action.compare("PutRolePolicy") == 0)
-      return new RGWPutRolePolicy(this->bl_post_body);
-    if (action.compare("GetRolePolicy") == 0)
-      return new RGWGetRolePolicy;
-    if (action.compare("ListRolePolicies") == 0)
-      return new RGWListRolePolicies;
-    if (action.compare("DeleteRolePolicy") == 0)
-      return new RGWDeleteRolePolicy(this->bl_post_body);
-    if (action.compare("PutUserPolicy") == 0)
-      return new RGWPutUserPolicy;
-    if (action.compare("GetUserPolicy") == 0)
-      return new RGWGetUserPolicy;
-    if (action.compare("ListUserPolicies") == 0)
-      return new RGWListUserPolicies;
-    if (action.compare("DeleteUserPolicy") == 0)
-      return new RGWDeleteUserPolicy;
-    if (action.compare("CreateOpenIDConnectProvider") == 0)
-      return new RGWCreateOIDCProvider;
-    if (action.compare("ListOpenIDConnectProviders") == 0)
-      return new RGWListOIDCProviders;
-    if (action.compare("GetOpenIDConnectProvider") == 0)
-      return new RGWGetOIDCProvider;
-    if (action.compare("DeleteOpenIDConnectProvider") == 0)
-      return new RGWDeleteOIDCProvider;
-    if (action.compare("TagRole") == 0)
-      return new RGWTagRole(this->bl_post_body);
-    if (action.compare("ListRoleTags") == 0)
-      return new RGWListRoleTags;
-    if (action.compare("UntagRole") == 0)
-      return new RGWUntagRole(this->bl_post_body);
-    if (action.compare("UpdateRole") == 0)
-      return new RGWUpdateRole(this->bl_post_body);
+    const std::string action_name = s->info.args.get("Action");
+    const auto action_it = op_generators.find(action_name);
+    if (action_it != op_generators.end()) {
+      return action_it->second(bl_post_body);
+    }
+    ldpp_dout(s, 10) << "unknown action '" << action_name << "' for IAM handler" << dendl;
+  } else {
+    ldpp_dout(s, 10) << "missing action argument in IAM handler" << dendl;
   }
-
   return nullptr;
 }
 
@@ -95,11 +69,7 @@ int RGWHandler_REST_IAM::init(rgw::sal::Driver* driver,
                               rgw::io::BasicClient *cio)
 {
   s->dialect = "iam";
-
-  if (int ret = RGWHandler_REST_IAM::init_from_header(s, RGWFormat::XML, true); ret < 0) {
-    ldpp_dout(s, 10) << "init_from_header returned err=" << ret <<  dendl;
-    return ret;
-  }
+  s->prot_flags = RGW_REST_IAM;
 
   return RGWHandler_REST::init(driver, s, cio);
 }
@@ -109,48 +79,6 @@ int RGWHandler_REST_IAM::authorize(const DoutPrefixProvider* dpp, optional_yield
   return RGW_Auth_S3::authorize(dpp, driver, auth_registry, s, y);
 }
 
-int RGWHandler_REST_IAM::init_from_header(req_state* s,
-                                          RGWFormat default_formatter,
-                                          bool configurable_format)
-{
-  string req;
-  string first;
-
-  s->prot_flags = RGW_REST_IAM;
-
-  const char *p, *req_name;
-  if (req_name = s->relative_uri.c_str(); *req_name == '?') {
-    p = req_name;
-  } else {
-    p = s->info.request_params.c_str();
-  }
-
-  s->info.args.set(p);
-  s->info.args.parse(s);
-
-  /* must be called after the args parsing */
-  if (int ret = allocate_formatter(s, default_formatter, configurable_format); ret < 0)
-    return ret;
-
-  if (*req_name != '/')
-    return 0;
-
-  req_name++;
-
-  if (!*req_name)
-    return 0;
-
-  req = req_name;
-  int pos = req.find('/');
-  if (pos >= 0) {
-    first = req.substr(0, pos);
-  } else {
-    first = req;
-  }
-
-  return 0;
-}
-
 RGWHandler_REST*
 RGWRESTMgr_IAM::get_handler(rgw::sal::Driver* driver,
                            req_state* const s,
index 1a25362f4ae2c63d8831cbde0e798accb87da793..3e579ab35ce767521e61f533d1fd66479ef3066f 100644 (file)
@@ -11,10 +11,10 @@ class RGWHandler_REST_IAM : public RGWHandler_REST {
   const rgw::auth::StrategyRegistry& auth_registry;
   bufferlist bl_post_body;
   RGWOp *op_post() override;
-  void rgw_iam_parse_input();
+
 public:
 
-  static int init_from_header(req_state *s, RGWFormat default_formatter, bool configurable_format);
+  static bool action_exists(const req_state* s);
 
   RGWHandler_REST_IAM(const rgw::auth::StrategyRegistry& auth_registry,
                      bufferlist& bl_post_body)
index 932ab0226b96f83e94fb99cea58f47869284f933..fe14ca4269faaed184f6eaa23ae645c7ab4bd00b 100644 (file)
@@ -16,6 +16,7 @@
 #include "auth/Crypto.h"
 #include <boost/algorithm/string.hpp>
 #include <boost/algorithm/string/replace.hpp>
+#include <boost/algorithm/string/predicate.hpp>
 #include <boost/tokenizer.hpp>
 #define BOOST_BIND_GLOBAL_PLACEHOLDERS
 #ifdef HAVE_WARN_IMPLICIT_CONST_INT_FLOAT_CONVERSION
@@ -4479,49 +4480,6 @@ RGWOp *RGWHandler_REST_Service_S3::op_head()
   return new RGWListBuckets_ObjStore_S3;
 }
 
-RGWOp *RGWHandler_REST_Service_S3::op_post()
-{
-  const auto max_size = s->cct->_conf->rgw_max_put_param_size;
-
-  int ret;
-  bufferlist data;
-  std::tie(ret, data) = rgw_rest_read_all_input(s, max_size, false);
-  if (ret < 0) {
-      return nullptr;
-  }
-
-  const auto post_body = data.to_str();
-
-  if (isSTSEnabled) {
-    RGWHandler_REST_STS sts_handler(auth_registry, post_body);
-    sts_handler.init(driver, s, s->cio);
-    auto op = sts_handler.get_op();
-    if (op) {
-      return op;
-    }
-  }
-
-  if (isIAMEnabled) {
-    RGWHandler_REST_IAM iam_handler(auth_registry, data);
-    iam_handler.init(driver, s, s->cio);
-    auto op = iam_handler.get_op();
-    if (op) {
-      return op;
-    }
-  }
-
-  if (isPSEnabled) {
-    RGWHandler_REST_PSTopic_AWS topic_handler(auth_registry, post_body);
-    topic_handler.init(driver, s, s->cio);
-    auto op = topic_handler.get_op();
-    if (op) {
-      return op;
-    }
-  }
-
-  return nullptr;
-}
-
 RGWOp *RGWHandler_REST_Bucket_S3::get_obj_op(bool get_data) const
 {
   // Non-website mode
@@ -5065,6 +5023,117 @@ int RGWHandler_Auth_S3::init(rgw::sal::Driver* driver, req_state *state,
   return RGWHandler_REST::init(driver, state, cio);
 }
 
+namespace {
+// utility classes and functions for handling parameters with the following format:
+// Attributes.entry.{N}.{key|value}={VALUE}
+// N - any unsigned number
+// VALUE - url encoded string
+
+// and Attribute is holding key and value
+// ctor and set are done according to the "type" argument
+// if type is not "key" or "value" its a no-op
+class Attribute {
+  std::string key;
+  std::string value;
+public:
+  Attribute(const std::string& type, const std::string& key_or_value) {
+    set(type, key_or_value);
+  }
+  void set(const std::string& type, const std::string& key_or_value) {
+    if (type == "key") {
+      key = key_or_value;
+    } else if (type == "value") {
+      value = key_or_value;
+    }
+  }
+  const std::string& get_key() const { return key; }
+  const std::string& get_value() const { return value; }
+};
+
+using AttributeMap = std::map<unsigned, Attribute>;
+
+// aggregate the attributes into a map
+// the key and value are associated by the index (N)
+// no assumptions are made on the order in which these parameters are added
+void update_attribute_map(const std::string& input, AttributeMap& map) {
+  const boost::char_separator<char> sep(".");
+  const boost::tokenizer tokens(input, sep);
+  auto token = tokens.begin();
+  if (*token != "Attributes") {
+      return;
+  }
+  ++token;
+
+  if (*token != "entry") {
+      return;
+  }
+  ++token;
+
+  unsigned idx;
+  try {
+    idx = std::stoul(*token);
+  } catch (const std::invalid_argument&) {
+    return;
+  }
+  ++token;
+
+  std::string key_or_value = "";
+  // get the rest of the string regardless of dots
+  // this is to allow dots in the value
+  while (token != tokens.end()) {
+    key_or_value.append(*token+".");
+    ++token;
+  }
+  // remove last separator
+  key_or_value.pop_back();
+
+  auto pos = key_or_value.find("=");
+  if (pos != std::string::npos) {
+    const auto key_or_value_lhs = key_or_value.substr(0, pos);
+    const auto key_or_value_rhs = url_decode(key_or_value.substr(pos + 1, key_or_value.size() - 1));
+    const auto map_it = map.find(idx);
+    if (map_it == map.end()) {
+      // new entry
+      map.emplace(std::make_pair(idx, Attribute(key_or_value_lhs, key_or_value_rhs)));
+    } else {
+      // existing entry
+      map_it->second.set(key_or_value_lhs, key_or_value_rhs);
+    }
+  }
+}
+}
+
+void parse_post_action(const std::string& post_body, req_state* s)
+{
+  if (post_body.size() > 0) {
+    ldpp_dout(s, 10) << "Content of POST: " << post_body << dendl;
+
+    if (post_body.find("Action") != string::npos) {
+      const boost::char_separator<char> sep("&");
+      const boost::tokenizer<boost::char_separator<char>> tokens(post_body, sep);
+      AttributeMap map;
+      for (const auto& t : tokens) {
+        const auto pos = t.find("=");
+        if (pos != string::npos) {
+          const auto key = t.substr(0, pos);
+          if (boost::starts_with(key, "Attributes.")) {
+            update_attribute_map(t, map);
+          } else {
+            s->info.args.append(t.substr(0, pos),
+                              url_decode(t.substr(pos+1, t.size() -1)));
+          }
+        }
+      }
+      // update the regular args with the content of the attribute map
+      for (const auto& attr : map) {
+          s->info.args.append(attr.second.get_key(), attr.second.get_value());
+      }
+    }
+  }
+  const auto payload_hash = rgw::auth::s3::calc_v4_payload_hash(post_body);
+  s->info.args.append("PayloadHash", payload_hash);
+}
+
 RGWHandler_REST* RGWRESTMgr_S3::get_handler(rgw::sal::Driver* driver,
                                            req_state* const s,
                                             const rgw::auth::StrategyRegistry& auth_registry,
@@ -5075,34 +5144,55 @@ RGWHandler_REST* RGWRESTMgr_S3::get_handler(rgw::sal::Driver* driver,
     RGWHandler_REST_S3::init_from_header(driver, s,
                                        is_s3website ? RGWFormat::HTML :
                                        RGWFormat::XML, true);
-  if (ret < 0)
-    return NULL;
+  if (ret < 0) {
+    return nullptr;
+  }
 
-  RGWHandler_REST* handler;
-  // TODO: Make this more readable
   if (is_s3website) {
     if (s->init_state.url_bucket.empty()) {
-      handler = new RGWHandler_REST_Service_S3Website(auth_registry);
-    } else if (rgw::sal::Object::empty(s->object.get())) {
-      handler = new RGWHandler_REST_Bucket_S3Website(auth_registry);
-    } else {
-      handler = new RGWHandler_REST_Obj_S3Website(auth_registry);
+      return new RGWHandler_REST_Service_S3Website(auth_registry);
     }
-  } else {
-    if (s->init_state.url_bucket.empty()) {
-      handler = new RGWHandler_REST_Service_S3(auth_registry, enable_sts, enable_iam, enable_pubsub);
-    } else if (!rgw::sal::Object::empty(s->object.get())) {
-      handler = new RGWHandler_REST_Obj_S3(auth_registry);
-    } else if (s->info.args.exist_obj_excl_sub_resource()) {
-      return NULL;
-    } else {
-      handler = new RGWHandler_REST_Bucket_S3(auth_registry, enable_pubsub);
+    if (rgw::sal::Object::empty(s->object.get())) {
+      return new RGWHandler_REST_Bucket_S3Website(auth_registry);
     }
+    return new RGWHandler_REST_Obj_S3Website(auth_registry);
   }
 
-  ldpp_dout(s, 20) << __func__ << " handler=" << typeid(*handler).name()
-                   << dendl;
-  return handler;
+  if (s->init_state.url_bucket.empty()) {
+    // no bucket
+    if (s->op == OP_POST) {
+      // POST will be one of: IAM, STS or topic service
+      const auto max_size = s->cct->_conf->rgw_max_put_param_size;
+      int ret;
+      bufferlist data;
+      std::tie(ret, data) = rgw_rest_read_all_input(s, max_size, false);
+      if (ret < 0) {
+        return nullptr;
+      }
+      parse_post_action(data.to_str(), s);
+      if (enable_sts && RGWHandler_REST_STS::action_exists(s)) {
+        return new RGWHandler_REST_STS(auth_registry);
+      }
+      if (enable_iam && RGWHandler_REST_IAM::action_exists(s)) {
+        return new RGWHandler_REST_IAM(auth_registry, data);
+      }
+      if (enable_pubsub && RGWHandler_REST_PSTopic_AWS::action_exists(s)) {
+        return new RGWHandler_REST_PSTopic_AWS(auth_registry); 
+      }
+      return nullptr;
+    }
+    // non-POST S3 service without a bucket
+    return new RGWHandler_REST_Service_S3(auth_registry);
+  }
+  if (!rgw::sal::Object::empty(s->object.get())) {
+    // has object
+    return new RGWHandler_REST_Obj_S3(auth_registry);
+  }
+  if (s->info.args.exist_obj_excl_sub_resource()) {
+    return nullptr;
+  }
+  // has bucket
+  return new RGWHandler_REST_Bucket_S3(auth_registry, enable_pubsub);
 }
 
 bool RGWHandler_REST_S3Website::web_dir() const {
index 7c1829aa7eb70fd8625172f43b57fd752b7fd385..f1b95c01e20ad62f76110994cfc10c0adb560d8a 100644 (file)
@@ -668,19 +668,14 @@ public:
 
 class RGWHandler_REST_Service_S3 : public RGWHandler_REST_S3 {
 protected:
-  const bool isSTSEnabled;
-  const bool isIAMEnabled;
-  const bool isPSEnabled;
   bool is_usage_op() const {
     return s->info.args.exists("usage");
   }
   RGWOp *op_get() override;
   RGWOp *op_head() override;
-  RGWOp *op_post() override;
 public:
-   RGWHandler_REST_Service_S3(const rgw::auth::StrategyRegistry& auth_registry,
-                              bool _isSTSEnabled, bool _isIAMEnabled, bool _isPSEnabled) :
-      RGWHandler_REST_S3(auth_registry), isSTSEnabled(_isSTSEnabled), isIAMEnabled(_isIAMEnabled), isPSEnabled(_isPSEnabled) {}
+   RGWHandler_REST_Service_S3(const rgw::auth::StrategyRegistry& auth_registry) :
+      RGWHandler_REST_S3(auth_registry) {}
   ~RGWHandler_REST_Service_S3() override = default;
 };
 
index b77b78c910197496b6ed5237c8c0e5b72d27e9a0..8e70faa99f4e1db735f2287ce8c5008c224384c6 100644 (file)
@@ -754,42 +754,34 @@ int RGW_Auth_STS::authorize(const DoutPrefixProvider *dpp,
   return rgw::auth::Strategy::apply(dpp, auth_registry.get_sts(), s, y);
 }
 
-void RGWHandler_REST_STS::rgw_sts_parse_input()
+using op_generator = RGWOp*(*)();
+static const std::unordered_map<std::string_view, op_generator> op_generators = {
+  {"AssumeRole", []() -> RGWOp* {return new RGWSTSAssumeRole;}},
+  {"GetSessionToken", []() -> RGWOp* {return new RGWSTSGetSessionToken;}},
+  {"AssumeRoleWithWebIdentity", []() -> RGWOp* {return new RGWSTSAssumeRoleWithWebIdentity;}}
+};
+
+bool RGWHandler_REST_STS::action_exists(const req_state* s) 
 {
-  if (post_body.size() > 0) {
-    ldpp_dout(s, 10) << "Content of POST: " << post_body << dendl;
-
-    if (post_body.find("Action") != string::npos) {
-      boost::char_separator<char> sep("&");
-      boost::tokenizer<boost::char_separator<char>> tokens(post_body, sep);
-      for (const auto& t : tokens) {
-        auto pos = t.find("=");
-        if (pos != string::npos) {
-          s->info.args.append(t.substr(0,pos),
-                              url_decode(t.substr(pos+1, t.size() -1)));
-        }
-      }
-    }
+  if (s->info.args.exists("Action")) {
+    const std::string action_name = s->info.args.get("Action");
+    return op_generators.contains(action_name);
   }
-  auto payload_hash = rgw::auth::s3::calc_v4_payload_hash(post_body);
-  s->info.args.append("PayloadHash", payload_hash);
+  return false;
 }
 
 RGWOp *RGWHandler_REST_STS::op_post()
 {
-  rgw_sts_parse_input();
-
-  if (s->info.args.exists("Action"))    {
-    string action = s->info.args.get("Action");
-    if (action == "AssumeRole") {
-      return new RGWSTSAssumeRole;
-    } else if (action == "GetSessionToken") {
-      return new RGWSTSGetSessionToken;
-    } else if (action == "AssumeRoleWithWebIdentity") {
-      return new RGWSTSAssumeRoleWithWebIdentity;
+  if (s->info.args.exists("Action")) {
+    const std::string action_name = s->info.args.get("Action");
+    const auto action_it = op_generators.find(action_name);
+    if (action_it != op_generators.end()) {
+      return action_it->second();
     }
+    ldpp_dout(s, 10) << "unknown action '" << action_name << "' for STS handler" << dendl;
+  } else {
+    ldpp_dout(s, 10) << "missing action argument in STS handler" << dendl;
   }
-
   return nullptr;
 }
 
@@ -798,11 +790,7 @@ int RGWHandler_REST_STS::init(rgw::sal::Driver* driver,
                               rgw::io::BasicClient *cio)
 {
   s->dialect = "sts";
-
-  if (int ret = RGWHandler_REST_STS::init_from_header(s, RGWFormat::XML, true); ret < 0) {
-    ldpp_dout(s, 10) << "init_from_header returned err=" << ret <<  dendl;
-    return ret;
-  }
+  s->prot_flags = RGW_REST_STS;
 
   return RGWHandler_REST::init(driver, s, cio);
 }
@@ -815,48 +803,6 @@ int RGWHandler_REST_STS::authorize(const DoutPrefixProvider* dpp, optional_yield
   return RGW_Auth_S3::authorize(dpp, driver, auth_registry, s, y);
 }
 
-int RGWHandler_REST_STS::init_from_header(req_state* s,
-                                          RGWFormat default_formatter,
-                                          bool configurable_format)
-{
-  string req;
-  string first;
-
-  s->prot_flags = RGW_REST_STS;
-
-  const char *p, *req_name;
-  if (req_name = s->relative_uri.c_str(); *req_name == '?') {
-    p = req_name;
-  } else {
-    p = s->info.request_params.c_str();
-  }
-
-  s->info.args.set(p);
-  s->info.args.parse(s);
-
-  /* must be called after the args parsing */
-  if (int ret = allocate_formatter(s, default_formatter, configurable_format); ret < 0)
-    return ret;
-
-  if (*req_name != '/')
-    return 0;
-
-  req_name++;
-
-  if (!*req_name)
-    return 0;
-
-  req = req_name;
-  int pos = req.find('/');
-  if (pos >= 0) {
-    first = req.substr(0, pos);
-  } else {
-    first = req;
-  }
-
-  return 0;
-}
-
 RGWHandler_REST*
 RGWRESTMgr_STS::get_handler(rgw::sal::Driver* driver,
                            req_state* const s,
index 9c9488c6c171c525b693af75c4a7c00b26728d1a..db17281fc740bf0160b0b27b90c54fccd311e0b2 100644 (file)
@@ -199,17 +199,14 @@ public:
 
 class RGWHandler_REST_STS : public RGWHandler_REST {
   const rgw::auth::StrategyRegistry& auth_registry;
-  const std::string& post_body;
   RGWOp *op_post() override;
-  void rgw_sts_parse_input();
 public:
 
-  static int init_from_header(req_state *s, RGWFormat default_formatter, bool configurable_format);
+  static bool action_exists(const req_state* s);
 
-  RGWHandler_REST_STS(const rgw::auth::StrategyRegistry& auth_registry, const std::string& post_body="")
+  RGWHandler_REST_STS(const rgw::auth::StrategyRegistry& auth_registry)
     : RGWHandler_REST(),
-      auth_registry(auth_registry),
-      post_body(post_body) {}
+      auth_registry(auth_registry) {}
   ~RGWHandler_REST_STS() override = default;
 
   int init(rgw::sal::Driver* driver,