]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
This commit deals with renaming user. Under the hood, the attributes of an
authorShilpa Jagannath <smanjara@redhat.com>
Wed, 19 Jun 2019 09:34:03 +0000 (15:04 +0530)
committerShilpa Jagannath <smanjara@redhat.com>
Tue, 30 Jul 2019 08:30:45 +0000 (14:00 +0530)
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>
doc/man/8/radosgw-admin.rst
src/rgw/rgw_admin.cc
src/rgw/rgw_bucket.cc
src/rgw/rgw_bucket.h
src/rgw/rgw_user.cc
src/rgw/rgw_user.h

index 7cc9c26cc0c329c79ae38b530bb5e59983ea0563..1fdb7f48eea7988d3d57e10750552eb8eceb7cfb 100644 (file)
@@ -35,6 +35,9 @@ which are as follows:
   Display information of a user, and any potentially available
   subusers and keys.
 
+:command:`user rename`
+  Renames a user.
+
 :command:`user rm`
   Remove a user.
 
@@ -467,6 +470,10 @@ Options
 
    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.
@@ -895,6 +902,10 @@ Generate a new user::
 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::
 
index 6a2ad126bfb1b975f18ece7f11fea31deb9217a2..dc6019955a3de965037da5aba0585c7a9d50c8f3 100644 (file)
@@ -84,6 +84,7 @@ void usage()
   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";
@@ -243,6 +244,7 @@ void usage()
   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";
@@ -390,6 +392,7 @@ enum {
   OPT_USER_CREATE,
   OPT_USER_INFO,
   OPT_USER_MODIFY,
+  OPT_USER_RENAME,
   OPT_USER_RM,
   OPT_USER_SUSPEND,
   OPT_USER_ENABLE,
@@ -644,6 +647,8 @@ static int get_cmd(const char *cmd, const char *prev_cmd, const char *prev_prev_
       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)
@@ -2748,6 +2753,7 @@ int main(int argc, const char **argv)
 
   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;
@@ -2907,6 +2913,8 @@ int main(int argc, const char **argv)
       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)) {
@@ -3313,6 +3321,11 @@ int main(int argc, const char **argv)
       }
       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;
@@ -4933,6 +4946,10 @@ int main(int argc, const char **argv)
   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);
 
@@ -5054,6 +5071,14 @@ int main(int argc, const char **argv)
     }
 
     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:
index e1792ad30d07d636feb9ef4c6d0aeb8a5de869dc..be0897b8c6bda9561d6e2266c66894d26adc859f 100644 (file)
@@ -190,6 +190,90 @@ int rgw_bucket_sync_user_stats(RGWRados *store, const string& tenant_name, const
   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,
@@ -826,7 +910,7 @@ int RGWBucket::init(RGWRados *storage, RGWBucketAdminOpState& op_state,
   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;
@@ -939,7 +1023,7 @@ int RGWBucket::link(RGWBucketAdminOpState& op_state,
 
     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) {
@@ -997,20 +1081,15 @@ int RGWBucket::link(RGWBucketAdminOpState& op_state,
 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;
@@ -1023,82 +1102,12 @@ int RGWBucket::chown(RGWBucketAdminOpState& op_state,
     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)
index 202eee0e903cbd38b1245a511921664b0c672fa3..8a4c1a6d30dbce2a2b356561df6670660e8afe13 100644 (file)
@@ -215,7 +215,8 @@ extern int rgw_link_bucket(RGWRados* store,
                            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);
index e93ff9ca7ca4f7fd73a1778a14af77edf73fa894..b3510736717512ddd6f2ac0cc93fd07bafa53d50 100644 (file)
@@ -1936,6 +1936,219 @@ int RGWUser::check_op(RGWUserAdminOpState& op_state, std::string *err_msg)
   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;
@@ -2063,6 +2276,7 @@ int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg)
   return 0;
 }
 
+
 int RGWUser::add(RGWUserAdminOpState& op_state, std::string *err_msg)
 {
   std::string subprocess_msg;
@@ -2083,6 +2297,26 @@ int RGWUser::add(RGWUserAdminOpState& op_state, std::string *err_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;
index 84a5f0b8dd29b17236c85c45175c15e3e98e0696..02805d9d521d7fbbe9b57cb61f6d7599d92e4194 100644 (file)
@@ -159,6 +159,7 @@ struct RGWUserAdminOpState {
   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;
@@ -257,6 +258,13 @@ struct RGWUserAdminOpState {
     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);
@@ -446,6 +454,7 @@ struct RGWUserAdminOpState {
   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; }
@@ -673,6 +682,8 @@ private:
   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();
@@ -693,8 +704,11 @@ public:
 
   /* 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);