From 0e73c9dd66e4aa32f5fc59d7aa8074614f25553e Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Mon, 25 Nov 2024 12:24:07 -0500 Subject: [PATCH] radosgw-admin: bucket link/unlink support accounts buckets are owned by accounts instead of their individual users add support for --account-id to 'bucket link' and 'bucket unlink' reject 'bucket link' when the requested --uid belongs to an account Fixes: https://tracker.ceph.com/issues/69042 Signed-off-by: Casey Bodley --- qa/tasks/radosgw_admin.py | 35 +++++++++++++++++ src/rgw/driver/rados/rgw_bucket.cc | 51 ++++++++++++++++++++----- src/rgw/driver/rados/rgw_bucket.h | 1 + src/rgw/driver/rados/rgw_rest_bucket.cc | 2 + 4 files changed, 80 insertions(+), 9 deletions(-) diff --git a/qa/tasks/radosgw_admin.py b/qa/tasks/radosgw_admin.py index 3b98702accaae..fb82378761bc7 100644 --- a/qa/tasks/radosgw_admin.py +++ b/qa/tasks/radosgw_admin.py @@ -16,6 +16,7 @@ import logging import time import datetime import sys +import errno from io import StringIO from queue import Queue @@ -725,6 +726,40 @@ def task(ctx, config): (err, out) = rgwadmin(ctx, client, ['user', 'rm', '--tenant', tenant_name, '--uid', 'tenanteduser'], check_status=True) + account_id = 'RGW12312312312312312' + account_name = 'testacct' + rgwadmin(ctx, client, [ + 'account', 'create', + '--account-id', account_id, + '--account-name', account_name, + ], check_status=True) + rgwadmin(ctx, client, [ + 'user', 'create', + '--account-id', account_id, + '--uid', 'testacctuser', + '--display-name', 'accountuser', + '--gen-access-key', + '--gen-secret', + ], check_status=True) + + # TESTCASE 'bucket link', 'bucket', 'account user', 'fails' + (err, out) = rgwadmin(ctx, client, ['bucket', 'link', '--bucket', bucket_name, '--uid', 'testacctuser']) + assert err == errno.EINVAL + + rgwadmin(ctx, client, ['user', 'rm', '--uid', 'testacctuser'], check_status=True) + + # TESTCASE 'bucket link', 'bucket', 'account', 'succeeds' + rgwadmin(ctx, client, + ['bucket', 'link', '--bucket', bucket_name, '--account-id', account_id], + check_status=True) + + # relink the bucket to the first user and delete the account + rgwadmin(ctx, client, + ['bucket', 'link', '--bucket', bucket_name, '--uid', user1], + check_status=True) + rgwadmin(ctx, client, ['account', 'rm', '--account-id', account_id], + check_status=True) + # TESTCASE 'object-rm', 'object', 'rm', 'remove object', 'succeeds, object is removed' # upload an object diff --git a/src/rgw/driver/rados/rgw_bucket.cc b/src/rgw/driver/rados/rgw_bucket.cc index 21d238d334180..2900272999314 100644 --- a/src/rgw/driver/rados/rgw_bucket.cc +++ b/src/rgw/driver/rados/rgw_bucket.cc @@ -1140,6 +1140,16 @@ int RGWBucketAdminOp::dump_s3_policy(rgw::sal::Driver* driver, RGWBucketAdminOpS int RGWBucketAdminOp::unlink(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, optional_yield y, string *err) { + rgw_owner owner; + if (op_state.is_account_op()) { + owner = op_state.get_account_id(); + } else if (op_state.is_user_op()) { + owner = op_state.get_user_id(); + } else { + set_err_msg(err, "requires user or account id"); + return -EINVAL; + } + auto radosdriver = dynamic_cast(driver); if (!radosdriver) { set_err_msg(err, "rados store only"); @@ -1152,13 +1162,18 @@ int RGWBucketAdminOp::unlink(rgw::sal::Driver* driver, RGWBucketAdminOpState& op return ret; auto* rados = radosdriver->getRados()->get_rados_handle(); - return radosdriver->ctl()->bucket->unlink_bucket(*rados, op_state.get_user_id(), op_state.get_bucket()->get_info().bucket, y, dpp, true); + return radosdriver->ctl()->bucket->unlink_bucket(*rados, owner, op_state.get_bucket()->get_info().bucket, y, dpp, true); } int RGWBucketAdminOp::link(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_state, const DoutPrefixProvider *dpp, optional_yield y, string *err) { - if (!op_state.is_user_op()) { - set_err_msg(err, "empty user id"); + rgw_owner new_owner; + if (op_state.is_account_op()) { + new_owner = op_state.get_account_id(); + } else if (op_state.is_user_op()) { + new_owner = op_state.get_user_id(); + } else { + set_err_msg(err, "requires user or account id"); return -EINVAL; } auto radosdriver = dynamic_cast(driver); @@ -1172,8 +1187,26 @@ int RGWBucketAdminOp::link(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_s if (ret < 0) return ret; + std::string display_name; + if (op_state.is_account_op()) { + RGWAccountInfo info; + rgw::sal::Attrs attrs; + RGWObjVersionTracker objv; + ret = driver->load_account_by_id(dpp, y, op_state.get_account_id(), + info, attrs, objv); + if (ret < 0) { + set_err_msg(err, "failed to load account"); + return ret; + } + display_name = std::move(info.name); + } else if (!bucket.get_user()->get_info().account_id.empty()) { + set_err_msg(err, "account users cannot own buckets. use --account-id instead"); + return -EINVAL; + } else { + display_name = bucket.get_user()->get_display_name(); + } + string bucket_id = op_state.get_bucket_id(); - std::string display_name = op_state.get_user_display_name(); std::unique_ptr loc_bucket; std::unique_ptr old_bucket; @@ -1187,7 +1220,7 @@ int RGWBucketAdminOp::link(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_s old_bucket = loc_bucket->clone(); - loc_bucket->get_key().tenant = op_state.get_user_id().tenant; + loc_bucket->get_key().tenant = op_state.get_tenant(); if (!op_state.new_bucket_name.empty()) { auto pos = op_state.new_bucket_name.find('/'); @@ -1236,14 +1269,14 @@ int RGWBucketAdminOp::link(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_s } RGWAccessControlPolicy policy_instance; - policy_instance.create_default(op_state.get_user_id(), display_name); + policy_instance.create_default(new_owner, display_name); owner = policy_instance.get_owner(); aclbl.clear(); policy_instance.encode(aclbl); bool exclusive = false; - loc_bucket->get_info().owner = op_state.get_user_id(); + loc_bucket->get_info().owner = new_owner; if (*loc_bucket != *old_bucket) { loc_bucket->get_info().bucket = loc_bucket->get_key(); loc_bucket->get_info().objv_tracker.version_for_read()->ver = 0; @@ -1259,13 +1292,13 @@ int RGWBucketAdminOp::link(rgw::sal::Driver* driver, RGWBucketAdminOpState& op_s /* link to user */ RGWBucketEntryPoint ep; ep.bucket = loc_bucket->get_info().bucket; - ep.owner = op_state.get_user_id(); + ep.owner = new_owner; ep.creation_time = loc_bucket->get_info().creation_time; ep.linked = true; rgw::sal::Attrs ep_attrs; rgw_ep_info ep_data{ep, ep_attrs}; - r = radosdriver->ctl()->bucket->link_bucket(*rados, op_state.get_user_id(), loc_bucket->get_info().bucket, loc_bucket->get_info().creation_time, y, dpp, true, &ep_data); + r = radosdriver->ctl()->bucket->link_bucket(*rados, new_owner, loc_bucket->get_info().bucket, loc_bucket->get_info().creation_time, y, dpp, true, &ep_data); if (r < 0) { set_err_msg(err, "failed to relink bucket"); return r; diff --git a/src/rgw/driver/rados/rgw_bucket.h b/src/rgw/driver/rados/rgw_bucket.h index 85434ba7299cb..9ee31c8814e60 100644 --- a/src/rgw/driver/rados/rgw_bucket.h +++ b/src/rgw/driver/rados/rgw_bucket.h @@ -361,6 +361,7 @@ public: void clear_failure() { failure = false; } const RGWBucketInfo& get_bucket_info() const { return bucket->get_info(); } + rgw::sal::User* get_user() { return user.get(); } }; class RGWBucketAdminOp { diff --git a/src/rgw/driver/rados/rgw_rest_bucket.cc b/src/rgw/driver/rados/rgw_rest_bucket.cc index dc71e40335ffa..0c3f70296049a 100644 --- a/src/rgw/driver/rados/rgw_rest_bucket.cc +++ b/src/rgw/driver/rados/rgw_rest_bucket.cc @@ -141,6 +141,7 @@ void RGWOp_Bucket_Link::execute(optional_yield y) RGWBucketAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); + RESTArgs::get_string(s, "account-id", op_state.account_id, &op_state.account_id); RESTArgs::get_string(s, "bucket", bucket, &bucket); RESTArgs::get_string(s, "bucket-id", bucket_id, &bucket_id); RESTArgs::get_string(s, "new-bucket-name", new_bucket_name, &new_bucket_name); @@ -184,6 +185,7 @@ void RGWOp_Bucket_Unlink::execute(optional_yield y) RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); + RESTArgs::get_string(s, "account-id", op_state.account_id, &op_state.account_id); RESTArgs::get_string(s, "bucket", bucket, &bucket); op_state.set_user_id(uid); -- 2.39.5