]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw_acl: Support ACL grants in headers.
authorcaleb miles <caselim@gmail.com>
Tue, 19 Feb 2013 17:15:30 +0000 (12:15 -0500)
committerYehuda Sadeh <yehuda@inktank.com>
Wed, 20 Feb 2013 20:32:41 +0000 (12:32 -0800)
Issue 3669: Support S3 ACL grants specified in request headers. Allow
requests, excluding POST object, to specify ACL grants in HTTP headers.

Signed-off-by: caleb miles <caleb.miles@inktank.com>
Conflicts:
src/rgw/rgw_acl_s3.cc
src/rgw/rgw_acl_s3.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
src/common/str_list.cc
src/include/str_list.h
src/rgw/rgw_acl_s3.cc
src/rgw/rgw_acl_s3.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_env.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_s3.h

index 36a383c3f1a209fdbea64b40930fa0b26e9b62c2..cae2b22015d4d27ed824c99e5088e3d088d1f5cb 100644 (file)
@@ -18,7 +18,7 @@
 
 using std::string;
 
-static bool get_next_token(const std::string &s, size_t& pos, string& token)
+static bool get_next_token(const std::string &s, size_t& pos, const char *delims, string& token)
 {
   int start = s.find_first_not_of(" \t", pos);
   int end;
@@ -30,7 +30,7 @@ static bool get_next_token(const std::string &s, size_t& pos, string& token)
     end = start + 1;
     pos = end;
   } else {
-    end = s.find_first_of(";,= \t", start+1);
+    end = s.find_first_of(delims, start+1);
     if (end >= 0)
       pos = end + 1;
   }
@@ -44,7 +44,13 @@ static bool get_next_token(const std::string &s, size_t& pos, string& token)
   return true;
 }
 
-void get_str_list(const std::string& str, std::list<string>& str_list)
+static bool get_next_token(const std::string &s, size_t& pos, string& token)
+{
+  const char *delims = ";,= \t";
+  return get_next_token(s, pos, delims, token);
+}
+
+void get_str_list(const std::string& str, const char *delims, std::list<string>& str_list)
 {
   size_t pos = 0;
   string token;
@@ -52,7 +58,7 @@ void get_str_list(const std::string& str, std::list<string>& str_list)
   str_list.clear();
 
   while (pos < str.size()) {
-    if (get_next_token(str, pos, token)) {
+    if (get_next_token(str, pos, delims, token)) {
       if (token.size() > 0) {
         str_list.push_back(token);
       }
@@ -60,6 +66,12 @@ void get_str_list(const std::string& str, std::list<string>& str_list)
   }
 }
 
+void get_str_list(const std::string& str, std::list<string>& str_list)
+{
+  const char *delims = ";,= \t";
+  return get_str_list(str, delims, str_list);
+}
+
 void get_str_set(const std::string& str, std::set<std::string>& str_set)
 {
   size_t pos = 0;
index ef4d8066971c30a0dd37466a7b7822ae90964296..a957e51d417e67839f6229fb19ff78cb87a59487 100644 (file)
@@ -7,6 +7,9 @@
 
 extern void get_str_list(const std::string& str,
                         std::list<std::string>& str_list);
+extern void get_str_list(const std::string& str,
+                         const char *delims,
+                        std::list<std::string>& str_list);
 extern void get_str_set(const std::string& str,
                        std::set<std::string>& str_list);
 
index 8ae57307d7c8a961bfb5ec6f059ef4234ec1e9ae..e67f77642a6c09c6b8b661642618e03b2936c035 100644 (file)
@@ -264,7 +264,89 @@ bool RGWAccessControlList_S3::xml_end(const char *el) {
   return true;
 }
 
-bool RGWAccessControlList_S3::create_canned(ACLOwner& owner, ACLOwner& bucket_owner, const string& canned_acl)
+struct s3_acl_header {
+  int rgw_perm;
+  const char *http_header;
+
+  s3_acl_header(int perm, const char *head) {
+    rgw_perm = perm;
+    http_header = head;
+  }
+};
+
+static const char *get_acl_header(RGWEnv *env,
+        const struct s3_acl_header *perm)
+{
+  const char *header = perm->http_header;
+
+  return env->get(header, NULL);
+}
+
+static int parse_grantee_str(RGWRados *store, string& grantee_str,
+        const struct s3_acl_header *perm, ACLGrant& grant)
+{
+  string id_type, id_val;
+  int rgw_perm = perm->rgw_perm;
+  int ret;
+
+  RGWUserInfo info;
+
+  ret = parse_key_value(grantee_str, id_type, id_val);
+  if (ret < 0)
+    return ret;
+
+  if (strcasecmp(id_type.c_str(), "emailAddress") == 0) {
+    ret = rgw_get_user_info_by_email(store, id_val, info);
+    if (ret < 0)
+      return ret;
+
+    grant.set_canon(info.user_id, info.display_name, rgw_perm);
+  } else if (strcasecmp(id_type.c_str(), "id") == 0) {
+    ret = rgw_get_user_info_by_uid(store, id_val, info);
+    if (ret < 0)
+      return ret;
+
+    grant.set_canon(info.user_id, info.display_name, rgw_perm);
+  } else if (strcasecmp(id_type.c_str(), "uri") == 0) {
+    ACLGroupTypeEnum gid = grant.uri_to_group(id_val);
+    if (gid == ACL_GROUP_NONE)
+      return -EINVAL;
+
+    grant.set_group(gid, rgw_perm);
+  } else {
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+static int parse_acl_header(RGWRados *store, RGWEnv *env,
+         const struct s3_acl_header *perm, std::list<ACLGrant>& _grants)
+{
+  std::list<string> grantees;
+  std::string hacl_str;
+
+  const char *hacl = get_acl_header(env, perm);
+  if (hacl == NULL)
+    return 0;
+
+  hacl_str = hacl;
+  get_str_list(hacl_str, ",", grantees);
+
+  list<string>::iterator it = grantees.begin();
+  for (; it != grantees.end(); it++) {
+    ACLGrant grant;
+    int ret = parse_grantee_str(store, *it, perm, grant);
+    if (ret < 0)
+      return ret;
+
+    _grants.push_back(grant);
+  }
+
+  return 0;
+}
+
+int RGWAccessControlList_S3::create_canned(ACLOwner& owner, ACLOwner& bucket_owner, const string& canned_acl)
 {
   acl_user_map.clear();
   grant_map.clear();
@@ -279,7 +361,7 @@ bool RGWAccessControlList_S3::create_canned(ACLOwner& owner, ACLOwner& bucket_ow
   add_grant(&owner_grant);
 
   if (canned_acl.size() == 0 || canned_acl.compare("private") == 0) {
-    return true;
+    return 0;
   }
 
   ACLGrant bucket_owner_grant;
@@ -304,10 +386,27 @@ bool RGWAccessControlList_S3::create_canned(ACLOwner& owner, ACLOwner& bucket_ow
     if (bid.compare(owner.get_id()) != 0)
       add_grant(&bucket_owner_grant);
   } else {
-    return false;
+    return -EINVAL;
   }
 
-  return true;
+  return 0;
+}
+
+int RGWAccessControlList_S3::create_from_grants(std::list<ACLGrant>& grants)
+{
+  if (grants.empty())
+    return -EINVAL;
+
+  acl_user_map.clear();
+  grant_map.clear();
+
+  std::list<ACLGrant>::iterator it = grants.begin();
+  for (; it != grants.end(); it++) {
+    ACLGrant g = *it;
+    add_grant(&g);
+  }
+
+  return 0;
 }
 
 bool RGWAccessControlPolicy_S3::xml_end(const char *el) {
@@ -325,6 +424,40 @@ bool RGWAccessControlPolicy_S3::xml_end(const char *el) {
   return true;
 }
 
+int RGWAccessControlPolicy_S3::create_from_headers(RGWRados *store, RGWEnv *env, ACLOwner& _owner)
+{
+  std::list<ACLGrant> grants;
+  int ret;
+
+  const s3_acl_header grant_read(RGW_PERM_READ,"HTTP_X_AMZ_GRANT_READ");
+  const s3_acl_header grant_write(RGW_PERM_WRITE, "HTTP_X_AMZ_GRANT_WRITE");
+  const s3_acl_header grant_read_acp(RGW_PERM_READ_ACP,"HTTP_X_AMZ_GRANT_READ_ACP");
+  const s3_acl_header grant_write_acp(RGW_PERM_WRITE_ACP, "HTTP_X_AMZ_GRANT_WRITE_ACP");
+  const s3_acl_header grant_full_control(RGW_PERM_FULL_CONTROL, "HTTP_X_AMZ_GRANT_FULL_CONTROL");
+
+  const s3_acl_header *perms[] = {
+                                   &grant_read,
+                                   &grant_write,
+                                   &grant_read_acp,
+                                   &grant_write_acp,
+                                   &grant_full_control,
+                                   NULL
+                                 };
+
+  for (const struct s3_acl_header **p = perms; *p; p++) {
+    ret = parse_acl_header(store, env, *p, grants);
+    if (ret < 0)
+      return false;
+  }
+
+  RGWAccessControlList_S3& _acl = static_cast<RGWAccessControlList_S3 &>(acl);
+  int r = _acl.create_from_grants(grants);
+
+  owner = _owner;
+
+  return r;
+}
+
 /*
   can only be called on object that was parsed
  */
index 453f68161f0e213b3fa7dc8213b24c8636a21920..6c14d1df1ad312bb6a60f11bf7e9dc23e3f241d5 100644 (file)
@@ -8,6 +8,7 @@
 
 #include <expat.h>
 
+#include "include/str_list.h"
 #include "rgw_xml.h"
 #include "rgw_acl.h"
 
@@ -66,7 +67,8 @@ public:
     out << "</AccessControlList>";
   }
 
-  bool create_canned(ACLOwner& owner, ACLOwner& bucket_owner, const string& canned_acl);
+  int create_canned(ACLOwner& owner, ACLOwner& bucket_owner, const string& canned_acl);
+  int create_from_grants(std::list<ACLGrant>& grants);
 };
 
 class ACLOwner_S3 : public ACLOwner, public XMLObj
@@ -86,6 +88,8 @@ public:
   }
 };
 
+class RGWEnv;
+
 class RGWAccessControlPolicy_S3 : public RGWAccessControlPolicy, public XMLObj
 {
 public:
@@ -105,12 +109,13 @@ public:
   int rebuild(RGWRados *store, ACLOwner *owner, RGWAccessControlPolicy& dest);
   bool compare_group_name(string& id, ACLGroupTypeEnum group);
 
-  virtual bool create_canned(ACLOwner& _owner, ACLOwner& bucket_owner, string canned_acl) {
+  virtual int create_canned(ACLOwner& _owner, ACLOwner& bucket_owner, string canned_acl) {
     RGWAccessControlList_S3& _acl = static_cast<RGWAccessControlList_S3 &>(acl);
-    bool ret = _acl.create_canned(_owner, bucket_owner, canned_acl);
+    int ret = _acl.create_canned(_owner, bucket_owner, canned_acl);
     owner = _owner;
     return ret;
   }
+  int create_from_headers(RGWRados *store, RGWEnv *env, ACLOwner& _owner);
 };
 
 /**
index 8564fdd985a5ee9c2f2d543c64db61b9ceadb32a..c4dfe2fc6c2c2d0b55d60e321ba5093eeb3900a0 100644 (file)
@@ -255,6 +255,28 @@ bool parse_iso8601(const char *s, struct tm *t)
   return true;
 }
 
+int parse_key_value(string& in_str, const char *delim, string& key, string& val)
+{
+  if (delim == NULL)
+    return -EINVAL;
+
+  int pos = in_str.find(delim);
+  if (pos < 0)
+    return -EINVAL;
+
+  trim_whitespace(in_str.substr(0, pos), key);
+  pos++;
+
+  trim_whitespace(in_str.substr(pos), val);
+
+  return 0;
+}
+
+int parse_key_value(string& in_str, string& key, string& val)
+{
+  return parse_key_value(in_str, "=", key,val);
+}
+
 int parse_time(const char *time_str, time_t *time)
 {
   struct tm tm;
index cd1ebaa71f6de5704c5b0cd4cb4e91f54902153d..8e987c5c26e51ad5ca448bd9e31a38c4d1eb88e2 100644 (file)
@@ -246,6 +246,8 @@ public:
   int get_int(const char *name, int def_val = 0);
   bool get_bool(const char *name, bool def_val = 0);
   size_t get_size(const char *name, size_t def_val = 0);
+  bool exists(const char *name);
+  bool exists_prefix(const char *prefix);
 };
 
 class RGWConf {
@@ -609,6 +611,7 @@ struct req_state {
    RGWAccessControlPolicy *object_acl;
 
    string canned_acl;
+   bool has_acl_header;
    const char *copy_source;
    const char *http_auth;
 
@@ -999,6 +1002,9 @@ static inline const char *rgw_obj_category_name(RGWObjCategory category)
 }
 
 extern string rgw_string_unquote(const string& s);
+extern void parse_csv_string(const string& ival, vector<string>& ovals);
+extern int parse_key_value(string& in_str, string& key, string& val);
+extern int parse_key_value(string& in_str, const char *delim, string& key, string& val);
 /** time parsing */
 extern int parse_time(const char *time_str, time_t *time);
 extern bool parse_rfc2616(const char *s, struct tm *t);
index b8f6b0440cc7e9e34741f39dda8b4e72201d0e5b..e23fa257d1b0063472ef300672603b9172d61146 100644 (file)
@@ -74,6 +74,24 @@ size_t RGWEnv::get_size(const char *name, size_t def_val)
   return atoll(s);  
 }
 
+bool RGWEnv::exists(const char *name)
+{
+  map<string, string>::iterator iter = env_map.find(name);
+  return (iter != env_map.end());
+}
+
+bool RGWEnv::exists_prefix(const char *prefix)
+{
+  if (env_map.empty() || prefix == NULL)
+    return false;
+
+  map<string, string>::iterator iter = env_map.lower_bound(prefix);
+  if (iter == env_map.end())
+    return false;
+
+  return (strncmp(iter->first.c_str(), prefix, strlen(prefix)) == 0);
+}
+
 void RGWConf::init(CephContext *cct, RGWEnv *env)
 {
   enable_ops_log = cct->_conf->rgw_enable_ops_log;
index 15349ebf0f9cc1f8003272a42102308b413043d0..0a022d0197548d9b63feecc9a12356dc0b2d96b9 100644 (file)
@@ -1796,8 +1796,9 @@ void RGWPutACLs::execute()
     ret = -EINVAL;
     return;
   }
-  if (!s->canned_acl.empty()) {
-    ret = get_canned_policy(owner, ss);
+
+  if (!s->canned_acl.empty() || s->has_acl_header) {
+    ret = get_policy_from_state(store, s, ss);
     if (ret < 0)
       return;
 
@@ -1807,7 +1808,6 @@ void RGWPutACLs::execute()
     len = ss.str().size();
   }
 
-
   if (!parser.parse(data, len, 1)) {
     ret = -EACCES;
     return;
index 08c10970e90042dd1d988d796316e5a1899eb361..8f1252cf2dc7659c607ec9708bf248fef60b9df7 100644 (file)
@@ -493,7 +493,7 @@ public:
   int verify_permission();
   void execute();
 
-  virtual int get_canned_policy(ACLOwner& owner, stringstream& ss) { return 0; }
+  virtual int get_policy_from_state(RGWRados *store, struct req_state *s, stringstream& ss) { return 0; }
   virtual int get_params() = 0;
   virtual void send_response() = 0;
   virtual const char *name() { return "put_acls"; }
index bbcceb827ac0274aeacd6af3045d9b5d1ff13215..b4ddded06273a5b0525b50b97d8d585ec4efee14 100644 (file)
@@ -277,11 +277,23 @@ void RGWStatBucket_ObjStore_S3::send_response()
   dump_start(s);
 }
 
+static int create_s3_policy(struct req_state *s, RGWRados *store, RGWAccessControlPolicy_S3& s3policy)
+{
+  if (s->has_acl_header) {
+    if (!s->canned_acl.empty())
+      return -ERR_INVALID_REQUEST;
+
+    return s3policy.create_from_headers(store, s->env, s->owner);
+  }
+
+  return s3policy.create_canned(s->owner, s->bucket_owner, s->canned_acl);
+}
+
 int RGWCreateBucket_ObjStore_S3::get_params()
 {
   RGWAccessControlPolicy_S3 s3policy(s->cct);
 
-  int r = s3policy.create_canned(s->owner, s->bucket_owner, s->canned_acl);
+  int r = create_s3_policy(s, store, s3policy);
   if (r < 0)
     return r;
 
@@ -317,9 +329,9 @@ int RGWPutObj_ObjStore_S3::get_params()
   if (!s->length)
     return -ERR_LENGTH_REQUIRED;
 
-  int r = s3policy.create_canned(s->owner, s->bucket_owner, s->canned_acl);
-  if (!r)
-     return -EINVAL;
+  int r = create_s3_policy(s, store, s3policy);
+  if (r < 0)
+    return r;
 
   policy = s3policy;
 
@@ -911,7 +923,7 @@ int RGWPostObj_ObjStore_S3::get_policy()
 
   RGWAccessControlPolicy_S3 s3policy(s->cct);
   ldout(s->cct, 20) << "canned_acl=" << canned_acl << dendl;
-  if (!s3policy.create_canned(s->owner, s->bucket_owner, canned_acl)) {
+  if (s3policy.create_canned(s->owner, s->bucket_owner, canned_acl) < 0) {
     err_msg = "Bad canned ACLs";
     return -EINVAL;
   }
@@ -1118,9 +1130,9 @@ int RGWCopyObj_ObjStore_S3::init_dest_policy()
   RGWAccessControlPolicy_S3 s3policy(s->cct);
 
   /* build a policy for the target object */
-  ret = s3policy.create_canned(s->owner, s->bucket_owner, s->canned_acl);
-  if (!ret)
-     return -EINVAL;
+  int r = create_s3_policy(s, store, s3policy);
+  if (r < 0)
+    return r;
 
   dest_policy = s3policy;
 
@@ -1198,7 +1210,7 @@ void RGWGetACLs_ObjStore_S3::send_response()
   s->cio->write(acls.c_str(), acls.size());
 }
 
-int RGWPutACLs_ObjStore_S3::get_canned_policy(ACLOwner& owner, stringstream& ss)
+int RGWPutACLs_ObjStore_S3::get_policy_from_state(RGWRados *store, struct req_state *s, stringstream& ss)
 {
   RGWAccessControlPolicy_S3 s3policy(s->cct);
 
@@ -1208,11 +1220,9 @@ int RGWPutACLs_ObjStore_S3::get_canned_policy(ACLOwner& owner, stringstream& ss)
       s->canned_acl.clear();
   }
 
-  bool r;
-  r = s3policy.create_canned(owner, s->bucket_owner, s->canned_acl);
-
-  if (!r)
-    return -EINVAL;
+  int r = create_s3_policy(s, store, s3policy);
+  if (r < 0)
+    return r;
 
   s3policy.to_xml(ss);
 
@@ -1231,9 +1241,9 @@ void RGWPutACLs_ObjStore_S3::send_response()
 int RGWInitMultipart_ObjStore_S3::get_params()
 {
   RGWAccessControlPolicy_S3 s3policy(s->cct);
-  ret = s3policy.create_canned(s->owner, s->bucket_owner, s->canned_acl);
-  if (!ret)
-     return -EINVAL;
+  ret = create_s3_policy(s, store, s3policy);
+  if (ret < 0)
+    return ret;
 
   policy = s3policy;
 
@@ -1712,6 +1722,8 @@ int RGWHandler_ObjStore_S3::init(RGWRados *store, struct req_state *s, RGWClient
   if (cacl)
     s->canned_acl = cacl;
 
+  s->has_acl_header = s->env->exists_prefix("HTTP_X_AMZ_GRANT");
+
   s->copy_source = s->env->get("HTTP_X_AMZ_COPY_SOURCE");
 
   s->dialect = "s3";
index dc38077fc3edaea58794e616c523772f70770bc7..cc900507c1238234f4076f3d1a9d7e993b818b86 100644 (file)
@@ -160,8 +160,7 @@ public:
   RGWPutACLs_ObjStore_S3() {}
   ~RGWPutACLs_ObjStore_S3() {}
 
-  int get_canned_policy(ACLOwner& owner, stringstream& ss);
-
+  int get_policy_from_state(RGWRados *store, struct req_state *s, stringstream& ss);
   void send_response();
 };