From f1007832acb8f7bcf5a39708b58e4913fd60f892 Mon Sep 17 00:00:00 2001 From: Radoslaw Zarzynski Date: Mon, 11 Jan 2016 18:49:21 +0100 Subject: [PATCH] rgw: add basics for Swift Account ACLs. Signed-off-by: Radoslaw Zarzynski --- src/rgw/rgw_acl_swift.cc | 125 +++++++++++++++++++++++++++++++++++++++ src/rgw/rgw_acl_swift.h | 15 +++++ src/rgw/rgw_common.h | 1 + src/rgw/rgw_op.cc | 77 ++++++++++++++++++++++-- 4 files changed, 213 insertions(+), 5 deletions(-) diff --git a/src/rgw/rgw_acl_swift.cc b/src/rgw/rgw_acl_swift.cc index 83fdc09c0f5b2..de48b82a89129 100644 --- a/src/rgw/rgw_acl_swift.cc +++ b/src/rgw/rgw_acl_swift.cc @@ -5,6 +5,7 @@ #include +#include "common/ceph_json.h" #include "rgw_common.h" #include "rgw_user.h" #include "rgw_acl_swift.h" @@ -15,6 +16,9 @@ using namespace std; #define SWIFT_PERM_READ RGW_PERM_READ_OBJS #define SWIFT_PERM_WRITE RGW_PERM_WRITE_OBJS +/* FIXME: do we really need separate RW? */ +#define SWIFT_PERM_RWRT (SWIFT_PERM_READ | SWIFT_PERM_WRITE) +#define SWIFT_PERM_ADMIN RGW_PERM_FULL_CONTROL #define SWIFT_GROUP_ALL_USERS ".r:*" @@ -136,3 +140,124 @@ void RGWAccessControlPolicy_SWIFT::to_str(string& read, string& write) } } +void RGWAccessControlPolicy_SWIFTAcct::add_grants(RGWRados * const store, + const list& uids, + const int perm) +{ + for (const auto uid : uids) { + ACLGrant grant; + RGWUserInfo grant_user; + + if (uid_is_public(uid)) { + grant.set_group(ACL_GROUP_ALL_USERS, perm); + acl.add_grant(&grant); + } else { + rgw_user user(uid); + + if (rgw_get_user_info_by_uid(store, user, grant_user) < 0) { + ldout(cct, 10) << "grant user does not exist:" << uid << dendl; + /* skipping silently */ + } else { + grant.set_canon(user, grant_user.display_name, perm); + acl.add_grant(&grant); + } + } + } +} + +bool RGWAccessControlPolicy_SWIFTAcct::create(RGWRados * const store, + const rgw_user& id, + const std::string& name, + const std::string& acl_str) +{ + acl.create_default(id, name); + owner.set_id(id); + owner.set_name(name); + + JSONParser parser; + + if (!parser.parse(acl_str.c_str(), acl_str.length())) { + ldout(cct, 0) << "ERROR: JSONParser::parse returned error=" << dendl; + return false; + } + + JSONObjIter iter = parser.find_first("admin"); + if (!iter.end() && (*iter)->is_array()) { + list admin; + decode_json_obj(admin, *iter); + ldout(cct, 0) << "admins: " << admin << dendl; + + add_grants(store, admin, SWIFT_PERM_ADMIN); + } + + iter = parser.find_first("read-write"); + if (!iter.end() && (*iter)->is_array()) { + list readwrite; + decode_json_obj(readwrite, *iter); + ldout(cct, 0) << "read-write: " << readwrite << dendl; + + add_grants(store, readwrite, SWIFT_PERM_RWRT); + } + + iter = parser.find_first("read-only"); + if (!iter.end() && (*iter)->is_array()) { + list readonly; + decode_json_obj(readonly, *iter); + ldout(cct, 0) << "read-only: " << readonly << dendl; + + add_grants(store, readonly, SWIFT_PERM_READ); + } + + return true; +} + +void RGWAccessControlPolicy_SWIFTAcct::to_str(std::string& acl_str) const +{ + list admin; + list readwrite; + list readonly; + + /* Parition the grant map into three not-overlapping groups. */ + for (const auto item : get_acl().get_grant_map()) { + const ACLGrant& grant = item.second; + const int perm = grant.get_permission().get_permissions(); + + rgw_user id; + if (!grant.get_id(id)) { + if (grant.get_group() != ACL_GROUP_ALL_USERS) { + continue; + } + id = SWIFT_GROUP_ALL_USERS; + } + + if (SWIFT_PERM_ADMIN == (perm & SWIFT_PERM_ADMIN)) { + admin.insert(admin.end(), id.to_str()); + } else if (SWIFT_PERM_RWRT == (perm & SWIFT_PERM_RWRT)) { + readwrite.insert(readwrite.end(), id.to_str()); + } else if (SWIFT_PERM_READ == (perm & SWIFT_PERM_READ)) { + readonly.insert(readonly.end(), id.to_str()); + } else { + // FIXME: print a warning + } + } + + /* Serialize the groups. */ + JSONFormatter formatter; + + formatter.open_object_section("acl"); + if (!readonly.empty()) { + encode_json("read-only", readonly, &formatter); + } + if (!readwrite.empty()) { + encode_json("read-write", readwrite, &formatter); + } + if (!admin.empty()) { + encode_json("admin", admin, &formatter); + } + formatter.close_section(); + + std::ostringstream oss; + formatter.flush(oss); + + acl_str = oss.str(); +} diff --git a/src/rgw/rgw_acl_swift.h b/src/rgw/rgw_acl_swift.h index ba72d7b914dc8..f791a2c2f3f04 100644 --- a/src/rgw/rgw_acl_swift.h +++ b/src/rgw/rgw_acl_swift.h @@ -24,4 +24,19 @@ public: void to_str(string& read, string& write); }; +class RGWAccessControlPolicy_SWIFTAcct : public RGWAccessControlPolicy +{ +public: + RGWAccessControlPolicy_SWIFTAcct(CephContext * const _cct) + : RGWAccessControlPolicy(_cct) + {} + ~RGWAccessControlPolicy_SWIFTAcct() {} + + void add_grants(RGWRados *store, const list& uids, int perm); + bool create(RGWRados *store, + const rgw_user& id, + const string& name, + const string& acl_str); + void to_str(string& acl) const; +}; #endif diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index ce4ed52d1d1b5..4e9b774418372 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -1279,6 +1279,7 @@ struct req_state { * through a well-defined interface. For more details, see rgw_auth.h. */ std::unique_ptr auth_identity; + std::unique_ptr user_acl; RGWAccessControlPolicy *bucket_acl; RGWAccessControlPolicy *object_acl; diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 7a02047f1d7d0..eff1684fae6e0 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -142,9 +142,32 @@ static int decode_policy(CephContext *cct, bufferlist& bl, RGWAccessControlPolic return 0; } -static int get_bucket_policy_from_attr(CephContext *cct, RGWRados *store, void *ctx, - RGWBucketInfo& bucket_info, map& bucket_attrs, - RGWAccessControlPolicy *policy, rgw_obj& obj) + +static int get_user_policy_from_attr(CephContext * const cct, + RGWRados * const store, + map& attrs, + RGWAccessControlPolicy& policy /* out */) +{ + auto aiter = attrs.find(RGW_ATTR_ACL); + if (aiter != attrs.end()) { + int ret = decode_policy(cct, aiter->second, &policy); + if (ret < 0) { + return ret; + } + } else { + return -ENOENT; + } + + return 0; +} + +static int get_bucket_policy_from_attr(CephContext *cct, + RGWRados *store, + void *ctx, + RGWBucketInfo& bucket_info, + map& bucket_attrs, + RGWAccessControlPolicy *policy, + rgw_obj& obj) { map::iterator aiter = bucket_attrs.find(RGW_ATTR_ACL); @@ -294,9 +317,9 @@ static int read_policy(RGWRados *store, struct req_state *s, } /** - * Get the AccessControlPolicy for a bucket or object off of disk. + * Get the AccessControlPolicy for an user, bucket or object off of disk. * s: The req_state to draw information from. - * only_bucket: If true, reads the bucket ACL rather than the object ACL. + * only_bucket: If true, reads the user and bucket ACLs rather than the object ACL. * Returns: 0 on success, -ERR# otherwise. */ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s) @@ -317,6 +340,8 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s) if(s->dialect.compare("s3") == 0) { s->bucket_acl = new RGWAccessControlPolicy_S3(s->cct); } else if(s->dialect.compare("swift") == 0) { + s->user_acl = std::unique_ptr( + new RGWAccessControlPolicy_SWIFTAcct(s->cct)); s->bucket_acl = new RGWAccessControlPolicy_SWIFT(s->cct); } else { s->bucket_acl = new RGWAccessControlPolicy(s->cct); @@ -337,6 +362,14 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s) } } + struct { + rgw_user uid; + std::string display_name; + } acct_acl_user = { + s->user->user_id, + s->user->display_name, + }; + if (!s->bucket_name.empty()) { s->bucket_exists = true; if (s->bucket_instance_id.empty()) { @@ -358,6 +391,10 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s) if (s->bucket_exists) { rgw_obj_key no_obj; ret = read_policy(store, s, s->bucket_info, s->bucket_attrs, s->bucket_acl, s->bucket, no_obj); + acct_acl_user = { + s->bucket_info.owner, + s->bucket_acl->get_owner().get_display_name(), + }; } else { s->bucket_acl->create_default(s->user->user_id, s->user->display_name); ret = -ERR_NO_SUCH_BUCKET; @@ -392,6 +429,36 @@ int rgw_build_bucket_policies(RGWRados* store, struct req_state* s) } } + /* handle user ACL only for those APIs which support it */ + if (s->user_acl) { + map uattrs; + + ret = rgw_get_user_attrs_by_uid(store, acct_acl_user.uid, uattrs); + if (!ret) { + ret = get_user_policy_from_attr(s->cct, store, uattrs, *s->user_acl); + } + if (-ENOENT == ret) { + /* In already existing clusters users won't have ACL. In such case + * assuming that only account owner has the rights seems to be + * reasonable. That allows to have only one verification logic. + * NOTE: there is small compatibility kludge for global, empty tenant: + * 1. if we try to reach an existing bucket, its owner is considered + * as account owner. + * 2. otherwise account owner is identity stored in s->user->user_id. */ + s->user_acl->create_default(acct_acl_user.uid, + acct_acl_user.display_name); + ret = 0; + } else { + ldout(s->cct, 0) << "NOTICE: couldn't get user attrs for handling ACL (user_id=" + << s->user->user_id + << ", ret=" + << ret + << ")" << dendl; + return ret; + } + } + + return ret; } -- 2.39.5