diff options
author | caleb miles <caselim@gmail.com> | 2013-02-19 12:15:30 -0500 |
---|---|---|
committer | Yehuda Sadeh <yehuda@inktank.com> | 2013-02-20 12:32:41 -0800 |
commit | eb0f49d4b68062701b842b9cfdde708868769bef (patch) | |
tree | a6cc98571ee45f209196779c50b8b87ac46792cd | |
parent | 2e1b02bf0117995932dc5723c70ddec628d375c2 (diff) | |
download | ceph-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.cc | 20 | ||||
-rw-r--r-- | src/include/str_list.h | 3 | ||||
-rw-r--r-- | src/rgw/rgw_acl_s3.cc | 141 | ||||
-rw-r--r-- | src/rgw/rgw_acl_s3.h | 11 | ||||
-rw-r--r-- | src/rgw/rgw_common.cc | 22 | ||||
-rw-r--r-- | src/rgw/rgw_common.h | 6 | ||||
-rw-r--r-- | src/rgw/rgw_env.cc | 18 | ||||
-rw-r--r-- | src/rgw/rgw_op.cc | 6 | ||||
-rw-r--r-- | src/rgw/rgw_op.h | 2 | ||||
-rw-r--r-- | src/rgw/rgw_rest_s3.cc | 46 | ||||
-rw-r--r-- | src/rgw/rgw_rest_s3.h | 3 |
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(); }; |