summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcaleb miles <caselim@gmail.com>2013-02-19 12:15:30 -0500
committerYehuda Sadeh <yehuda@inktank.com>2013-02-20 12:32:41 -0800
commiteb0f49d4b68062701b842b9cfdde708868769bef (patch)
treea6cc98571ee45f209196779c50b8b87ac46792cd
parent2e1b02bf0117995932dc5723c70ddec628d375c2 (diff)
downloadceph-eb0f49d4b68062701b842b9cfdde708868769bef.tar.gz
rgw_acl: Support ACL grants in headers.
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>
-rw-r--r--src/common/str_list.cc20
-rw-r--r--src/include/str_list.h3
-rw-r--r--src/rgw/rgw_acl_s3.cc141
-rw-r--r--src/rgw/rgw_acl_s3.h11
-rw-r--r--src/rgw/rgw_common.cc22
-rw-r--r--src/rgw/rgw_common.h6
-rw-r--r--src/rgw/rgw_env.cc18
-rw-r--r--src/rgw/rgw_op.cc6
-rw-r--r--src/rgw/rgw_op.h2
-rw-r--r--src/rgw/rgw_rest_s3.cc46
-rw-r--r--src/rgw/rgw_rest_s3.h3
11 files changed, 244 insertions, 34 deletions
diff --git a/src/common/str_list.cc b/src/common/str_list.cc
index 36a383c3f1a..cae2b22015d 100644
--- a/src/common/str_list.cc
+++ b/src/common/str_list.cc
@@ -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;
diff --git a/src/include/str_list.h b/src/include/str_list.h
index ef4d8066971..a957e51d417 100644
--- a/src/include/str_list.h
+++ b/src/include/str_list.h
@@ -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);
diff --git a/src/rgw/rgw_acl_s3.cc b/src/rgw/rgw_acl_s3.cc
index 8ae57307d7c..e67f77642a6 100644
--- a/src/rgw/rgw_acl_s3.cc
+++ b/src/rgw/rgw_acl_s3.cc
@@ -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
*/
diff --git a/src/rgw/rgw_acl_s3.h b/src/rgw/rgw_acl_s3.h
index 453f68161f0..6c14d1df1ad 100644
--- a/src/rgw/rgw_acl_s3.h
+++ b/src/rgw/rgw_acl_s3.h
@@ -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);
};
/**
diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc
index 8564fdd985a..c4dfe2fc6c2 100644
--- a/src/rgw/rgw_common.cc
+++ b/src/rgw/rgw_common.cc
@@ -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;
diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h
index cd1ebaa71f6..8e987c5c26e 100644
--- a/src/rgw/rgw_common.h
+++ b/src/rgw/rgw_common.h
@@ -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);
diff --git a/src/rgw/rgw_env.cc b/src/rgw/rgw_env.cc
index b8f6b0440cc..e23fa257d1b 100644
--- a/src/rgw/rgw_env.cc
+++ b/src/rgw/rgw_env.cc
@@ -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;
diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc
index 15349ebf0f9..0a022d01975 100644
--- a/src/rgw/rgw_op.cc
+++ b/src/rgw/rgw_op.cc
@@ -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;
diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h
index 08c10970e90..8f1252cf2dc 100644
--- a/src/rgw/rgw_op.h
+++ b/src/rgw/rgw_op.h
@@ -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"; }
diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc
index bbcceb827ac..b4ddded0627 100644
--- a/src/rgw/rgw_rest_s3.cc
+++ b/src/rgw/rgw_rest_s3.cc
@@ -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";
diff --git a/src/rgw/rgw_rest_s3.h b/src/rgw/rgw_rest_s3.h
index dc38077fc3e..cc900507c12 100644
--- a/src/rgw/rgw_rest_s3.h
+++ b/src/rgw/rgw_rest_s3.h
@@ -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();
};