From d81d96a0147baa8713c239546f96ba2160a9cb45 Mon Sep 17 00:00:00 2001 From: Shilpa Jagannath Date: Tue, 9 Apr 2019 15:38:25 +0530 Subject: [PATCH] Set object ACLS to reflect bucket owner supporting bucket unlink/link. Provides command line tool to update the acl on object of a bucket after bucket unlink/link. "radosgw-admin bucket chown --bucket " Signed-off-by: Shilpa Jagannath --- src/rgw/rgw_acl.cc | 14 ++++ src/rgw/rgw_acl.h | 1 + src/rgw/rgw_admin.cc | 18 +++++ src/rgw/rgw_bucket.cc | 125 ++++++++++++++++++++++++++++++ src/rgw/rgw_bucket.h | 2 + src/rgw/rgw_op.cc | 24 +++--- src/rgw/rgw_op.h | 2 + src/test/cli/radosgw-admin/help.t | 1 + 8 files changed, 175 insertions(+), 12 deletions(-) diff --git a/src/rgw/rgw_acl.cc b/src/rgw/rgw_acl.cc index 8c02f8e350e64..b2adbe6b61c45 100644 --- a/src/rgw/rgw_acl.cc +++ b/src/rgw/rgw_acl.cc @@ -52,6 +52,20 @@ void RGWAccessControlList::add_grant(ACLGrant *grant) _add_grant(grant); } +void RGWAccessControlList::remove_canon_user_grant(rgw_user& user_id) +{ + auto multi_map_iter = grant_map.find(user_id.to_str()); + if(multi_map_iter != grant_map.end()) { + auto grants = grant_map.equal_range(user_id.to_str()); + grant_map.erase(grants.first, grants.second); + } + + auto map_iter = acl_user_map.find(user_id.to_str()); + if (map_iter != acl_user_map.end()){ + acl_user_map.erase(map_iter); + } +} + uint32_t RGWAccessControlList::get_perm(const DoutPrefixProvider* dpp, const rgw::auth::Identity& auth_identity, const uint32_t perm_mask) diff --git a/src/rgw/rgw_acl.h b/src/rgw/rgw_acl.h index 0e84d75eef8c7..dc8f25dd22457 100644 --- a/src/rgw/rgw_acl.h +++ b/src/rgw/rgw_acl.h @@ -344,6 +344,7 @@ public: static void generate_test_instances(list& o); void add_grant(ACLGrant *grant); + void remove_canon_user_grant(rgw_user& user_id); multimap& get_grant_map() { return grant_map; } const multimap& get_grant_map() const { return grant_map; } diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 384b3549e9f54..6a2ad126bfb1b 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -105,6 +105,7 @@ void usage() cout << " bucket stats returns bucket statistics\n"; cout << " bucket rm remove bucket\n"; cout << " bucket check check bucket index\n"; + cout << " bucket chown link bucket to specified user and update its object ACLs\n"; cout << " bucket reshard reshard bucket\n"; cout << " bucket rewrite rewrite all objects in the specified bucket\n"; cout << " bucket sync disable disable bucket sync\n"; @@ -415,6 +416,7 @@ enum { OPT_BUCKET_RM, OPT_BUCKET_REWRITE, OPT_BUCKET_RESHARD, + OPT_BUCKET_CHOWN, OPT_POLICY, OPT_POOL_ADD, OPT_POOL_RM, @@ -680,6 +682,8 @@ static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_ return OPT_BUCKET_STATS; if (strcmp(cmd, "rm") == 0) return OPT_BUCKET_RM; + if (strcmp(cmd, "chown") == 0) + return OPT_BUCKET_CHOWN; if (strcmp(cmd, "rewrite") == 0) return OPT_BUCKET_REWRITE; if (strcmp(cmd, "reshard") == 0) @@ -5551,6 +5555,20 @@ int main(int argc, const char **argv) } } + if (opt_cmd == OPT_BUCKET_CHOWN) { + + bucket_op.set_bucket_name(bucket_name); + bucket_op.set_new_bucket_name(new_bucket_name); + string err; + string marker; + + int r = RGWBucketAdminOp::chown(store, bucket_op, marker, &err); + if (r < 0) { + cerr << "failure: " << cpp_strerror(-r) << ": " << err << std::endl; + return -r; + } + } + if (opt_cmd == OPT_LOG_LIST) { // filter by date? if (date.size() && date.size() != 10) { diff --git a/src/rgw/rgw_bucket.cc b/src/rgw/rgw_bucket.cc index 2a19b1b39dc51..e1792ad30d07d 100644 --- a/src/rgw/rgw_bucket.cc +++ b/src/rgw/rgw_bucket.cc @@ -24,6 +24,7 @@ #include "rgw_user.h" #include "rgw_string.h" #include "rgw_multi.h" +#include "rgw_op.h" #include "services/svc_zone.h" #include "services/svc_sys_obj.h" @@ -993,6 +994,113 @@ int RGWBucket::link(RGWBucketAdminOpState& op_state, return 0; } +int RGWBucket::chown(RGWBucketAdminOpState& op_state, + map& attrs, const string& marker, std::string *err_msg) +{ + + //after bucket link + rgw_bucket& bucket = op_state.get_bucket(); + tenant = bucket.tenant; + bucket_name = bucket.name; + + std::vector objs; + map common_prefixes; + RGWObjectCtx obj_ctx(store); + + RGWBucketInfo bucket_info; + RGWSysObjectCtx sys_ctx = store->svc.sysobj->init_obj_ctx(); + + int ret = store->get_bucket_info(sys_ctx, tenant, bucket_name, bucket_info, NULL, &attrs); + if (ret < 0) { + set_err_msg(err_msg, "bucket info failed: tenant: " + tenant + "bucket_name: " + bucket_name + " " + cpp_strerror(-ret)); + return ret; + } + + RGWUserInfo user_info; + ret = rgw_get_user_info_by_uid(store, bucket_info.owner, user_info); + if (ret < 0) { + set_err_msg(err_msg, "user info failed: " + cpp_strerror(-ret)); + return ret; + } + + RGWRados::Bucket target(store, bucket_info); + RGWRados::Bucket::List list_op(&target); + + list_op.params.list_versions = true; + list_op.params.allow_unordered = true; + list_op.params.marker = marker; + + bool is_truncated = false; + int count = 0; + int max_entries = 1000; + + //Loop through objects and update object acls to point to bucket owner + + do { + objs.clear(); + ret = list_op.list_objects(max_entries, &objs, &common_prefixes, &is_truncated); + if (ret < 0) { + set_err_msg(err_msg, "list objects failed: " + cpp_strerror(-ret)); + return ret; + } + + list_op.params.marker = list_op.get_next_marker(); + count += objs.size(); + + for (const auto& obj : objs) { + + const rgw_obj r_obj(bucket_info.bucket, obj.key); + ret = get_obj_attrs(store, obj_ctx, bucket_info, r_obj, attrs); + if (ret < 0){ + set_err_msg(err_msg, "failed to read object " + obj.key.name + "with " + cpp_strerror(-ret)); + continue; + } + const auto& aiter = attrs.find(RGW_ATTR_ACL); + if (aiter == attrs.end()) { + set_err_msg(err_msg, "no acls found for object " + obj.key.name + " .Continuing with next object." + cpp_strerror(-ret)); + continue; + } else { + bufferlist& bl = aiter->second; + RGWAccessControlPolicy policy(store->ctx()); + ACLOwner owner; + try { + decode(policy, bl); + owner = policy.get_owner(); + } catch (buffer::error& err) { + set_err_msg(err_msg, "decode policy failed: "); + return -EIO; + } + + //Get the ACL from the policy + RGWAccessControlList& acl = policy.get_acl(); + + //Remove grant that is set to old owner + acl.remove_canon_user_grant(owner.get_id()); + + //Create a grant and add grant + ACLGrant grant; + grant.set_canon(bucket_info.owner, user_info.display_name, RGW_PERM_FULL_CONTROL); + acl.add_grant(&grant); + + //Update the ACL owner to the new user + owner.set_id(bucket_info.owner); + owner.set_name(user_info.display_name); + policy.set_owner(owner); + + bl.clear(); + encode(policy, bl); + + ret = modify_obj_attr(store, obj_ctx, bucket_info, r_obj, RGW_ATTR_ACL, bl); + if (ret < 0) { + set_err_msg(err_msg, "modify attr failed: " + cpp_strerror(-ret)); + return ret; + } + } + } cerr << count << " objects processed, next marker " << list_op.params.marker.name << std::endl; + } while(is_truncated); + return 0; +} + int RGWBucket::unlink(RGWBucketAdminOpState& op_state, std::string *err_msg) { rgw_bucket bucket = op_state.get_bucket(); @@ -1433,6 +1541,23 @@ int RGWBucketAdminOp::link(RGWRados *store, RGWBucketAdminOpState& op_state, str } +int RGWBucketAdminOp::chown(RGWRados *store, RGWBucketAdminOpState& op_state, const string& marker, string *err) +{ + RGWBucket bucket; + map attrs; + + int ret = bucket.init(store, op_state, err, &attrs); + if (ret < 0) + return ret; + + ret = bucket.link(op_state, attrs, err); + if (ret < 0) + return ret; + + return bucket.chown(op_state, attrs, marker, err); + +} + int RGWBucketAdminOp::check_index(RGWRados *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, optional_yield y) { diff --git a/src/rgw/rgw_bucket.h b/src/rgw/rgw_bucket.h index 166f2e4a0619e..202eee0e903cb 100644 --- a/src/rgw/rgw_bucket.h +++ b/src/rgw/rgw_bucket.h @@ -343,6 +343,7 @@ public: int remove(RGWBucketAdminOpState& op_state, optional_yield y, bool bypass_gc = false, bool keep_index_consistent = true, std::string *err_msg = NULL); int link(RGWBucketAdminOpState& op_state, map& attrs, std::string *err_msg = NULL); + int chown(RGWBucketAdminOpState& op_state, map& attrs, const string& marker, std::string *err_msg = NULL); int unlink(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL); int set_quota(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL); @@ -367,6 +368,7 @@ public: static int unlink(RGWRados *store, RGWBucketAdminOpState& op_state); static int link(RGWRados *store, RGWBucketAdminOpState& op_state, string *err_msg = NULL); + static int chown(RGWRados *store, RGWBucketAdminOpState& op_state, const string& marker, string *err_msg = NULL); static int check_index(RGWRados *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher, optional_yield y); diff --git a/src/rgw/rgw_op.cc b/src/rgw/rgw_op.cc index 11c810aa8926d..0f2d53a265e23 100644 --- a/src/rgw/rgw_op.cc +++ b/src/rgw/rgw_op.cc @@ -321,9 +321,9 @@ vector get_iam_user_policy_from_attr(CephContext* cct, return policies; } -static int get_obj_attrs(RGWRados *store, struct req_state *s, const rgw_obj& obj, map& attrs) +int get_obj_attrs(RGWRados *store, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& obj, map& attrs) { - RGWRados::Object op_target(store, s->bucket_info, *static_cast(s->obj_ctx), obj); + RGWRados::Object op_target(store, bucket_info, obj_ctx, obj); RGWRados::Object::Read read_op(&op_target); read_op.params.attrs = &attrs; @@ -440,14 +440,14 @@ static int get_multipart_info(RGWRados *store, struct req_state *s, return get_multipart_info(store, s, meta_obj, policy, attrs, upload_info); } -static int modify_obj_attr(RGWRados *store, struct req_state *s, const rgw_obj& obj, const char* attr_name, bufferlist& attr_val) +int modify_obj_attr(RGWRados *store, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& obj, const char* attr_name, bufferlist& attr_val) { map attrs; - RGWRados::Object op_target(store, s->bucket_info, *static_cast(s->obj_ctx), obj); + RGWRados::Object op_target(store, bucket_info, obj_ctx, obj); RGWRados::Object::Read read_op(&op_target); read_op.params.attrs = &attrs; - + int r = read_op.prepare(s->yield); if (r < 0) { return r; @@ -813,7 +813,7 @@ static int rgw_iam_add_tags_from_bl(struct req_state* s, bufferlist& bl){ static int rgw_iam_add_existing_objtags(RGWRados* store, struct req_state* s, rgw_obj& obj, std::uint64_t action){ map attrs; store->set_atomic(s->obj_ctx, obj); - int op_ret = get_obj_attrs(store, s, obj, attrs); + int op_ret = get_obj_attrs(store, *(s->obj_ctx), s->bucket_info, obj, attrs); if (op_ret < 0) return op_ret; auto tags = attrs.find(RGW_ATTR_TAGS); @@ -1045,7 +1045,7 @@ void RGWGetObjTags::execute() store->set_atomic(s->obj_ctx, obj); - op_ret = get_obj_attrs(store, s, obj, attrs); + op_ret = get_obj_attrs(store, *(s->obj_ctx), s->bucket_info, obj, attrs); if (op_ret < 0) { ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << obj << " ret=" << op_ret << dendl; @@ -1097,7 +1097,7 @@ void RGWPutObjTags::execute() rgw_obj obj; obj = rgw_obj(s->bucket, s->object); store->set_atomic(s->obj_ctx, obj); - op_ret = modify_obj_attr(store, s, obj, RGW_ATTR_TAGS, tags_bl); + op_ret = modify_obj_attr(store, *(s->obj_ctx), s->bucket_info, obj, RGW_ATTR_TAGS, tags_bl); if (op_ret == -ECANCELED){ op_ret = -ERR_TAG_CONFLICT; } @@ -4532,7 +4532,7 @@ void RGWPutMetadataObject::execute() } /* check if obj exists, read orig attrs */ - op_ret = get_obj_attrs(store, s, obj, orig_attrs); + op_ret = get_obj_attrs(store, *(s->obj_ctx), s->bucket_info, obj, orig_attrs); if (op_ret < 0) { return; } @@ -4689,7 +4689,7 @@ void RGWDeleteObj::execute() if (!s->object.empty()) { if (need_object_expiration() || multipart_delete) { /* check if obj exists, read orig attrs */ - op_ret = get_obj_attrs(store, s, obj, attrs); + op_ret = get_obj_attrs(store, *(s->obj_ctx), s->bucket_info, obj, attrs); if (op_ret < 0) { return; } @@ -5355,7 +5355,7 @@ void RGWPutACLs::execute() obj = rgw_obj(s->bucket, s->object); store->set_atomic(s->obj_ctx, obj); //if instance is empty, we should modify the latest object - op_ret = modify_obj_attr(store, s, obj, RGW_ATTR_ACL, bl); + op_ret = modify_obj_attr(store, *(s->obj_ctx), s->bucket_info, obj, RGW_ATTR_ACL, bl); } else { attrs = s->bucket_attrs; attrs[RGW_ATTR_ACL] = bl; @@ -5916,7 +5916,7 @@ void RGWCompleteMultipart::execute() return; } - op_ret = get_obj_attrs(store, s, meta_obj, attrs); + op_ret = get_obj_attrs(store, *(s->obj_ctx), s->bucket_info, meta_obj, attrs); if (op_ret < 0) { ldpp_dout(this, 0) << "ERROR: failed to get obj attrs, obj=" << meta_obj diff --git a/src/rgw/rgw_op.h b/src/rgw/rgw_op.h index ecbedde64adbe..d7128111b878d 100644 --- a/src/rgw/rgw_op.h +++ b/src/rgw/rgw_op.h @@ -2380,5 +2380,7 @@ static inline int parse_value_and_bound( return 0; } +int get_obj_attrs(RGWRados *store, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& obj, map& attrs); +int modify_obj_attr(RGWRados *store, RGWObjectCtx& obj_ctx, RGWBucketInfo& bucket_info, const rgw_obj& obj, const char* attr_name, bufferlist& attr_val); #endif /* CEPH_RGW_OP_H */ diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index cb8f46ca46843..a24f94497f9a1 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -25,6 +25,7 @@ bucket stats returns bucket statistics bucket rm remove bucket bucket check check bucket index + bucket chown link bucket to specified user and update its object ACLs bucket reshard reshard bucket bucket rewrite rewrite all objects in the specified bucket bucket sync disable disable bucket sync -- 2.39.5