existing user are moved under the new user namespace specified by "--new-uid".
It calls bucket link and bucket chown to link the buckets and objects to the new
user namespace. Access and secret keys of the user(and the subusers) are preserved.
Usage: "radosgw-admin user rename --uid=<> --new-uid=<>"
Signed-off-by: Shilpa Jagannath <smanjara@redhat.com>
Display information of a user, and any potentially available
subusers and keys.
+:command:`user rename`
+ Renames a user.
+
:command:`user rm`
Remove a user.
The radosgw user ID.
+.. option:: --new-uid=uid
+
+ ID of the new user. Used with 'user rename' command.
+
.. option:: --subuser=<name>
Name of the subuser.
Remove a user::
$ radosgw-admin user rm --uid=johnny
+
+Rename a user::
+
+ $ radosgw-admin user rename --uid=johny --new-uid=joe
Remove a user and all associated buckets with their contents::
cout << " user create create a new user\n" ;
cout << " user modify modify user\n";
cout << " user info get user info\n";
+ cout << " user rename rename user\n";
cout << " user rm remove user\n";
cout << " user suspend suspend a user\n";
cout << " user enable re-enable user after suspension\n";
cout << "options:\n";
cout << " --tenant=<tenant> tenant name\n";
cout << " --uid=<id> user id\n";
+ cout << " --new-uid=<id> new user id\n";
cout << " --subuser=<name> subuser name\n";
cout << " --access-key=<key> S3 access key\n";
cout << " --email=<email> user's email address\n";
OPT_USER_CREATE,
OPT_USER_INFO,
OPT_USER_MODIFY,
+ OPT_USER_RENAME,
OPT_USER_RM,
OPT_USER_SUSPEND,
OPT_USER_ENABLE,
return OPT_USER_INFO;
if (strcmp(cmd, "modify") == 0)
return OPT_USER_MODIFY;
+ if (strcmp(cmd, "rename") == 0)
+ return OPT_USER_RENAME;
if (strcmp(cmd, "rm") == 0)
return OPT_USER_RM;
if (strcmp(cmd, "suspend") == 0)
rgw_user user_id;
string tenant;
+ rgw_user new_user_id;
std::string access_key, secret_key, user_email, display_name;
std::string bucket_name, pool_name, object;
rgw_pool pool;
break;
} else if (ceph_argparse_witharg(args, i, &val, "-i", "--uid", (char*)NULL)) {
user_id.from_str(val);
+ } else if (ceph_argparse_witharg(args, i, &val, "-i", "--new-uid", (char*)NULL)) {
+ new_user_id.from_str(val);
} else if (ceph_argparse_witharg(args, i, &val, "--tenant", (char*)NULL)) {
tenant = val;
} else if (ceph_argparse_witharg(args, i, &val, "--access-key", (char*)NULL)) {
}
user_id.tenant = tenant;
}
+
+ if (!new_user_id.empty() && !tenant.empty()) {
+ new_user_id.tenant = tenant;
+ }
+
/* check key parameter conflict */
if ((!access_key.empty()) && gen_access_key) {
cerr << "ERROR: key parameter conflict, --access-key & --gen-access-key" << std::endl;
if (!user_email.empty())
user_op.set_user_email(user_email);
+ if (!user_id.empty()) {
+ user_op.set_new_user_id(new_user_id);
+ }
+
if (!access_key.empty())
user_op.set_access_key(access_key);
}
output_user_info = false;
+ break;
+ case OPT_USER_RENAME:
+ ret = user.rename(user_op, &err_msg);
+ if (ret < 0) {
+ cerr << "could not rename user: " << err_msg << std::endl;
+ return -ret;
+ }
+
break;
case OPT_USER_ENABLE:
case OPT_USER_SUSPEND:
return 0;
}
+int rgw_bucket_chown(RGWRados* const store, RGWUserInfo& user_info, RGWBucketInfo& bucket_info, const string& marker, map<string, bufferlist>& attrs)
+{
+ RGWObjectCtx obj_ctx(store);
+ std::vector<rgw_bucket_dir_entry> objs;
+ map<string, bool> common_prefixes;
+
+ 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();
+ int ret = list_op.list_objects(max_entries, &objs, &common_prefixes, &is_truncated);
+ if (ret < 0) {
+ ldout(store->ctx(), 0) << "ERROR: list objects failed: " << cpp_strerror(-ret) << dendl;
+ 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){
+ ldout(store->ctx(), 0) << "ERROR: failed to read object " << obj.key.name << cpp_strerror(-ret) << dendl;
+ continue;
+ }
+ const auto& aiter = attrs.find(RGW_ATTR_ACL);
+ if (aiter == attrs.end()) {
+ ldout(store->ctx(), 0) << "ERROR: no acls found for object " << obj.key.name << " .Continuing with next object." << dendl;
+ continue;
+ } else {
+ bufferlist& bl = aiter->second;
+ RGWAccessControlPolicy policy(store->ctx());
+ ACLOwner owner;
+ try {
+ decode(policy, bl);
+ owner = policy.get_owner();
+ } catch (buffer::error& err) {
+ ldout(store->ctx(), 0) << "ERROR: decode policy failed" << err << dendl;
+ 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) {
+ ldout(store->ctx(), 0) << "ERROR: modify attr failed " << cpp_strerror(-ret) << dendl;
+ return ret;
+ }
+ }
+ } cerr << count << " objects processed in " << bucket_info.bucket.name << " .Next marker " << list_op.params.marker.name << std::endl;
+ } while(is_truncated);
+ return 0;
+}
+
int rgw_link_bucket(RGWRados* const store,
const rgw_user& user_id,
rgw_bucket& bucket,
if (!bucket_name.empty()) {
ceph::real_time mtime;
int r = store->get_bucket_info(obj_ctx, bucket_tenant, bucket_name,
- bucket_info, &mtime, pattrs);
+ bucket_info, &mtime, null_yield, pattrs);
if (r < 0) {
set_err_msg(err_msg, "failed to fetch bucket info for bucket=" + bucket_name);
ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket_name << dendl;
store->get_bucket_instance_obj(bucket, obj_bucket_instance);
auto inst_sysobj = obj_ctx.get_obj(obj_bucket_instance);
- r = sysobj.wop()
+ r = inst_sysobj.wop()
.set_objv_tracker(&objv_tracker)
.write_attr(RGW_ATTR_ACL, aclbl, null_yield);
if (r < 0) {
int RGWBucket::chown(RGWBucketAdminOpState& op_state,
map<string, bufferlist>& 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<rgw_bucket_dir_entry> objs;
- map<string, bool> 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);
+ int ret = store->get_bucket_info(sys_ctx, tenant, bucket_name, bucket_info, NULL, null_yield, &attrs);
if (ret < 0) {
set_err_msg(err_msg, "bucket info failed: tenant: " + tenant + "bucket_name: " + bucket_name + " " + cpp_strerror(-ret));
return 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;
+ ret = rgw_bucket_chown(store, user_info, bucket_info, marker, attrs);
+ if (ret < 0) {
+ set_err_msg(err_msg, "Failed to change object ownership" + cpp_strerror(-ret));
+ }
+
+ return ret;
}
int RGWBucket::unlink(RGWBucketAdminOpState& op_state, std::string *err_msg)
rgw_ep_info *pinfo = nullptr);
extern int rgw_unlink_bucket(RGWRados *store, const rgw_user& user_id,
const string& tenant_name, const string& bucket_name, bool update_entrypoint = true);
-
+extern int rgw_bucket_chown(RGWRados* const store, RGWUserInfo& user_info, RGWBucketInfo& bucket_info,
+ const string& marker, map<string, bufferlist>& attrs);
extern int rgw_remove_object(RGWRados *store, const RGWBucketInfo& bucket_info, const rgw_bucket& bucket, rgw_obj_key& key);
extern int rgw_remove_bucket(RGWRados *store, rgw_bucket& bucket, bool delete_children, optional_yield y);
extern int rgw_remove_bucket_bypass_gc(RGWRados *store, rgw_bucket& bucket, int concurrent_max, optional_yield y);
return 0;
}
+int RGWUser::execute_user_rename(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+
+ // unlink buckets from existing user
+ rgw_user& old_uid = op_state.get_user_id();
+ RGWUserInfo old_user_info = op_state.get_user_info();
+
+ bool is_truncated = false;
+ string marker;
+ CephContext *cct = store->ctx();
+ size_t max_buckets = cct->_conf->rgw_list_buckets_max_chunk;
+ vector<rgw_bucket> read_buckets;
+ do {
+ RGWUserBuckets buckets;
+ int ret = rgw_read_user_buckets(store, old_uid, buckets, marker, string(),
+ max_buckets, false, &is_truncated);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to read user bucket info");
+ return ret;
+ }
+
+ map<std::string, RGWBucketEnt>& m = buckets.get_buckets();
+
+ std::map<std::string, RGWBucketEnt>::iterator it;
+ for (it = m.begin(); it != m.end(); ++it) {
+ RGWBucketEnt obj = it->second;
+ read_buckets.push_back(obj.bucket); //Save the list of all buckets of the user to be able to use after bucket unlink
+ ret = rgw_unlink_bucket(store, old_uid, (obj.bucket.tenant), (obj.bucket.name));
+ if (ret < 0) {
+ set_err_msg(err_msg, "error unlinking bucket " + cpp_strerror(-ret));
+ return ret;
+ }
+
+ marker = it->first;
+ }
+
+ } while (is_truncated);
+
+ // delete old user
+ std::string subprocess_msg;
+ int ret = execute_remove(op_state, &subprocess_msg);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to remove existing user, " + subprocess_msg);
+ return ret;
+ }
+
+ // create new user with old user's attributes
+ rgw_user& uid = op_state.get_new_uid();
+
+ if (old_uid.tenant != uid.tenant) {
+ set_err_msg(err_msg, "Users have to be under the same tenant namespace "
+ + old_uid.tenant + "!=" + uid.tenant);
+ }
+
+ string display_name = old_user_info.display_name;
+ RGWUserAdminOpState new_op_state;
+ new_op_state.set_user_id(uid);
+
+ ret = execute_rename(new_op_state, old_user_info, &subprocess_msg);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to create new user, " + subprocess_msg);
+ return ret;
+ }
+
+ RGWUserInfo user_info;
+ ret = rgw_get_user_info_by_uid(store, uid, user_info);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to fetch user info");
+ return ret;
+ }
+
+
+ // link bucket and objects to new user
+ ACLOwner owner;
+ RGWAccessControlPolicy policy_instance;
+ policy_instance.create_default(user_info.user_id, display_name);
+ owner = policy_instance.get_owner();
+ bufferlist aclbl;
+ policy_instance.encode(aclbl);
+
+ map<string, bufferlist> attrs;
+ for (vector<rgw_bucket>::iterator iter = read_buckets.begin(); iter != read_buckets.end(); ++iter) {
+
+ RGWBucketInfo bucket_info;
+ rgw_bucket bucket = *iter;
+ RGWSysObjectCtx sys_ctx = store->svc.sysobj->init_obj_ctx();
+
+ ret = store->get_bucket_info(sys_ctx, bucket.tenant, bucket.name,
+ bucket_info, NULL, null_yield, &attrs);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to fetch bucket info for bucket= " + bucket.name);
+ return ret;
+ }
+
+ RGWObjVersionTracker objv_tracker;
+ RGWObjVersionTracker old_version = bucket_info.objv_tracker;
+
+ ret = store->set_bucket_owner(bucket, owner);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to set bucket owner: " + cpp_strerror(-ret));
+ return ret;
+ }
+
+ const rgw_pool& root_pool = store->svc.zone->get_zone_params().domain_root;
+ std::string bucket_entry;
+ rgw_make_bucket_entry_name(bucket.tenant, bucket.name, bucket_entry);
+ rgw_raw_obj obj(root_pool, bucket_entry);
+ auto obj_ctx = store->svc.sysobj->init_obj_ctx();
+ auto sysobj = obj_ctx.get_obj(obj);
+ rgw_raw_obj obj_bucket_instance;
+
+ store->get_bucket_instance_obj(bucket, obj_bucket_instance);
+ auto inst_sysobj = obj_ctx.get_obj(obj_bucket_instance);
+ ret = inst_sysobj.wop()
+ .set_objv_tracker(&objv_tracker)
+ .write_attr(RGW_ATTR_ACL, aclbl, null_yield);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to set new acl on bucket " + bucket.name);
+ return ret;
+ }
+
+ RGWBucketEntryPoint ep;
+ ep.bucket = bucket_info.bucket;
+ ep.owner = user_info.user_id;
+ ep.creation_time = bucket_info.creation_time;
+ ep.linked = true;
+ map<string, bufferlist> ep_attrs;
+ rgw_ep_info ep_data{ep, ep_attrs};
+
+ ret = rgw_link_bucket(store, user_info.user_id, bucket_info.bucket,
+ ceph::real_time(), true, &ep_data);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to link bucket " + bucket.name + " to new user");
+ return ret;
+ }
+
+ RGWBucketInfo new_bucket_info;
+ ret = store->get_bucket_info(sys_ctx, bucket.tenant, bucket.name,
+ new_bucket_info, NULL, null_yield, &attrs);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to fetch bucket info for bucket= " + bucket.name);
+ return ret;
+ }
+
+ ret = rgw_bucket_chown(store, user_info, new_bucket_info, marker, attrs);
+ if (ret < 0) {
+ set_err_msg(err_msg, "failed to run bucket chown" + cpp_strerror(-ret));
+ return ret;
+ }
+ }
+ return 0;
+}
+
+int RGWUser:: execute_rename(RGWUserAdminOpState& op_state, RGWUserInfo& old_user_info, std::string *err_msg)
+{
+ std::string subprocess_msg;
+ int ret = 0;
+ bool defer_user_update = true;
+
+ rgw_user& user_id = op_state.get_user_id();
+
+ RGWUserInfo user_info;
+ user_info = old_user_info;
+ user_info.user_id = user_id;
+
+ // update swift_keys with new user id
+ auto modify_keys = user_info.swift_keys;
+ map<string, RGWAccessKey>::iterator it;
+
+ user_info.swift_keys.clear();
+
+ for (it = modify_keys.begin(); it != modify_keys.end(); it++) {
+
+ RGWAccessKey old_key;
+ old_key = it->second;
+
+ std::string id;
+ user_id.to_str(id);
+ id.append(":");
+ id.append(old_key.subuser);
+
+ old_key.id = id;
+ user_info.swift_keys[id] = old_key;
+ }
+
+ op_state.set_user_info(user_info);
+ op_state.set_populated();
+ op_state.set_initialized();
+
+ // update the helper objects
+ ret = init_members(op_state);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to initialize user");
+ return ret;
+ }
+
+ // see if we need to add an access key
+ if (op_state.has_key_op()) {
+ ret = keys.add(op_state, &subprocess_msg, defer_user_update);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to create access key, " + subprocess_msg);
+ return ret;
+ }
+ }
+
+ ret = update(op_state, err_msg);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+
int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg)
{
std::string subprocess_msg;
return 0;
}
+
int RGWUser::add(RGWUserAdminOpState& op_state, std::string *err_msg)
{
std::string subprocess_msg;
return 0;
}
+int RGWUser::rename(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+ std::string subprocess_msg;
+ int ret;
+
+ ret = check_op(op_state, &subprocess_msg);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to parse parameters, " + subprocess_msg);
+ return ret;
+ }
+
+ ret = execute_user_rename(op_state, &subprocess_msg);
+ if (ret < 0) {
+ set_err_msg(err_msg, "unable to rename user, " + subprocess_msg);
+ return ret;
+ }
+
+ return 0;
+}
+
int RGWUser::execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg, optional_yield y)
{
int ret;
rgw_user user_id;
std::string user_email;
std::string display_name;
+ rgw_user new_user_id;
int32_t max_buckets;
__u8 suspended;
__u8 admin;
user_id = id;
}
+ void set_new_user_id(rgw_user& id) {
+ if (id.empty())
+ return;
+
+ new_user_id = id;
+ }
+
void set_user_email(std::string& email) {
/* always lowercase email address */
boost::algorithm::to_lower(email);
std::string get_caps() { return caps; }
std::string get_user_email() { return user_email; }
std::string get_display_name() { return display_name; }
+ rgw_user& get_new_uid() { return new_user_id; }
map<int, std::string>& get_temp_url_keys() { return temp_url_keys; }
RGWUserInfo& get_user_info() { return info; }
int execute_remove(RGWUserAdminOpState& op_state,
std::string *err_msg, optional_yield y);
int execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg);
+ int execute_user_rename(RGWUserAdminOpState& op_state, std::string *err_msg);
+ int execute_rename(RGWUserAdminOpState& op_state, RGWUserInfo& old_user_info, std::string *err_msg);
public:
RGWUser();
/* API Contracted Methods */
int add(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
int remove(RGWUserAdminOpState& op_state, optional_yield y, std::string *err_msg = NULL);
+ int rename(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
/* remove an already populated RGWUser */
int remove(std::string *err_msg = NULL);