]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
radosgw-admin: bucket link/unlink support accounts 60829/head
authorCasey Bodley <cbodley@redhat.com>
Mon, 25 Nov 2024 17:24:07 +0000 (12:24 -0500)
committerCasey Bodley <cbodley@redhat.com>
Fri, 6 Dec 2024 14:09:39 +0000 (09:09 -0500)
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 <cbodley@redhat.com>
qa/tasks/radosgw_admin.py
src/rgw/driver/rados/rgw_bucket.cc
src/rgw/driver/rados/rgw_bucket.h
src/rgw/driver/rados/rgw_rest_bucket.cc

index 3b98702accaae65de8bc3c2edd1e16abd69b8d73..fb82378761bc701bc42a18a8dfb26fc0cba4147b 100644 (file)
@@ -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
index 21d238d33418010c69627a22a1de96881c256bf7..29002729993147cd3b98033b5b7c5aa130a6c3dc 100644 (file)
@@ -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<rgw::sal::RadosStore*>(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<rgw::sal::RadosStore*>(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<rgw::sal::Bucket> loc_bucket;
   std::unique_ptr<rgw::sal::Bucket> 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;
index 85434ba7299cbe4dcfcfef2a2e58d7af202cabc9..9ee31c8814e604fec80931b2220edacb8ceb0677 100644 (file)
@@ -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 {
index dc71e40335ffa9dbfc7fba82f2c3d2856035c35a..0c3f70296049a0fb8078f70c37408ac0d449f8f0 100644 (file)
@@ -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);