]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw: Initial commit for User Policy REST APIs.
authorPritha Srivastava <prsrivas@redhat.com>
Mon, 26 Feb 2018 09:45:02 +0000 (15:15 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Fri, 29 Jun 2018 10:07:41 +0000 (15:37 +0530)
Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
src/rgw/CMakeLists.txt
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_rest_s3.cc
src/rgw/rgw_rest_user_policy.cc [new file with mode: 0644]
src/rgw/rgw_rest_user_policy.h [new file with mode: 0644]

index a973270811fb5527001a05e4c6ee877632886772..4a50930f744add203e413c735ec125002574da41 100644 (file)
@@ -109,7 +109,7 @@ set(librgw_common_srcs
   rgw_crypt.cc
   rgw_crypt_sanitize.cc
   rgw_iam_policy.cc
-)
+  rgw_rest_user_policy.cc)
 add_library(rgw_common OBJECT ${librgw_common_srcs})
 
 if(WITH_LTTNG)
index bdeb7aa45aa1e12b1ab4d71e16b3dc1779a38c8a..ad409d392c0438d520ac102d1d9620175f2cfb35 100644 (file)
@@ -95,6 +95,7 @@ rgw_http_errors rgw_http_s3_errors({
     { ERR_NO_SUCH_USER, {404, "NoSuchUser"}},
     { ERR_NO_ROLE_FOUND, {404, "NoSuchEntity"}},
     { ERR_NO_SUCH_SUBUSER, {404, "NoSuchSubUser"}},
+    { ERR_NO_SUCH_ENTITY, {404, "NoSuchEntity"}},
     { ERR_METHOD_NOT_ALLOWED, {405, "MethodNotAllowed" }},
     { ETIMEDOUT, {408, "RequestTimeout" }},
     { EEXIST, {409, "BucketAlreadyExists" }},
@@ -1729,7 +1730,8 @@ bool RGWUserCaps::is_valid_cap_type(const string& tp)
                                     "mdlog",
                                     "datalog",
                                     "opstate",
-                                    "roles"};
+                                    "roles",
+                                    "user-policy"};
 
   for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) {
     if (tp.compare(cap_type[i]) == 0) {
index d5c0e7e15d7ef12e02f7cd9a4d1d7ddfa0e769c0..969106020438b5750dda994f5d486f88d198dbec 100644 (file)
@@ -106,7 +106,7 @@ using ceph::crypto::MD5;
 
 /* IAM Policy */
 #define RGW_ATTR_IAM_POLICY    RGW_ATTR_PREFIX "iam-policy"
-
+#define RGW_ATTR_USER_POLICY    RGW_ATTR_PREFIX "user-policy"
 
 /* RGW File Attributes */
 #define RGW_ATTR_UNIX_KEY1      RGW_ATTR_PREFIX "unix-key1"
@@ -219,7 +219,7 @@ using ceph::crypto::MD5;
 #define ERR_INVALID_ENCRYPTION_ALGORITHM                 2214
 
 #define ERR_BUSY_RESHARDING      2300
-
+#define ERR_NO_SUCH_ENTITY       2301
 #ifndef UINT32_MAX
 #define UINT32_MAX (0xffffffffu)
 #endif
@@ -501,6 +501,10 @@ enum RGWOpType {
   RGW_OP_PUT_LC,
   RGW_OP_GET_LC,
   RGW_OP_DELETE_LC,
+  RGW_OP_PUT_USER_POLICY,
+  RGW_OP_GET_USER_POLICY,
+  RGW_OP_LIST_USER_POLICIES,
+  RGW_OP_DELETE_USER_POLICY,
   /* rgw specific */
   RGW_OP_ADMIN_SET_METADATA,
   RGW_OP_GET_OBJ_LAYOUT,
index a585f8b5e9aef3a5eb7888f78c3af7ff4c0426d2..2eb60879bf56c97c57f63554449f873b5311bc1f 100644 (file)
@@ -443,7 +443,10 @@ static const actpair actpairs[] =
  { "s3:PutObjectTagging", s3PutObjectTagging },
  { "s3:PutObjectVersionTagging", s3PutObjectVersionTagging },
  { "s3:PutReplicationConfiguration", s3PutReplicationConfiguration },
- { "s3:RestoreObject", s3RestoreObject }};
+ { "s3:RestoreObject", s3RestoreObject },
+ { "iam:PutUserPolicy", iamPutUserPolicy },
+ { "iam:GetUserPolicy", iamGetUserPolicy },
+ { "iam:DeleteUserPolicy", iamDeleteUserPolicy }};
 
 struct PolicyParser;
 
@@ -799,10 +802,14 @@ bool ParseState::do_string(CephContext* cct, const char* s, size_t l) {
   } else if ((w->id == TokenID::Action) ||
             (w->id == TokenID::NotAction)) {
     is_action = true;
-    for (auto& p : actpairs) {
-      if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) {
-        is_validaction = true;
-       (w->id == TokenID::Action ? t->action : t->notaction) |= p.bit;
+    if (*s == '*') {
+      is_validaction = true;
+    } else {
+      for (auto& p : actpairs) {
+        if (match_policy({s, l}, p.name, MATCH_POLICY_ACTION)) {
+          is_validaction = true;
+    (w->id == TokenID::Action ? t->action : t->notaction) |= p.bit;
+        }
       }
     }
   } else if (w->id == TokenID::Resource || w->id == TokenID::NotResource) {
index a3e5ccba0b86d23e55109b337f322ce4dba028e0..a923b837b3fae6e005f54a3b21985ffeb4eaaf59 100644 (file)
@@ -98,6 +98,11 @@ static constexpr std::uint64_t s3DeleteObjectVersionTagging = 1ULL << 53;
 static constexpr std::uint64_t s3Count = 54;
 static constexpr std::uint64_t s3All = (1ULL << s3Count) - 1;
 
+static constexpr std::uint64_t iamPutUserPolicy = 1ULL << 56;
+static constexpr std::uint64_t iamGetUserPolicy = 1ULL << 57;
+static constexpr std::uint64_t iamDeleteUserPolicy = 1ULL << 58;
+static constexpr std::uint64_t iamListUserPolicies = 1ULL << 59;
+
 namespace {
 inline int op_to_perm(std::uint64_t op) {
   switch (op) {
index b60bbeb58e1086fadd596599bef286b5a3b4e3b0..6fd340d9178fd7cfa149034caf9b02111afb61df 100644 (file)
@@ -42,7 +42,7 @@
 #include "rgw_rest_role.h"
 #include "rgw_crypt.h"
 #include "rgw_crypt_sanitize.h"
-
+#include "rgw_rest_user_policy.h"
 #include "include/assert.h"
 
 #define dout_context g_ceph_context
@@ -2979,6 +2979,14 @@ RGWOp *RGWHandler_REST_Service_S3::op_post()
       return new RGWListRolePolicies;
     if (action.compare("DeleteRolePolicy") == 0)
       return new RGWDeleteRolePolicy;
+    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;
   }
   return NULL;
 }
diff --git a/src/rgw/rgw_rest_user_policy.cc b/src/rgw/rgw_rest_user_policy.cc
new file mode 100644 (file)
index 0000000..aa1624a
--- /dev/null
@@ -0,0 +1,304 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#include <errno.h>
+#include <regex>
+
+#include "common/errno.h"
+#include "common/Formatter.h"
+#include "common/ceph_json.h"
+
+#include "include/types.h"
+#include "rgw_string.h"
+
+#include "rgw_common.h"
+#include "rgw_op.h"
+#include "rgw_rest.h"
+#include "rgw_rest_user_policy.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+using rgw::IAM::Policy;
+
+void RGWRestUserPolicy::dump(Formatter *f) const
+{
+  encode_json("policyname", policy_name , f);
+  encode_json("username", user_name , f);
+  encode_json("policydocument", policy, f);
+}
+
+void RGWRestUserPolicy::send_response()
+{
+  if (op_ret) {
+    set_req_state_err(s, op_ret);
+  }
+  dump_errno(s);
+  end_header(s);
+}
+
+int RGWRestUserPolicy::verify_permission()
+{
+  int ret = check_caps(s->user->caps);
+  ldout(s->cct, 0) << "INFO: verify_permissions ret" << ret << dendl;
+  return ret;
+}
+
+bool RGWRestUserPolicy::validate_input()
+{
+  if (policy_name.length() > MAX_POLICY_NAME_LEN) {
+    ldout(s->cct, 0) << "ERROR: Invalid policy name length " << dendl;
+    return false;
+  }
+
+  std::regex regex_policy_name("[A-Za-z0-9:=,.@-]+");
+  if (! std::regex_match(policy_name, regex_policy_name)) {
+    ldout(s->cct, 0) << "ERROR: Invalid chars in policy name " << dendl;
+    return false;
+  }
+
+  return true;
+}
+
+int RGWUserPolicyRead::check_caps(RGWUserCaps& caps)
+{
+    return caps.check_cap("user-policy", RGW_CAP_READ);
+}
+
+int RGWUserPolicyWrite::check_caps(RGWUserCaps& caps)
+{
+    return caps.check_cap("user-policy", RGW_CAP_WRITE);
+}
+
+int RGWPutUserPolicy::get_params()
+{
+  policy_name = s->info.args.get("PolicyName");
+  user_name = s->info.args.get("UserName");
+  policy = s->info.args.get("PolicyDocument");
+
+  if (policy_name.empty() || user_name.empty() || policy.empty()) {
+    ldout(s->cct, 20) << "ERROR: one of policy name, user name or policy document is empty"
+    << dendl;
+    return -EINVAL;
+  }
+
+  if (! validate_input()) {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+void RGWPutUserPolicy::execute()
+{
+  op_ret = get_params();
+  if (op_ret < 0) {
+    return;
+  }
+
+  bufferlist bl = bufferlist::static_from_string(policy);
+  ldout(s->cct, 0) << "policy: " << policy << dendl;
+  ldout(s->cct, 0) << "bufferlist: " << bl.c_str() << dendl;
+
+  RGWUserInfo info;
+  rgw_user user_id(user_name);
+  op_ret = rgw_get_user_info_by_uid(store, user_id, info);
+  if (op_ret < 0) {
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+
+  map<string, bufferlist> uattrs;
+  op_ret = rgw_get_user_attrs_by_uid(store, user_id, uattrs);
+  if (op_ret == -ENOENT) {
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+
+  try {
+    const Policy p(s->cct, s->user->user_id.tenant, bl);
+    map<string, string> policies;
+    if (auto it = uattrs.find(RGW_ATTR_USER_POLICY); it != uattrs.end()) {
+      bufferlist out_bl = uattrs[RGW_ATTR_USER_POLICY];
+      decode(policies, out_bl);
+    }
+    bufferlist in_bl;
+    policies[policy_name] = policy;
+    encode(policies, in_bl);
+    uattrs[RGW_ATTR_USER_POLICY] = in_bl;
+
+    RGWObjVersionTracker objv_tracker;
+    op_ret = rgw_store_user_info(store, info, &info, &objv_tracker, real_time(), false, &uattrs);
+    if (op_ret < 0) {
+      op_ret = -ERR_INTERNAL_ERROR;
+    }
+  } catch (rgw::IAM::PolicyParseException& e) {
+    ldout(s->cct, 20) << "failed to parse policy: " << e.what() << dendl;
+    op_ret = -ERR_MALFORMED_DOC;
+  }
+  ldout(s->cct, 20) << "op_ret is : " << op_ret << dendl;
+}
+
+int RGWGetUserPolicy::get_params()
+{
+  policy_name = s->info.args.get("PolicyName");
+  user_name = s->info.args.get("UserName");
+
+  if (policy_name.empty() || user_name.empty()) {
+    ldout(s->cct, 20) << "ERROR: one of policy name or user name is empty"
+    << dendl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+void RGWGetUserPolicy::execute()
+{
+  op_ret = get_params();
+  if (op_ret < 0) {
+    return;
+  }
+
+  rgw_user user_id(user_name);
+  map<string, bufferlist> uattrs;
+  op_ret = rgw_get_user_attrs_by_uid(store, user_id, uattrs);
+  if (op_ret == -ENOENT) {
+    ldout(s->cct, 0) << "ERROR: attrs not found for user" << user_name << dendl;
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+
+  if (op_ret == 0) {
+    map<string, string> policies;
+    if (auto it = uattrs.find(RGW_ATTR_USER_POLICY); it != uattrs.end()) {
+      bufferlist bl = uattrs[RGW_ATTR_USER_POLICY];
+      decode(policies, bl);
+      if (auto it = policies.find(policy_name); it != policies.end()) {
+        policy = policies[policy_name];
+        s->formatter->open_object_section("userpolicy");
+        dump(s->formatter);
+        s->formatter->close_section();
+      } else {
+        ldout(s->cct, 0) << "ERROR: policy not found" << policy << dendl;
+        op_ret = -ERR_NO_SUCH_ENTITY;
+        return;
+      }
+    } else {
+      ldout(s->cct, 0) << "ERROR: RGW_ATTR_USER_POLICY not found" << dendl;
+      op_ret = -ERR_NO_SUCH_ENTITY;
+      return;
+    }
+  }
+  if (op_ret < 0) {
+    op_ret = -ERR_INTERNAL_ERROR;
+  }
+}
+
+int RGWListUserPolicies::get_params()
+{
+  user_name = s->info.args.get("UserName");
+
+  if (user_name.empty()) {
+    ldout(s->cct, 20) << "ERROR: user name is empty" << dendl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+void RGWListUserPolicies::execute()
+{
+  op_ret = get_params();
+  if (op_ret < 0) {
+    return;
+  }
+
+  rgw_user user_id(user_name);
+  map<string, bufferlist> uattrs;
+  op_ret = rgw_get_user_attrs_by_uid(store, user_id, uattrs);
+  if (op_ret == -ENOENT) {
+    ldout(s->cct, 0) << "ERROR: attrs not found for user" << user_name << dendl;
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+
+  if (op_ret == 0) {
+    map<string, string> policies;
+    if (auto it = uattrs.find(RGW_ATTR_USER_POLICY); it != uattrs.end()) {
+      bufferlist bl = uattrs[RGW_ATTR_USER_POLICY];
+      decode(policies, bl);
+      for (const auto& p : policies) {
+        s->formatter->open_object_section("policies");
+        s->formatter->dump_string("policy", p.first);
+        s->formatter->close_section();
+      }
+    } else {
+      ldout(s->cct, 0) << "ERROR: RGW_ATTR_USER_POLICY not found" << dendl;
+      op_ret = -ERR_NO_SUCH_ENTITY;
+      return;
+    }
+  }
+  if (op_ret < 0) {
+    op_ret = -ERR_INTERNAL_ERROR;
+  }
+}
+
+int RGWDeleteUserPolicy::get_params()
+{
+  policy_name = s->info.args.get("PolicyName");
+  user_name = s->info.args.get("UserName");
+
+  if (policy_name.empty() || user_name.empty()) {
+    ldout(s->cct, 20) << "ERROR: One of policy name or user name is empty"<< dendl;
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+void RGWDeleteUserPolicy::execute()
+{
+  op_ret = get_params();
+  if (op_ret < 0) {
+    return;
+  }
+  
+  RGWUserInfo info;
+  rgw_user user_id(user_name);
+  op_ret = rgw_get_user_info_by_uid(store, user_id, info);
+  if (op_ret < 0) {
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+  
+  map<string, bufferlist> uattrs;
+  op_ret = rgw_get_user_attrs_by_uid(store, user_id, uattrs);
+  if (op_ret == -ENOENT) {
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+
+  map<string, string> policies;
+  if (auto it = uattrs.find(RGW_ATTR_USER_POLICY); it != uattrs.end()) {
+    bufferlist out_bl = uattrs[RGW_ATTR_USER_POLICY];
+    decode(policies, out_bl);
+
+    if (auto p = policies.find(policy_name); p != policies.end()) {
+      bufferlist in_bl;
+      policies.erase(p);
+      encode(policies, in_bl);
+      uattrs[RGW_ATTR_USER_POLICY] = in_bl;
+
+      RGWObjVersionTracker objv_tracker;
+      op_ret = rgw_store_user_info(store, info, &info, &objv_tracker, real_time(), false, &uattrs);
+      if (op_ret < 0) {
+        op_ret = -ERR_INTERNAL_ERROR;
+      }
+    } else {
+      op_ret = -ERR_NO_SUCH_ENTITY;
+      return;
+    }
+  } else {
+    op_ret = -ERR_NO_SUCH_ENTITY;
+    return;
+  }
+}
\ No newline at end of file
diff --git a/src/rgw/rgw_rest_user_policy.h b/src/rgw/rgw_rest_user_policy.h
new file mode 100644 (file)
index 0000000..577ff48
--- /dev/null
@@ -0,0 +1,70 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+#ifndef CEPH_RGW_REST_USER_POLICY_H
+#define CEPH_RGW_REST_USER_POLICY_H
+
+class RGWRestUserPolicy : public RGWRESTOp {
+protected:
+  static constexpr int MAX_POLICY_NAME_LEN = 128;
+  string policy_name;
+  string user_name;
+  string policy;
+
+  bool validate_input();
+
+public:
+  int verify_permission() override;
+  void send_response() override;
+  void dump(Formatter *f) const;
+};
+
+class RGWUserPolicyRead : public RGWRestUserPolicy {
+public:
+  RGWUserPolicyRead() = default;
+  int check_caps(RGWUserCaps& caps) override;
+};
+
+class RGWUserPolicyWrite : public RGWRestUserPolicy {
+public:
+  RGWUserPolicyWrite() = default;
+  int check_caps(RGWUserCaps& caps) override;
+};
+
+class RGWPutUserPolicy : public RGWUserPolicyWrite {
+public:
+  RGWPutUserPolicy() = default;
+  void execute() override;
+  int get_params();
+  const char* name() const override { return "put_user-policy"; }
+  RGWOpType get_type() override { return RGW_OP_PUT_USER_POLICY; }
+};
+
+class RGWGetUserPolicy : public RGWUserPolicyRead {
+public:
+  RGWGetUserPolicy() = default;
+  void execute() override;
+  int get_params();
+  const char* name() const override { return "get_user_policy"; }
+  RGWOpType get_type() override { return RGW_OP_GET_USER_POLICY; }
+};
+
+class RGWListUserPolicies : public RGWUserPolicyRead {
+public:
+  RGWListUserPolicies() = default;
+  void execute() override;
+  int get_params();
+  const char* name() const override { return "list_user_policies"; }
+  RGWOpType get_type() override { return RGW_OP_LIST_USER_POLICIES; }
+};
+
+class RGWDeleteUserPolicy : public RGWUserPolicyWrite {
+public:
+  RGWDeleteUserPolicy() = default;
+  void execute() override;
+  int get_params();
+  const char* name() const override { return "delete_user_policy"; }
+  RGWOpType get_type() override { return RGW_OP_DELETE_USER_POLICY; }
+};
+
+#endif /* CEPH_RGW_REST_USER_POLICY_H */
+