]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Create RESTful endpoint for user and bucket administration.
authorcaleb miles <caselim@gmail.com>
Mon, 25 Mar 2013 15:46:34 +0000 (11:46 -0400)
committercaleb miles <caselim@gmail.com>
Tue, 2 Apr 2013 01:44:45 +0000 (21:44 -0400)
Expose the following operations through a RESTful endpoint:
    user create
    user modify
    user remove
    subuser create
    subuser modify
    subuser remove
    key create
    key remove
    bucket list
    bucket stats
    bucket link
    bucket unlink
    bucket check
    bucket remove
    remove object

building on the existing /{admin} endpoint.

Signed-off-by caleb miles <caleb.miles@inktank.com>

19 files changed:
src/Makefile.am
src/rgw/rgw_admin.cc
src/rgw/rgw_bucket.cc [new file with mode: 0644]
src/rgw/rgw_bucket.h [new file with mode: 0644]
src/rgw/rgw_common.cc
src/rgw/rgw_main.cc
src/rgw/rgw_op.cc
src/rgw/rgw_op.h
src/rgw/rgw_rados.cc
src/rgw/rgw_rados.h
src/rgw/rgw_rest.cc
src/rgw/rgw_rest.h
src/rgw/rgw_rest_bucket.cc [new file with mode: 0644]
src/rgw/rgw_rest_bucket.h [new file with mode: 0644]
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_rest_user.cc [new file with mode: 0644]
src/rgw/rgw_rest_user.h [new file with mode: 0644]
src/rgw/rgw_user.cc
src/rgw/rgw_user.h

index 6bd7d00e685f452b07255a8de0acff4b783b467f..290687b431c9c08a892af48c607344d6ac6a3795 100644 (file)
@@ -359,7 +359,8 @@ librgw_a_SOURCES =  \
        rgw/rgw_usage.cc \
        rgw/rgw_json_enc.cc \
        rgw/rgw_user.cc \
-       rgw/rgw_tools.cc \
+       rgw/rgw_bucket.cc\
+        rgw/rgw_tools.cc \
        rgw/rgw_rados.cc \
        rgw/rgw_op.cc \
        rgw/rgw_common.cc \
@@ -388,6 +389,8 @@ radosgw_SOURCES = \
         rgw/rgw_rest_swift.cc \
         rgw/rgw_rest_s3.cc \
         rgw/rgw_rest_usage.cc \
+        rgw/rgw_rest_user.cc \
+        rgw/rgw_rest_bucket.cc \
         rgw/rgw_http_client.cc \
         rgw/rgw_swift.cc \
        rgw/rgw_swift_auth.cc \
@@ -1992,10 +1995,13 @@ noinst_HEADERS = \
        rgw/rgw_rest_s3.h\
        rgw/rgw_rest_admin.h\
        rgw/rgw_rest_usage.h\
-       rgw/rgw_tools.h\
+       rgw/rgw_rest_user.h\
+       rgw/rgw_rest_bucket.h\
+        rgw/rgw_tools.h\
        rgw/rgw_usage.h\
        rgw/rgw_user.h\
-       sample.ceph.conf\
+       rgw/rgw_bucket.h\
+        sample.ceph.conf\
        tools/common.h\
        test/osd/RadosModel.h\
        test/osd/Object.h\
index 304aa8376075fe00c5be492dfb8472bf428d1dc6..01a28b5af768279a9d141e77d5109307ecfa0cfe 100644 (file)
@@ -19,6 +19,7 @@ using namespace std;
 
 #include "common/armor.h"
 #include "rgw_user.h"
+#include "rgw_bucket.h"
 #include "rgw_rados.h"
 #include "rgw_acl.h"
 #include "rgw_acl_s3.h"
@@ -170,21 +171,6 @@ enum {
   OPT_CAPS_RM,
 };
 
-static uint32_t str_to_perm(const char *str)
-{
-  if (strcasecmp(str, "read") == 0)
-    return RGW_PERM_READ;
-  else if (strcasecmp(str, "write") == 0)
-    return RGW_PERM_WRITE;
-  else if (strcasecmp(str, "readwrite") == 0)
-    return RGW_PERM_READ | RGW_PERM_WRITE;
-  else if (strcasecmp(str, "full") == 0)
-    return RGW_PERM_FULL_CONTROL;
-
-  usage_exit();
-  return 0; // unreachable
-}
-
 static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more)
 {
   *need_more = false;
@@ -329,119 +315,8 @@ static void show_user_info(RGWUserInfo& info, Formatter *formatter)
   encode_json("user_info", info, formatter);
   formatter->flush(cout);
   cout << std::endl;
-
-}
-
-static int create_bucket(string bucket_str, string& user_id, string& display_name)
-{
-  RGWAccessControlPolicy policy, old_policy;
-  map<string, bufferlist> attrs;
-  bufferlist aclbl;
-  string no_oid;
-  rgw_obj obj;
-  RGWBucketInfo bucket_info;
-
-  int ret;
-
-  // defaule policy (private)
-  policy.create_default(user_id, display_name);
-  policy.encode(aclbl);
-
-  ret = store->get_bucket_info(NULL, bucket_str, bucket_info);
-  if (ret < 0)
-    return ret;
-
-  rgw_bucket& bucket = bucket_info.bucket;
-
-  ret = store->create_bucket(user_id, bucket, attrs);
-  if (ret && ret != -EEXIST)   
-    goto done;
-
-  obj.init(bucket, no_oid);
-
-  ret = store->set_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
-  if (ret < 0) {
-    cerr << "couldn't set acl on bucket" << std::endl;
-  }
-
-  ret = rgw_add_bucket(store, user_id, bucket);
-
-  dout(20) << "ret=" << ret << dendl;
-
-  if (ret == -EEXIST)
-    ret = 0;
-done:
-  return ret;
-}
-
-static void remove_old_indexes(RGWUserInfo& old_info, RGWUserInfo& new_info)
-{
-  int ret;
-  bool success = true;
-
-  if (!old_info.user_id.empty() && old_info.user_id.compare(new_info.user_id) != 0) {
-    ret = rgw_remove_uid_index(store, old_info.user_id);
-    if (ret < 0 && ret != -ENOENT) {
-      cerr << "ERROR: could not remove index for uid " << old_info.user_id << " return code: " << ret << std::endl;
-      success = false;
-    }
-  }
-
-  if (!old_info.user_email.empty() &&
-      old_info.user_email.compare(new_info.user_email) != 0) {
-    ret = rgw_remove_email_index(store, old_info.user_email);
-    if (ret < 0 && ret != -ENOENT) {
-      cerr << "ERROR: could not remove index for email " << old_info.user_email << " return code: " << ret << std::endl;
-      success = false;
-    }
-  }
-
-  map<string, RGWAccessKey>::iterator old_iter;
-  for (old_iter = old_info.swift_keys.begin(); old_iter != old_info.swift_keys.end(); ++old_iter) {
-    RGWAccessKey& swift_key = old_iter->second;
-    map<string, RGWAccessKey>::iterator new_iter = new_info.swift_keys.find(swift_key.id);
-    if (new_iter == new_info.swift_keys.end()) {
-      ret = rgw_remove_swift_name_index(store, swift_key.id);
-      if (ret < 0 && ret != -ENOENT) {
-        cerr << "ERROR: could not remove index for swift_name " << swift_key.id << " return code: " << ret << std::endl;
-        success = false;
-      }
-    }
-  }
-
-  /* we're not removing access keys here.. keys are removed explicitly using the key rm command and removing the old key
-     index is handled there */
-
-  if (!success)
-    cerr << "ERROR: this should be fixed manually!" << std::endl;
-}
-
-static bool char_is_unreserved_url(char c)
-{
-  if (isalnum(c))
-    return true;
-
-  switch (c) {
-  case '-':
-  case '.':
-  case '_':
-  case '~':
-    return true;
-  default:
-    return false;
-  }
 }
 
-static bool validate_access_key(string& key)
-{
-  const char *p = key.c_str();
-  while (*p) {
-    if (!char_is_unreserved_url(*p))
-      return false;
-    p++;
-  }
-  return true;
-}
 
 static void dump_bucket_usage(map<RGWObjCategory, RGWBucketStats>& stats, Formatter *formatter)
 {
@@ -487,213 +362,27 @@ int bucket_stats(rgw_bucket& bucket, Formatter *formatter)
   return 0;
 }
 
-enum ObjectKeyType {
-  KEY_TYPE_SWIFT,
-  KEY_TYPE_S3,
-};
-
-static void check_bad_index_multipart(RGWRados *store, rgw_bucket& bucket, bool fix)
-{
-  int max = 1000;
-  string prefix;
-  string marker;
-  string delim;
-
-  map<string, bool> common_prefixes;
-  string ns = "multipart";
-
-  bool is_truncated;
-  list<string> objs_to_unlink;
-  map<string, bool> meta_objs;
-  map<string, string> all_objs;
-
-  do {
-    vector<RGWObjEnt> result;
-    int r = store->list_objects(bucket, max, prefix, delim, marker,
-                               result, common_prefixes, false, ns,
-                               &is_truncated, NULL);
-
-    if (r < 0) {
-      cerr << "failed to list objects in bucket=" << bucket << " err=" << cpp_strerror(-r) << std::endl;
-      return;
-    }
-
-    vector<RGWObjEnt>::iterator iter;
-    for (iter = result.begin(); iter != result.end(); ++iter) {
-      RGWObjEnt& ent = *iter;
-
-      rgw_obj obj(bucket, ent.name);
-      obj.set_ns(ns);
-
-      string& oid = obj.object;
-      marker = oid;
-
-      int pos = oid.find_last_of('.');
-      if (pos < 0)
-       continue;
-
-      string name = oid.substr(0, pos);
-      string suffix = oid.substr(pos + 1);
-
-      if (suffix.compare("meta") == 0) {
-       meta_objs[name] = true;
-      } else {
-       all_objs[oid] = name;
-      }
-    }
-
-  } while (is_truncated);
-
-  map<string, string>::iterator aiter;
-  for (aiter = all_objs.begin(); aiter != all_objs.end(); ++aiter) {
-    string& name = aiter->second;
-
-    if (meta_objs.find(name) == meta_objs.end()) {
-      objs_to_unlink.push_back(aiter->first);
-    }
-  }
-
-  if (objs_to_unlink.empty())
-    return;
-
-  if (!fix) {
-    cout << "Need to unlink the following objects from bucket=" << bucket << std::endl;
-  } else {
-    cout << "Unlinking the following objects from bucket=" << bucket << std::endl;
-  }
-  for (list<string>::iterator oiter = objs_to_unlink.begin(); oiter != objs_to_unlink.end(); ++oiter) {
-    cout << *oiter << std::endl;
-  }
-
-  if (fix) {
-    int r = store->remove_objs_from_index(bucket, objs_to_unlink);
-    if (r < 0) {
-      cerr << "ERROR: remove_obj_from_index() returned error: " << cpp_strerror(-r) << std::endl;
-    }
+class StoreDestructor {
+  RGWRados *store;
+public:
+  StoreDestructor(RGWRados *_s) : store(_s) {}
+  ~StoreDestructor() {
+    RGWStoreManager::close_storage(store);
   }
-}
+};
 
-static void check_bad_user_bucket_mapping(RGWRados *store, const string& user_id, bool fix)
+static int init_bucket(string& bucket_name, rgw_bucket& bucket)
 {
-  RGWUserBuckets user_buckets;
-  int ret = rgw_read_user_buckets(store, user_id, user_buckets, false);
-  if (ret < 0) {
-    cerr << "failed to read user buckets: " << cpp_strerror(-ret) << std::endl;
-    return;
-  }
-
-  map<string, RGWBucketEnt>& buckets = user_buckets.get_buckets();
-  for (map<string, RGWBucketEnt>::iterator i = buckets.begin();
-       i != buckets.end();
-       ++i) {
-    RGWBucketEnt& bucket_ent = i->second;
-    rgw_bucket& bucket = bucket_ent.bucket;
-
+  if (!bucket_name.empty()) {
     RGWBucketInfo bucket_info;
-    int r = store->get_bucket_info(NULL, bucket.name, bucket_info);
+    int r = store->get_bucket_info(NULL, bucket_name, bucket_info);
     if (r < 0) {
-      cerr << "could not get bucket info for bucket=" << bucket << std::endl;
-      continue;
-    }
-
-    rgw_bucket& actual_bucket = bucket_info.bucket;
-
-    if (actual_bucket.name.compare(bucket.name) != 0 ||
-        actual_bucket.pool.compare(bucket.pool) != 0 ||
-        actual_bucket.marker.compare(bucket.marker) != 0 ||
-        actual_bucket.bucket_id.compare(bucket.bucket_id) != 0) {
-      cout << "bucket info mismatch: expected " << actual_bucket << " got " << bucket << std::endl;
-      if (fix) {
-       cout << "fixing" << std::endl;
-       r = rgw_add_bucket(store, user_id, actual_bucket);
-       if (r < 0) {
-         cerr << "failed to fix bucket: " << cpp_strerror(-r) << std::endl;
-       }
-      }
-    }
-  }
-}
-
-static int remove_object(RGWRados *store, rgw_bucket& bucket, std::string& object)
-{
-  RGWRadosCtx *rctx = new RGWRadosCtx(store);
-  rgw_obj obj(bucket,object);
-
-  int ret = store->delete_obj(rctx, obj);
-
-  return ret;
-}
-
-static int remove_bucket(RGWRados *store, rgw_bucket& bucket, bool delete_children)
-{
-  map<RGWObjCategory, RGWBucketStats> stats;
-  std::vector<RGWObjEnt> objs;
-  std::string prefix, delim, marker, ns;
-  map<string, bool> common_prefixes;
-  rgw_obj obj;
-  RGWBucketInfo info;
-  bufferlist bl;
-
-  int ret = store->get_bucket_stats(bucket, stats);
-
-  if (ret < 0)
-    return ret;
-
-  obj.bucket = bucket;
-  int max = 1000;
-
-  ret = rgw_get_obj(store, NULL, store->zone.domain_root, bucket.name, bl, NULL);
-
-  bufferlist::iterator iter = bl.begin();
-  try {
-    ::decode(info, iter);
-  } catch (buffer::error& err) {
-    cerr << "ERROR: could not decode buffer info, caught buffer::error" << std::endl;
-    return -EIO;
-  }
-
-  if (delete_children) {
-    ret = store->list_objects(bucket, max, prefix, delim, marker, objs, common_prefixes,
-                                 false, ns, (bool *)false, NULL);
-    if (ret < 0)
-      return ret;
-
-    while (!objs.empty()) {
-      std::vector<RGWObjEnt>::iterator it = objs.begin();
-      for (it = objs.begin(); it != objs.end(); ++it) {
-        ret = remove_object(store, bucket, (*it).name);
-        if (ret < 0)
-          return ret;
-      }
-      objs.clear();
-
-      ret = store->list_objects(bucket, max, prefix, delim, marker, objs, common_prefixes,
-                                   false, ns, (bool *)false, NULL);
-      if (ret < 0)
-        return ret;
+      cerr << "could not get bucket info for bucket=" << bucket_name << std::endl;
+      return r;
     }
+    bucket = bucket_info.bucket;
   }
-
-  ret = store->delete_bucket(bucket);
-  if (ret < 0) {
-    cerr << "ERROR: could not remove bucket " << bucket.name << std::endl;
-
-    return ret;
-  }
-
-  ret = rgw_remove_user_bucket_info(store, info.owner, bucket);
-  if (ret < 0) {
-    cerr << "ERROR: unable to remove user bucket information" << std::endl;
-  }
-
-  return ret;
-}
-
-static bool bucket_object_check_filter(const string& name)
-{
-  string ns;
-  string obj = name;
-  return rgw_obj::translate_raw_obj_to_obj_in_ns(obj, ns);
+  return 0;
 }
 
 static int read_input(const string& infile, bufferlist& bl)
@@ -754,16 +443,6 @@ static int read_decode_json(const string& infile, T& t)
   }
   return 0;
 }
-    
-class StoreDestructor {
-  RGWRados *store;
-public:
-  StoreDestructor(RGWRados *_s) : store(_s) {}
-  ~StoreDestructor() {
-    RGWStoreManager::close_storage(store);
-  }
-};
-
 int main(int argc, char **argv) 
 {
   vector<const char*> args;
@@ -778,20 +457,15 @@ int main(int argc, char **argv)
   std::string date, subuser, access, format;
   std::string start_date, end_date;
   std::string key_type_str;
-  ObjectKeyType key_type = KEY_TYPE_S3;
+  int key_type = KEY_TYPE_UNDEFINED;
   rgw_bucket bucket;
   uint32_t perm_mask = 0;
-  bool specified_perm_mask = false;
   RGWUserInfo info;
   int opt_cmd = OPT_NO_CMD;
   bool need_more;
-  int gen_secret = false;
-  int gen_key = false;
-  bool implicit_gen_secret = true;
-  bool implicit_gen_key = true;
-  char secret_key_buf[SECRET_KEY_LEN + 1];
-  char public_id_buf[PUBLIC_ID_LEN + 1];
-  bool user_modify_op;
+  int gen_access_key = 0;
+  int gen_secret_key = 0;
+  bool set_perm = false;
   string bucket_id;
   Formatter *formatter = NULL;
   int purge_data = false;
@@ -808,7 +482,9 @@ int main(int argc, char **argv)
   map<string, bool> categories;
   string caps;
   int check_objects = false;
-  string infile;
+  std::string infile;
+  RGWUserAdminOpState user_op;
+  RGWBucketAdminOpState bucket_op;
 
   std::string val;
   std::ostringstream errs;
@@ -847,10 +523,10 @@ int main(int argc, char **argv)
         cerr << "bad key type: " << key_type_str << std::endl;
         return usage();
       }
-    } else if (ceph_argparse_binary_flag(args, i, &gen_key, NULL, "--gen-access-key", (char*)NULL)) {
-      implicit_gen_key = false;
-    } else if (ceph_argparse_binary_flag(args, i, &gen_secret, NULL, "--gen-secret", (char*)NULL)) {
-      implicit_gen_secret = false;
+    } else if (ceph_argparse_binary_flag(args, i, &gen_access_key, NULL, "--gen-access-key", (char*)NULL)) {
+      // do nothing
+    } else if (ceph_argparse_binary_flag(args, i, &gen_secret_key, NULL, "--gen-secret", (char*)NULL)) {
+      // do nothing
     } else if (ceph_argparse_binary_flag(args, i, &show_log_entries, NULL, "--show_log_entries", (char*)NULL)) {
       // do nothing
     } else if (ceph_argparse_binary_flag(args, i, &show_log_sum, NULL, "--show_log_sum", (char*)NULL)) {
@@ -874,8 +550,8 @@ int main(int argc, char **argv)
       end_date = val;
     } else if (ceph_argparse_witharg(args, i, &val, "--access", (char*)NULL)) {
       access = val;
-      perm_mask = str_to_perm(access.c_str());
-      specified_perm_mask = true;
+      perm_mask = rgw_str_to_perm(access.c_str());
+      set_perm = true;
     } else if (ceph_argparse_witharg(args, i, &val, "--bucket-id", (char*)NULL)) {
       bucket_id = val;
       if (bucket_id.empty()) {
@@ -905,7 +581,7 @@ int main(int argc, char **argv)
     } else if (ceph_argparse_binary_flag(args, i, &fix, NULL, "--fix", (char*)NULL)) {
       // do nothing
     } else if (ceph_argparse_binary_flag(args, i, &check_objects, NULL, "--check-objects", (char*)NULL)) {
-      // do nothing
+     // do nothing
     } else if (ceph_argparse_witharg(args, i, &val, "--caps", (char*)NULL)) {
       caps = val;
     } else if (ceph_argparse_witharg(args, i, &val, "-i", "--infile", (char*)NULL)) {
@@ -949,33 +625,7 @@ int main(int argc, char **argv)
     return usage();
   }
 
-  if (!subuser.empty()) {
-    char *suser = strdup(subuser.c_str());
-    char *p = strchr(suser, ':');
-    if (p) {
-      *p = '\0';
-      if (!user_id.empty()) {
-        if (user_id != suser) {
-          cerr << "bad subuser " << subuser << " for uid " << user_id << std::endl;
-          return 1;
-        }
-      } else {
-        user_id = suser;
-      }
-      subuser = p + 1;
-    }
-    free(suser);
-  }
-
-  if (opt_cmd == OPT_KEY_RM && key_type == KEY_TYPE_S3 && access_key.empty()) {
-    cerr << "error: access key was not specified" << std::endl;
-    return usage();
-  }
-
-  user_modify_op = (opt_cmd == OPT_USER_MODIFY || opt_cmd == OPT_SUBUSER_MODIFY ||
-                    opt_cmd == OPT_SUBUSER_CREATE || opt_cmd == OPT_SUBUSER_RM ||
-                    opt_cmd == OPT_KEY_CREATE || opt_cmd == OPT_KEY_RM || opt_cmd == OPT_USER_RM ||
-                   opt_cmd == OPT_CAPS_ADD || opt_cmd == OPT_CAPS_RM);
+  RGWStreamFlusher f(formatter, cout);
 
   store = RGWStoreManager::get_storage(g_ceph_context, false);
   if (!store) {
@@ -985,389 +635,207 @@ int main(int argc, char **argv)
 
   StoreDestructor store_destructor(store);
 
-  if (opt_cmd != OPT_USER_CREATE && 
-      opt_cmd != OPT_LOG_SHOW && opt_cmd != OPT_LOG_LIST && opt_cmd != OPT_LOG_RM && 
-      user_id.empty()) {
-    bool found = false;
-    string s;
-    if (!found && (!user_email.empty())) {
-      s = user_email;
-      if (rgw_get_user_info_by_email(store, s, info) >= 0) {
-       found = true;
-      } else {
-       cerr << "could not find user by specified email" << std::endl;
-      }
-    }
-    if (!found && (!access_key.empty())) {
-      s = access_key;
-      if (rgw_get_user_info_by_access_key(store, s, info) >= 0) {
-       found = true;
-      } else {
-       cerr << "could not find user by specified access key" << std::endl;
-      }
-    }
-    if (found)
-      user_id = info.user_id.c_str();
+  /* populate user operation */
+
+  if (!user_id.empty()) {
+    user_op.set_user_id(user_id);
+    bucket_op.set_user_id(user_id);
   }
 
+  if (!display_name.empty())
+    user_op.set_display_name(display_name);
 
-  if (user_modify_op || opt_cmd == OPT_USER_CREATE ||
-      opt_cmd == OPT_USER_INFO || opt_cmd == OPT_BUCKET_UNLINK || opt_cmd == OPT_BUCKET_LINK ||
-      opt_cmd == OPT_USER_SUSPEND || opt_cmd == OPT_USER_ENABLE) {
-    if (user_id.empty()) {
-      cerr << "user_id was not specified, aborting" << std::endl;
-      return usage();
-    }
+  if (!user_email.empty())
+    user_op.set_user_email(user_email);
 
-    bool found = (rgw_get_user_info_by_uid(store, user_id, info) >= 0);
+  if (!access_key.empty())
+    user_op.set_access_key(access_key);
 
-    if (opt_cmd == OPT_USER_CREATE) {
-      if (found) {
-        if (info.display_name.compare(display_name) != 0 ||
-            info.user_email.compare(user_email) != 0) {
-          cerr << "error: user already exists with different display_name/email" << std::endl;
-          return 1;
-        }
-        /* turn into OPT_USER_MODIFY */
-        opt_cmd = OPT_USER_MODIFY;
-        user_modify_op = true;
-      }
-    } else if (!found) {
-      cerr << "error reading user info, aborting" << std::endl;
-      return 1;
-    }
-  }
+  if (!secret_key.empty())
+    user_op.set_secret_key(secret_key);
 
-  bool subuser_found = false;
+  if (!subuser.empty())
+    user_op.set_subuser(subuser);
 
-  if (!subuser.empty()) {
-    map<string, RGWSubUser>::iterator iter = info.subusers.find(subuser);
-    subuser_found = (iter != info.subusers.end());
+  if (!caps.empty())
+    user_op.set_caps(caps);
 
-    if (!subuser_found && opt_cmd != OPT_SUBUSER_CREATE && opt_cmd != OPT_USER_CREATE) {
-      cerr << "subuser specified but was not found, aborting" << std::endl;
-      return 1;
-    }
-  }
+  user_op.set_purge_data(purge_data);
 
-  if (opt_cmd == OPT_SUBUSER_CREATE || opt_cmd == OPT_SUBUSER_MODIFY ||
-      opt_cmd == OPT_SUBUSER_RM) {
-    if (subuser.empty()) {
-      cerr << "subuser creation was requires specifying subuser name" << std::endl;
-      return 1;
-    }
-    if (opt_cmd == OPT_SUBUSER_CREATE) {
-      if (subuser_found) {
-        cerr << "error: subuser already exists" << std::endl;
-        return 1;
-      }
-      if (!key_type_str.empty() && key_type == KEY_TYPE_S3) {
-        cerr << "error: subusers may not be created with an S3 key, aborting" << std::endl;
-        return 1;
-      }
-    } else if (!subuser_found) {
-      cerr << "error: subuser doesn't exist" << std::endl;
-      return 1;
-    }
-  }
+  if (purge_keys)
+    user_op.set_purge_keys();
 
-  bool keys_not_requested = (access_key.empty() && secret_key.empty() && !gen_secret && !gen_key &&
-                             opt_cmd != OPT_KEY_CREATE);
+  if (gen_access_key)
+    user_op.set_gen_access();
 
-  if (opt_cmd == OPT_USER_CREATE || (user_modify_op && !keys_not_requested)) {
-    int ret;
+  if (gen_secret_key)
+    user_op.set_gen_secret();
 
-    if (opt_cmd == OPT_USER_CREATE && display_name.empty()) {
-      cerr << "display name was not specified, aborting" << std::endl;
-      return 0;
-    }
+  if (max_buckets >= 0)
+    user_op.set_max_buckets(max_buckets);
 
-    if ((secret_key.empty() && implicit_gen_secret) || gen_secret) {
-      ret = gen_rand_base64(g_ceph_context, secret_key_buf, sizeof(secret_key_buf));
-      if (ret < 0) {
-        cerr << "aborting" << std::endl;
-        return 1;
-      }
-      secret_key = secret_key_buf;
-    }
-    if ((access_key.empty() && implicit_gen_key) || gen_key) {
-      RGWUserInfo duplicate_check;
-      string duplicate_check_id;
-      do {
-       ret = gen_rand_alphanumeric_upper(g_ceph_context, public_id_buf, sizeof(public_id_buf));
-       if (ret < 0) {
-         cerr << "aborting" << std::endl;
-         return 1;
-       }
-       access_key = public_id_buf;
-       duplicate_check_id = access_key;
-      } while (!rgw_get_user_info_by_access_key(store, duplicate_check_id, duplicate_check));
-    }
-  }
+  if (set_perm)
+    user_op.set_perm(perm_mask);
 
-  map<string, RGWAccessKey>::iterator kiter;
-  map<string, RGWSubUser>::iterator uiter;
-  RGWUserInfo old_info = info;
+  if (key_type != KEY_TYPE_UNDEFINED)
+    user_op.set_key_type(key_type);
 
-  if (!bucket_name.empty()) {
-    string bucket_name_str = bucket_name;
-    RGWBucketInfo bucket_info;
-    int r = store->get_bucket_info(NULL, bucket_name_str, bucket_info);
-    if (r < 0) {
-      cerr << "could not get bucket info for bucket=" << bucket_name_str << std::endl;
-      return r;
+  // set suspension operation parameters
+  if (opt_cmd == OPT_USER_ENABLE)
+    user_op.set_suspension(false);
+  else if (opt_cmd == OPT_USER_SUSPEND)
+    user_op.set_suspension(true);
+
+  // RGWUser to use for user operations
+  RGWUser user;
+  int ret = 0;
+  if (!user_id.empty() || !subuser.empty()) {
+    ret = user.init(store, user_op);
+    if (ret < 0) {
+      cerr << "user.init failed: " << cpp_strerror(-ret) << std::endl;
+      return -ret;
     }
-    bucket = bucket_info.bucket;
   }
 
-  int err;
+  /* populate bucket operation */
+  bucket_op.set_bucket_name(bucket_name);
+  bucket_op.set_object(object);
+  bucket_op.set_check_objects(check_objects);
+  bucket_op.set_delete_children(delete_child_objects);
+
+  // required to gather errors from operations
+  std::string err_msg;
+
+  bool output_user_info = true;
+
   switch (opt_cmd) {
+  case OPT_USER_INFO:
+    break;
   case OPT_USER_CREATE:
-  case OPT_USER_MODIFY:
-  case OPT_SUBUSER_CREATE:
-  case OPT_SUBUSER_MODIFY:
-  case OPT_KEY_CREATE:
-  case OPT_CAPS_ADD:
-  case OPT_CAPS_RM:
-    if (!user_id.empty())
-      info.user_id = user_id;
-    if (max_buckets >= 0)
-      info.max_buckets = max_buckets;
-    if (key_type == KEY_TYPE_SWIFT) {
-      access_key = info.user_id;
-      access_key.append(":");
-      access_key.append(subuser);
+    ret = user.add(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not create user: " << err_msg << std::endl;
+      return -ret;
     }
-    if ((!access_key.empty()) && (!secret_key.empty())) {
-      if (key_type == KEY_TYPE_S3 && !validate_access_key(access_key)) {
-        cerr << "access key contains illegal characters" << std::endl;
-        return 1;
-      }
-      RGWAccessKey k;
-      k.id = access_key;
-      k.key = secret_key;
-      if (!subuser.empty())
-        k.subuser = subuser;
-      if (key_type == KEY_TYPE_SWIFT)
-        info.swift_keys[access_key] = k;
-      else
-        info.access_keys[access_key] = k;
-   } else if (opt_cmd == OPT_KEY_CREATE && (access_key.empty() || secret_key.empty())) {
-      if (key_type == KEY_TYPE_SWIFT)
-        cerr << "swift key modification requires both subuser and secret key" << std::endl;
-      else
-        cerr << "access key modification requires both access key and secret key" << std::endl;
-      return 1;
-    } else if (opt_cmd == OPT_CAPS_ADD) {
-      err = info.caps.add_from_string(caps);
-      if (err < 0) {
-        cerr << "failed to add caps, err=" << cpp_strerror(-err) << std::endl;
-       return 1;
-      }
-    } else if (opt_cmd == OPT_CAPS_RM) {
-      err = info.caps.remove_from_string(caps);
-      if (err < 0) {
-        cerr << "failed to remove caps, err=" << cpp_strerror(-err) << std::endl;
-       return 1;
-      }
+
+    break;
+  case OPT_USER_RM:
+    ret = user.remove(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not remove user: " << err_msg << std::endl;
+      return -ret;
+    }
+
+    output_user_info = false;
+    break;
+  case OPT_USER_ENABLE:
+  case OPT_USER_SUSPEND:
+  case OPT_USER_MODIFY:
+    ret = user.modify(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not modify user: " << err_msg << std::endl;
+      return -ret;
     }
-    if (!display_name.empty())
-      info.display_name = display_name;
-    if (!user_email.empty())
-      info.user_email = user_email;
-    if (!subuser.empty()) {
-      RGWSubUser u = info.subusers[subuser];
-      u.name = subuser;
-      if (specified_perm_mask)
-        u.perm_mask = perm_mask;
-
-      info.subusers[subuser] = u;
+
+    break;
+  case OPT_SUBUSER_CREATE:
+    ret = user.subusers.add(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not create subuser: " << err_msg << std::endl;
+      return -ret;
     }
-    if ((err = rgw_store_user_info(store, info, &old_info, false)) < 0) {
-      cerr << "error storing user info: " << cpp_strerror(-err) << std::endl;
-      break;
+
+    break;
+  case OPT_SUBUSER_MODIFY:
+    ret = user.subusers.modify(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not modify subuser: " << err_msg << std::endl;
+      return -ret;
     }
 
-    remove_old_indexes(old_info, info);
+    ret = user.info(info, &err_msg);
+    if (ret < 0) {
+      cerr << "could not fetch user info: " << err_msg << std::endl;
+      return -ret;
+    }
 
     show_user_info(info, formatter);
-    break;
 
+    break;
   case OPT_SUBUSER_RM:
-    uiter = info.subusers.find(subuser);
-    assert (uiter != info.subusers.end());
-    info.subusers.erase(uiter);
-    if (purge_keys) {
-      map<string, RGWAccessKey> *keys_map;
-      access_key = info.user_id;
-      access_key.append(":");
-      access_key.append(subuser);
-      keys_map = &info.swift_keys;
-      kiter = keys_map->find(access_key);
-      if (kiter != keys_map->end()) {
-        rgw_remove_key_index(store, kiter->second);
-        keys_map->erase(kiter);
-      }
+    ret = user.subusers.remove(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not remove subuser: " << err_msg << std::endl;
+      return -ret;
     }
-    if ((err = rgw_store_user_info(store, info, &old_info, false)) < 0) {
-      cerr << "error storing user info: " << cpp_strerror(-err) << std::endl;
-      break;
+
+    break;
+  case OPT_CAPS_ADD:
+    ret = user.caps.add(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not add caps: " << err_msg << std::endl;
+      return -ret;
+    }
+
+    break;
+  case OPT_CAPS_RM:
+    ret = user.caps.remove(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not add remove caps: " << err_msg << std::endl;
+      return -ret;
     }
-    remove_old_indexes(old_info, info);
 
-    show_user_info(info, formatter);
     break;
+  case OPT_KEY_CREATE:
+    ret = user.keys.add(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not create key: " << err_msg << std::endl;
+      return -ret;
+    }
 
+    break;
   case OPT_KEY_RM:
-    {
-      map<string, RGWAccessKey> *keys_map;
-      if (key_type == KEY_TYPE_SWIFT) {
-        access_key = info.user_id;
-        access_key.append(":");
-        access_key.append(subuser);
-        keys_map = &info.swift_keys;
-      } else {
-        keys_map = &info.access_keys;
-      }
-      kiter = keys_map->find(access_key);
-      if (kiter == keys_map->end()) {
-        cerr << "key not found" << std::endl;
-      } else {
-        rgw_remove_key_index(store, kiter->second);
-        keys_map->erase(kiter);
-        if ((err = rgw_store_user_info(store, info, &old_info, false)) < 0) {
-          cerr << "error storing user info: " << cpp_strerror(-err) << std::endl;
-          break;
-        }
-      }
+    ret = user.keys.remove(user_op, &err_msg);
+    if (ret < 0) {
+      cerr << "could not remove key: " << err_msg << std::endl;
+      return -ret;
     }
-    show_user_info(info, formatter);
+
     break;
+  default:
+    output_user_info = false;
+  }
 
-  case OPT_USER_INFO:
+  // output the result of a user operation
+  if (output_user_info) {
+    ret = user.info(info, &err_msg);
+    if (ret < 0) {
+      cerr << "could not fetch user info: " << err_msg << std::endl;
+      return -ret;
+    }
     show_user_info(info, formatter);
-    break;
   }
 
   if (opt_cmd == OPT_POLICY) {
-    bufferlist bl;
-    rgw_obj obj(bucket, object);
-    int ret = store->get_attr(NULL, obj, RGW_ATTR_ACL, bl);
-
-    RGWAccessControlPolicy_S3 policy(g_ceph_context);
+    int ret = RGWBucketAdminOp::get_policy(store, bucket_op, cout);
     if (ret >= 0) {
-      bufferlist::iterator iter = bl.begin();
-      try {
-        policy.decode(iter);
-      } catch (buffer::error& err) {
-        dout(0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
-        return -EIO;
-      }
-      policy.to_xml(cout);
       cout << std::endl;
     }
   }
 
-  if (opt_cmd == OPT_BUCKETS_LIST) {
-    RGWAccessHandle handle;
-
-    formatter->reset();
-    formatter->open_array_section("buckets");
-    if (!user_id.empty()) {
-      RGWUserBuckets buckets;
-      if (rgw_read_user_buckets(store, user_id, buckets, false) < 0) {
-        cerr << "list buckets: could not get buckets for uid " << user_id << std::endl;
-      } else {
-        map<string, RGWBucketEnt>& m = buckets.get_buckets();
-        map<string, RGWBucketEnt>::iterator iter;
+  if (opt_cmd == OPT_BUCKETS_LIST || opt_cmd == OPT_BUCKET_STATS) {
+    if (opt_cmd == OPT_BUCKET_STATS)
+      bucket_op.set_fetch_stats(true);
 
-        for (iter = m.begin(); iter != m.end(); ++iter) {
-          RGWBucketEnt obj = iter->second;
-         formatter->dump_string("bucket", obj.bucket.name);
-        }
-      }
-    } else {
-      if (store->list_buckets_init(&handle) < 0) {
-        cerr << "list buckets: no buckets found" << std::endl;
-      } else {
-        RGWObjEnt obj;
-        while (store->list_buckets_next(obj, &handle) >= 0) {
-          formatter->dump_string("bucket", obj.name);
-        }
-      }
-    }
-    formatter->close_section();
-    formatter->flush(cout);
-    cout << std::endl;
+    RGWBucketAdminOp::info(store, bucket_op, f);
   }
 
   if (opt_cmd == OPT_BUCKET_LINK) {
-    if (bucket_name.empty()) {
-      cerr << "bucket name was not specified" << std::endl;
-      return usage();
-    }
-    string uid_str(user_id);
-    
-    string no_oid;
-    bufferlist aclbl;
-    rgw_obj obj(bucket, no_oid);
-
-    int r = store->get_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
-    if (r >= 0) {
-      RGWAccessControlPolicy policy;
-      ACLOwner owner;
-      try {
-       bufferlist::iterator iter = aclbl.begin();
-       ::decode(policy, iter);
-       owner = policy.get_owner();
-      } catch (buffer::error& err) {
-       dout(10) << "couldn't decode policy" << dendl;
-       return -EINVAL;
-      }
-      //cout << "bucket is linked to user '" << owner.get_id() << "'.. unlinking" << std::endl;
-      r = rgw_remove_user_bucket_info(store, owner.get_id(), bucket);
-      if (r < 0) {
-        cerr << "could not unlink policy from user '" << owner.get_id() << "'" << std::endl;
-        return r;
-      }
-
-      // now update the user for the bucket...
-      if (info.display_name.empty()) {
-        cerr << "WARNING: user " << info.user_id << " has no display name set" << std::endl;
-      } else {
-        policy.create_default(info.user_id, info.display_name);
-
-        // ...and encode the acl
-        aclbl.clear();
-        policy.encode(aclbl);
-
-        r = store->set_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
-        if (r < 0)
-          return r;
-
-        r = rgw_add_bucket(store, info.user_id, bucket);
-        if (r < 0)
-          return r;
-      }
-    } else {
-      // the bucket seems not to exist, so we should probably create it...
-      r = create_bucket(bucket_name.c_str(), uid_str, info.display_name);
-      if (r < 0)
-        cerr << "error linking bucket to user: r=" << r << std::endl;
-      return -r;
-    }
+   RGWBucketAdminOp::link(store, bucket_op);
   }
 
   if (opt_cmd == OPT_BUCKET_UNLINK) {
-    if (bucket_name.empty()) {
-      cerr << "bucket name was not specified" << std::endl;
-      return usage();
-    }
-
-    int r = rgw_remove_user_bucket_info(store, user_id, bucket);
-    if (r < 0)
-      cerr << "error unlinking bucket " <<  cpp_strerror(-r) << std::endl;
-    return -r;
+    RGWBucketAdminOp::unlink(store, bucket_op);
   }
 
   if (opt_cmd == OPT_TEMP_REMOVE) {
@@ -1521,30 +989,6 @@ next:
     }
   }
   
-  if (opt_cmd == OPT_USER_RM) {
-    RGWUserBuckets buckets;
-
-    if (rgw_read_user_buckets(store, user_id, buckets, false) >= 0) {
-      map<string, RGWBucketEnt>& m = buckets.get_buckets();
-
-      if (!m.empty() && purge_data) {
-       int ret;
-        for (std::map<string, RGWBucketEnt>::iterator it = m.begin(); it != m.end(); ++it) {
-          ret = remove_bucket(store, ((*it).second).bucket, true);
-
-          if (ret < 0)
-            return ret;
-        }
-      }
-
-      if (!m.empty() && !purge_data) {
-        cerr << "ERROR: specify --purge-data to remove a user with a non-empty bucket list" << std::endl;
-        return 1;
-      }
-    }
-    rgw_delete_user(store, info);
-  }
-  
   if (opt_cmd == OPT_POOL_ADD) {
     if (pool_name.empty()) {
       cerr << "need to specify pool to add!" << std::endl;
@@ -1587,71 +1031,6 @@ next:
     cout << std::endl;
   }
 
-  if (opt_cmd == OPT_BUCKET_STATS) {
-    if (bucket_name.empty() && user_id.empty()) {
-      cerr << "either bucket or uid needs to be specified" << std::endl;
-      return usage();
-    }
-    formatter->reset();
-    if (user_id.empty()) {
-      bucket_stats(bucket, formatter);
-    } else {
-      RGWUserBuckets buckets;
-      if (rgw_read_user_buckets(store, user_id, buckets, false) < 0) {
-       cerr << "could not get buckets for uid " << user_id << std::endl;
-      } else {
-       formatter->open_array_section("buckets");
-       map<string, RGWBucketEnt>& m = buckets.get_buckets();
-       for (map<string, RGWBucketEnt>::iterator iter = m.begin(); iter != m.end(); ++iter) {
-         RGWBucketEnt obj = iter->second;
-         bucket_stats(obj.bucket, formatter);
-       }
-       formatter->close_section();
-      }
-    }
-    formatter->flush(cout);
-    cout << std::endl;
-  }
-
-  if (opt_cmd == OPT_USER_SUSPEND || opt_cmd == OPT_USER_ENABLE) {
-    __u8 disable = (opt_cmd == OPT_USER_SUSPEND ? 1 : 0);
-
-    if (user_id.empty()) {
-      cerr << "uid was not specified" << std::endl;
-      return usage();
-    }
-    RGWUserBuckets buckets;
-    if (rgw_read_user_buckets(store, user_id, buckets, false) < 0) {
-      cerr << "could not get buckets for uid " << user_id << std::endl;
-    }
-    map<string, RGWBucketEnt>& m = buckets.get_buckets();
-    map<string, RGWBucketEnt>::iterator iter;
-
-    int ret;
-    info.suspended = disable;
-    ret = rgw_store_user_info(store, info, &old_info, false);
-    if (ret < 0) {
-      cerr << "ERROR: failed to store user info user=" << user_id << " ret=" << ret << std::endl;
-      return 1;
-    }
-     
-    if (disable)
-      dout(0) << "disabling user buckets" << dendl;
-    else
-      dout(0) << "enabling user buckets" << dendl;
-
-    vector<rgw_bucket> bucket_names;
-    for (iter = m.begin(); iter != m.end(); ++iter) {
-      RGWBucketEnt obj = iter->second;
-      bucket_names.push_back(obj.bucket);
-    }
-    ret = store->set_buckets_enabled(bucket_names, !disable);
-    if (ret < 0) {
-      cerr << "ERROR: failed to change pool" << std::endl;
-      return 1;
-    }
-  } 
-
   if (opt_cmd == OPT_USAGE_SHOW) {
     uint64_t start_epoch = 0;
     uint64_t end_epoch = (uint64_t)-1;
@@ -1673,7 +1052,6 @@ next:
       }
     }
 
-    RGWStreamFlusher f(formatter, cout);
 
     ret = RGWUsage::show(store, user_id, start_epoch, end_epoch,
                         show_log_entries, show_log_sum, &categories,
@@ -1719,18 +1097,28 @@ next:
   }
 
   if (opt_cmd == OPT_OBJECT_RM) {
-    int ret = remove_object(store, bucket, object);
+    int ret = init_bucket(bucket_name, bucket);
+    if (ret < 0) {
+      cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
+    ret = rgw_remove_object(store, bucket, object);
 
     if (ret < 0) {
       cerr << "ERROR: object remove returned: " << cpp_strerror(-ret) << std::endl;
-      return 1;
+      return -ret;
     }
   }
 
   if (opt_cmd == OPT_OBJECT_UNLINK) {
+    int ret = init_bucket(bucket_name, bucket);
+    if (ret < 0) {
+      cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
+      return -ret;
+    }
     list<string> oid_list;
     oid_list.push_back(object);
-    int ret = store->remove_objs_from_index(bucket, oid_list);
+    ret = store->remove_objs_from_index(bucket, oid_list);
     if (ret < 0) {
       cerr << "ERROR: remove_obj_from_index() returned error: " << cpp_strerror(-ret) << std::endl;
       return 1;
@@ -1738,82 +1126,11 @@ next:
   }
 
   if (opt_cmd == OPT_BUCKET_CHECK) {
-    check_bad_index_multipart(store, bucket, fix);
-
-    map<RGWObjCategory, RGWBucketStats> existing_stats;
-    map<RGWObjCategory, RGWBucketStats> calculated_stats;
-
-    if (check_objects) {
-      if (!fix) {
-       cerr << "--check-objects flag requires --fix" << std::endl;
-       return 1;
-      }
-#define BUCKET_TAG_TIMEOUT 30
-      cout << "Checking objects, decreasing bucket 2-phase commit timeout.\n"
-              "** Note that timeout will reset only when operation completes successfully **" << std::endl;
-
-      store->cls_obj_set_bucket_tag_timeout(bucket, BUCKET_TAG_TIMEOUT);
-
-      string prefix;
-      string marker;
-      bool is_truncated = true;
-
-      while (is_truncated) {
-       map<string, RGWObjEnt> result;
-       int r = store->cls_bucket_list(bucket, marker, prefix, 1000, 
-                                   result, &is_truncated, &marker,
-                                    bucket_object_check_filter);
-
-       if (r < 0 && r != -ENOENT) {
-          cerr << "ERROR: failed operation r=" << r << std::endl;
-       }
-
-       if (r == -ENOENT)
-         break;
-
-       map<string, RGWObjEnt>::iterator iter;
-       for (iter = result.begin(); iter != result.end(); ++iter) {
-         cout << iter->first << std::endl;
-       }
-
-      }
-
-      store->cls_obj_set_bucket_tag_timeout(bucket, 0);
-
-    }
-    int r = store->bucket_check_index(bucket, &existing_stats, &calculated_stats);
-    if (r < 0) {
-      cerr << "failed to check index err=" << cpp_strerror(-r) << std::endl;
-      return r;
-    }
-
-    formatter->open_object_section("check_result");
-    formatter->open_object_section("existing_header");
-    dump_bucket_usage(existing_stats, formatter);
-    formatter->close_section();
-    formatter->open_object_section("calculated_header");
-    dump_bucket_usage(calculated_stats, formatter);
-    formatter->close_section();
-    formatter->close_section();
-    formatter->flush(cout);
-
-    if (fix) {
-      r = store->bucket_rebuild_index(bucket);
-      if (r < 0) {
-        cerr << "failed to rebuild index err=" << cpp_strerror(-r) << std::endl;
-        return r;
-      }
-    }
-
+    RGWBucketAdminOp::check_index(store, bucket_op, f);
   }
 
   if (opt_cmd == OPT_BUCKET_RM) {
-    int ret = remove_bucket(store, bucket, delete_child_objects);
-
-    if (ret < 0) {
-      cerr << "ERROR: bucket remove returned: " << cpp_strerror(-ret) << std::endl;
-      return 1;
-    }
+    RGWBucketAdminOp::remove_bucket(store, bucket_op);
   }
 
   if (opt_cmd == OPT_GC_LIST) {
diff --git a/src/rgw/rgw_bucket.cc b/src/rgw/rgw_bucket.cc
new file mode 100644 (file)
index 0000000..8758ba5
--- /dev/null
@@ -0,0 +1,968 @@
+#include <errno.h>
+
+#include <string>
+#include <map>
+
+#include "common/errno.h"
+#include "rgw_rados.h"
+#include "rgw_acl.h"
+#include "rgw_acl_s3.h"
+
+#include "include/types.h"
+#include "rgw_bucket.h"
+#include "rgw_user.h"
+#include "rgw_string.h"
+
+// until everything is moved from rgw_common
+#include "rgw_common.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+#define BUCKET_TAG_TIMEOUT 30
+
+using namespace std;
+
+// define as static when RGWBucket implementation compete
+void rgw_get_buckets_obj(string& user_id, string& buckets_obj_id)
+{
+  buckets_obj_id = user_id;
+  buckets_obj_id += RGW_BUCKETS_OBJ_PREFIX;
+}
+
+static int rgw_read_buckets_from_attr(RGWRados *store, string& user_id, RGWUserBuckets& buckets)
+{
+  bufferlist bl;
+  rgw_obj obj(store->zone.user_uid_pool, user_id);
+  int ret = store->get_attr(NULL, obj, RGW_ATTR_BUCKETS, bl);
+  if (ret)
+    return ret;
+
+  bufferlist::iterator iter = bl.begin();
+  try {
+    buckets.decode(iter);
+  } catch (buffer::error& err) {
+    ldout(store->ctx(), 0) << "ERROR: failed to decode buckets info, caught buffer::error" << dendl;
+    return -EIO;
+  }
+  return 0;
+}
+
+/**
+ * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
+ * Returns: 0 on success, -ERR# on failure.
+ */
+int rgw_read_user_buckets(RGWRados *store, string user_id, RGWUserBuckets& buckets, bool need_stats)
+{
+  int ret;
+  buckets.clear();
+  if (store->supports_omap()) {
+    string buckets_obj_id;
+    rgw_get_buckets_obj(user_id, buckets_obj_id);
+    bufferlist bl;
+    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
+    bufferlist header;
+    map<string,bufferlist> m;
+
+    ret = store->omap_get_all(obj, header, m);
+    if (ret == -ENOENT)
+      ret = 0;
+
+    if (ret < 0)
+      return ret;
+
+    for (map<string,bufferlist>::iterator q = m.begin(); q != m.end(); q++) {
+      bufferlist::iterator iter = q->second.begin();
+      RGWBucketEnt bucket;
+      ::decode(bucket, iter);
+      buckets.add(bucket);
+    }
+  } else {
+    ret = rgw_read_buckets_from_attr(store, user_id, buckets);
+    switch (ret) {
+    case 0:
+      break;
+    case -ENODATA:
+      ret = 0;
+      return 0;
+    default:
+      return ret;
+    }
+  }
+
+  if (need_stats) {
+    map<string, RGWBucketEnt>& m = buckets.get_buckets();
+    int r = store->update_containers_stats(m);
+    if (r < 0) {
+      ldout(store->ctx(), 0) << "ERROR: could not get stats for buckets" << dendl;
+    }
+  }
+  return 0;
+}
+
+/**
+ * Store the set of buckets associated with a user on a n xattr
+ * not used with all backends
+ * This completely overwrites any previously-stored list, so be careful!
+ * Returns 0 on success, -ERR# otherwise.
+ */
+int rgw_write_buckets_attr(RGWRados *store, string user_id, RGWUserBuckets& buckets)
+{
+  bufferlist bl;
+  buckets.encode(bl);
+
+  rgw_obj obj(store->zone.user_uid_pool, user_id);
+
+  int ret = store->set_attr(NULL, obj, RGW_ATTR_BUCKETS, bl);
+
+  return ret;
+}
+
+int rgw_add_bucket(RGWRados *store, string user_id, rgw_bucket& bucket)
+{
+  int ret;
+  string& bucket_name = bucket.name;
+
+  if (store->supports_omap()) {
+    bufferlist bl;
+
+    RGWBucketEnt new_bucket;
+    new_bucket.bucket = bucket;
+    new_bucket.size = 0;
+    time(&new_bucket.mtime);
+    ::encode(new_bucket, bl);
+
+    string buckets_obj_id;
+    rgw_get_buckets_obj(user_id, buckets_obj_id);
+
+    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
+    ret = store->omap_set(obj, bucket_name, bl);
+    if (ret < 0) {
+      ldout(store->ctx(), 0) << "ERROR: error adding bucket to directory: "
+          << cpp_strerror(-ret)<< dendl;
+    }
+  } else {
+    RGWUserBuckets buckets;
+
+    ret = rgw_read_user_buckets(store, user_id, buckets, false);
+    RGWBucketEnt new_bucket;
+
+    switch (ret) {
+    case 0:
+    case -ENOENT:
+    case -ENODATA:
+      new_bucket.bucket = bucket;
+      new_bucket.size = 0;
+      time(&new_bucket.mtime);
+      buckets.add(new_bucket);
+      ret = rgw_write_buckets_attr(store, user_id, buckets);
+      break;
+    default:
+      ldout(store->ctx(), 10) << "rgw_write_buckets_attr returned " << ret << dendl;
+      break;
+    }
+  }
+
+  return ret;
+}
+
+int rgw_remove_user_bucket_info(RGWRados *store, string user_id, rgw_bucket& bucket)
+{
+  int ret;
+
+  if (store->supports_omap()) {
+    bufferlist bl;
+
+    string buckets_obj_id;
+    rgw_get_buckets_obj(user_id, buckets_obj_id);
+
+    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
+    ret = store->omap_del(obj, bucket.name);
+    if (ret < 0) {
+      ldout(store->ctx(), 0) << "ERROR: error removing bucket from directory: "
+          << cpp_strerror(-ret)<< dendl;
+    }
+  } else {
+    RGWUserBuckets buckets;
+
+    ret = rgw_read_user_buckets(store, user_id, buckets, false);
+
+    if (ret == 0 || ret == -ENOENT) {
+      buckets.remove(bucket.name);
+      ret = rgw_write_buckets_attr(store, user_id, buckets);
+    }
+  }
+
+  return ret;
+}
+
+int RGWBucket::create_bucket(string bucket_str, string& user_id, string& display_name)
+{
+  RGWAccessControlPolicy policy, old_policy;
+  map<string, bufferlist> attrs;
+  bufferlist aclbl;
+  string no_oid;
+  rgw_obj obj;
+  RGWBucketInfo bucket_info;
+
+  int ret;
+
+  // defaule policy (private)
+  policy.create_default(user_id, display_name);
+  policy.encode(aclbl);
+
+  ret = store->get_bucket_info(NULL, bucket_str, bucket_info);
+  if (ret < 0)
+    return ret;
+
+  rgw_bucket& bucket = bucket_info.bucket;
+
+  ret = store->create_bucket(user_id, bucket, attrs);
+  if (ret && ret != -EEXIST)
+    goto done;
+
+  obj.init(bucket, no_oid);
+
+  ret = store->set_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
+  if (ret < 0) {
+    lderr(store->ctx()) << "ERROR: failed to set acl on bucket" << dendl;
+    goto done;
+  }
+
+  ret = rgw_add_bucket(store, user_id, bucket);
+
+  if (ret == -EEXIST)
+    ret = 0;
+done:
+  return ret;
+}
+
+static void dump_mulipart_index_results(list<std::string>& objs_to_unlink,
+        Formatter *f)
+{
+  // make sure that an appropiately titled header has been opened previously
+  list<std::string>::iterator oiter = objs_to_unlink.begin();
+
+  f->open_array_section("invalid_multipart_entries");
+
+  for ( ; oiter != objs_to_unlink.end(); ++oiter) {
+    f->dump_string("object",  *oiter);
+  }
+
+  f->close_section();
+}
+
+void check_bad_user_bucket_mapping(RGWRados *store, const string& user_id, bool fix)
+{
+  RGWUserBuckets user_buckets;
+  int ret = rgw_read_user_buckets(store, user_id, user_buckets, false);
+  if (ret < 0) {
+    ldout(store->ctx(), 0) << "failed to read user buckets: " << cpp_strerror(-ret) << dendl;
+    return;
+  }
+
+  map<string, RGWBucketEnt>& buckets = user_buckets.get_buckets();
+  for (map<string, RGWBucketEnt>::iterator i = buckets.begin();
+       i != buckets.end();
+       ++i) {
+    RGWBucketEnt& bucket_ent = i->second;
+    rgw_bucket& bucket = bucket_ent.bucket;
+
+    RGWBucketInfo bucket_info;
+    int r = store->get_bucket_info(NULL, bucket.name, bucket_info);
+    if (r < 0) {
+      ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket << dendl;
+      continue;
+    }
+
+    rgw_bucket& actual_bucket = bucket_info.bucket;
+
+    if (actual_bucket.name.compare(bucket.name) != 0 ||
+        actual_bucket.pool.compare(bucket.pool) != 0 ||
+        actual_bucket.marker.compare(bucket.marker) != 0 ||
+        actual_bucket.bucket_id.compare(bucket.bucket_id) != 0) {
+      cout << "bucket info mismatch: expected " << actual_bucket << " got " << bucket << std::endl;
+      if (fix) {
+        cout << "fixing" << std::endl;
+        r = rgw_add_bucket(store, user_id, actual_bucket);
+        if (r < 0) {
+          cerr << "failed to fix bucket: " << cpp_strerror(-r) << std::endl;
+        }
+      }
+    }
+  }
+}
+
+static bool bucket_object_check_filter(const string& name)
+{
+  string ns;
+  string obj = name;
+  return rgw_obj::translate_raw_obj_to_obj_in_ns(obj, ns);
+}
+
+int rgw_remove_object(RGWRados *store, rgw_bucket& bucket, std::string& object)
+{
+  RGWRadosCtx rctx(store);
+
+  rgw_obj obj(bucket,object);
+
+  int ret = store->delete_obj((void *)&rctx, obj);
+
+  return ret;
+}
+
+int rgw_remove_bucket(RGWRados *store, rgw_bucket& bucket, bool delete_children)
+{
+  int ret;
+  map<RGWObjCategory, RGWBucketStats> stats;
+  std::vector<RGWObjEnt> objs;
+  std::string prefix, delim, marker, ns;
+  map<string, bool> common_prefixes;
+  rgw_obj obj;
+  RGWBucketInfo info;
+  bufferlist bl;
+
+  ret = store->get_bucket_stats(bucket, stats);
+  if (ret < 0)
+    return ret;
+
+  obj.bucket = bucket;
+  int max = 1000;
+
+  ret = rgw_get_obj(store, NULL, store->zone.domain_root,\
+           bucket.name, bl, NULL);
+
+  bufferlist::iterator iter = bl.begin();
+  try {
+    ::decode(info, iter);
+  } catch (buffer::error& err) {
+    //cerr << "ERROR: could not decode buffer info, caught buffer::error" << std::endl;
+    return -EIO;
+  }
+
+  if (delete_children) {
+    ret = store->list_objects(bucket, max, prefix, delim, marker,\
+            objs, common_prefixes,\
+            false, ns, (bool *)false, NULL);
+
+    if (ret < 0)
+      return ret;
+
+    while (!objs.empty()) {
+      std::vector<RGWObjEnt>::iterator it = objs.begin();
+      for (it = objs.begin(); it != objs.end(); ++it) {
+        ret = rgw_remove_object(store, bucket, (*it).name);
+        if (ret < 0)
+          return ret;
+      }
+      objs.clear();
+
+      ret = store->list_objects(bucket, max, prefix, delim, marker, objs, common_prefixes,
+                                false, ns, (bool *)false, NULL);
+      if (ret < 0)
+        return ret;
+    }
+  }
+
+  ret = store->delete_bucket(bucket);
+  if (ret < 0) {
+    lderr(store->ctx()) << "ERROR: could not remove bucket " << bucket.name << dendl;
+    return ret;
+  }
+
+  ret = rgw_remove_user_bucket_info(store, info.owner, bucket);
+  if (ret < 0) {
+    lderr(store->ctx()) << "ERROR: unable to remove user bucket information" << dendl;
+  }
+
+  return ret;
+}
+
+static void set_err_msg(std::string *sink, std::string msg)
+{
+  if (sink && !msg.empty())
+    *sink = msg;
+}
+
+int RGWBucket::init(RGWRados *storage, RGWBucketAdminOpState& op_state)
+{
+  if (!storage)
+    return -EINVAL;
+
+  store = storage;
+
+  RGWUserInfo info;
+  RGWBucketInfo bucket_info;
+
+  user_id = op_state.get_user_id();
+  bucket_name = op_state.get_bucket_name();
+  RGWUserBuckets user_buckets;
+
+  if (bucket_name.empty() && user_id.empty())
+    return -EINVAL;
+
+  if (!bucket_name.empty()) {
+    int r = store->get_bucket_info(NULL, bucket_name, bucket_info);
+    if (r < 0) {
+      ldout(store->ctx(), 0) << "could not get bucket info for bucket=" << bucket_name << dendl;
+      return r;
+    }
+
+    op_state.set_bucket(bucket_info.bucket);
+  }
+
+  if (!user_id.empty()) {
+    int r = rgw_get_user_info_by_uid(store, user_id, info);
+    if (r < 0)
+      return r;
+
+    r = rgw_read_user_buckets(store, user_id, user_buckets, true);
+    if (r < 0)
+      return r;
+
+    op_state.set_user_buckets(user_buckets);
+    op_state.display_name = info.display_name;
+  }
+
+  clear_failure();
+  return 0;
+}
+
+int RGWBucket::link(RGWBucketAdminOpState& op_state, std::string *err_msg)
+{
+  if (!op_state.is_user_op()) {
+    set_err_msg(err_msg, "empty user id");
+    return -EINVAL;
+  }
+
+  std::string no_oid;
+
+  std::string display_name = op_state.get_user_display_name();
+  rgw_bucket bucket = op_state.get_bucket();
+
+  string uid_str(user_id);
+  bufferlist aclbl;
+  rgw_obj obj(bucket, no_oid);
+
+  int r = store->get_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
+  if (r >= 0) {
+    RGWAccessControlPolicy policy;
+    ACLOwner owner;
+    try {
+     bufferlist::iterator iter = aclbl.begin();
+     ::decode(policy, iter);
+     owner = policy.get_owner();
+    } catch (buffer::error& err) {
+      set_err_msg(err_msg, "couldn't decode policy");
+      return -EIO;
+    }
+
+    r = rgw_remove_user_bucket_info(store, owner.get_id(), bucket);
+    if (r < 0) {
+      set_err_msg(err_msg, "could not unlink policy from user " + owner.get_id());
+      return r;
+    }
+
+    // now update the user for the bucket...
+    if (display_name.empty()) {
+      ldout(store->ctx(), 0) << "WARNING: user " << user_id << " has no display name set" << dendl;
+    }
+    policy.create_default(user_id, display_name);
+
+    owner = policy.get_owner();
+    r = store->set_bucket_owner(bucket, owner);
+    if (r < 0) {
+      set_err_msg(err_msg, "failed to set bucket owner: " + cpp_strerror(-r));
+      return r;
+    }
+
+    // ...and encode the acl
+    aclbl.clear();
+    policy.encode(aclbl);
+
+    r = store->set_attr(NULL, obj, RGW_ATTR_ACL, aclbl);
+    if (r < 0)
+      return r;
+
+    r = rgw_add_bucket(store, user_id, bucket);
+    if (r < 0)
+      return r;
+  } else {
+    // the bucket seems not to exist, so we should probably create it...
+    r = create_bucket(bucket_name.c_str(), uid_str, display_name);
+    if (r < 0) {
+      set_err_msg(err_msg, "error linking bucket to user r=" + cpp_strerror(-r));
+    }
+
+    return r;
+  }
+
+  return 0;
+}
+
+int RGWBucket::unlink(RGWBucketAdminOpState& op_state, std::string *err_msg)
+{
+  rgw_bucket bucket = op_state.get_bucket();
+
+  if (!op_state.is_user_op()) {
+    set_err_msg(err_msg, "could not fetch user or user bucket info");
+    return -EINVAL;
+  }
+
+  int r = rgw_remove_user_bucket_info(store, user_id, bucket);
+  if (r < 0) {
+    set_err_msg(err_msg, "error unlinking bucket" + cpp_strerror(-r));
+  }
+
+  return r;
+}
+
+int RGWBucket::remove(RGWBucketAdminOpState& op_state, std::string *err_msg)
+{
+  bool delete_children = op_state.will_delete_children();
+  rgw_bucket bucket = op_state.get_bucket();
+
+  int ret = rgw_remove_bucket(store, bucket, delete_children);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove bucket" + cpp_strerror(-ret));
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWBucket::remove_object(RGWBucketAdminOpState& op_state, std::string *err_msg)
+{
+  rgw_bucket bucket = op_state.get_bucket();
+  std::string object_name = op_state.get_object_name();
+
+  int ret = rgw_remove_object(store, bucket, object_name);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove object" + cpp_strerror(-ret));
+    return ret;
+  }
+
+  return 0;
+}
+
+static void dump_bucket_index(map<string, RGWObjEnt> result,  Formatter *f)
+{
+  map<string, RGWObjEnt>::iterator iter;
+  for (iter = result.begin(); iter != result.end(); ++iter) {
+    f->dump_string("object", iter->first);
+   }
+}
+
+static void dump_bucket_usage(map<RGWObjCategory, RGWBucketStats>& stats, Formatter *formatter)
+{
+  map<RGWObjCategory, RGWBucketStats>::iterator iter;
+
+  formatter->open_object_section("usage");
+  for (iter = stats.begin(); iter != stats.end(); ++iter) {
+    RGWBucketStats& s = iter->second;
+    const char *cat_name = rgw_obj_category_name(iter->first);
+    formatter->open_object_section(cat_name);
+    formatter->dump_int("size_kb", s.num_kb);
+    formatter->dump_int("size_kb_actual", s.num_kb_rounded);
+    formatter->dump_int("num_objects", s.num_objects);
+    formatter->close_section();
+  }
+  formatter->close_section();
+}
+
+static void dump_index_check(map<RGWObjCategory, RGWBucketStats> existing_stats,
+        map<RGWObjCategory, RGWBucketStats> calculated_stats,
+        Formatter *formatter)
+{
+  formatter->open_object_section("check_result");
+  formatter->open_object_section("existing_header");
+  dump_bucket_usage(existing_stats, formatter);
+  formatter->close_section();
+  formatter->open_object_section("calculated_header");
+  dump_bucket_usage(calculated_stats, formatter);
+  formatter->close_section();
+  formatter->close_section();
+  formatter->flush(cout);
+}
+
+int RGWBucket::check_bad_index_multipart(RGWBucketAdminOpState& op_state,
+        list<std::string>& objs_to_unlink, std::string *err_msg)
+{
+  bool fix_index = op_state.will_fix_index();
+  rgw_bucket bucket = op_state.get_bucket();
+
+  int max = 1000;
+  string prefix;
+  string marker;
+  string delim;
+
+  map<string, bool> common_prefixes;
+  string ns = "multipart";
+
+  bool is_truncated;
+  map<string, bool> meta_objs;
+  map<string, string> all_objs;
+
+  do {
+    vector<RGWObjEnt> result;
+    int r = store->list_objects(bucket, max, prefix, delim, marker,
+                                result, common_prefixes, false, ns,
+                                &is_truncated, NULL);
+
+    if (r < 0) {
+      set_err_msg(err_msg, "failed to list objects in bucket=" + bucket.name +
+              " err=" +  cpp_strerror(-r));
+
+      return r;
+    }
+
+    vector<RGWObjEnt>::iterator iter;
+    for (iter = result.begin(); iter != result.end(); ++iter) {
+      RGWObjEnt& ent = *iter;
+
+      rgw_obj obj(bucket, ent.name);
+      obj.set_ns(ns);
+
+      string& oid = obj.object;
+      marker = oid;
+
+      int pos = oid.find_last_of('.');
+      if (pos < 0)
+        continue;
+
+      string name = oid.substr(0, pos);
+      string suffix = oid.substr(pos + 1);
+
+      if (suffix.compare("meta") == 0) {
+        meta_objs[name] = true;
+      } else {
+        all_objs[oid] = name;
+      }
+    }
+
+  } while (is_truncated);
+
+  map<string, string>::iterator aiter;
+  for (aiter = all_objs.begin(); aiter != all_objs.end(); ++aiter) {
+    string& name = aiter->second;
+
+    if (meta_objs.find(name) == meta_objs.end()) {
+      objs_to_unlink.push_back(aiter->first);
+    }
+  }
+
+  if (objs_to_unlink.empty())
+    return 0;
+
+  if (fix_index) {
+    int r = store->remove_objs_from_index(bucket, objs_to_unlink);
+    if (r < 0) {
+      set_err_msg(err_msg, "ERROR: remove_obj_from_index() returned error: " +
+              cpp_strerror(-r));
+
+      return r;
+    }
+  }
+
+  return 0;
+}
+
+int RGWBucket::check_object_index(RGWBucketAdminOpState& op_state,
+        map<string, RGWObjEnt> result, std::string *err_msg)
+{
+
+  bool fix_index = op_state.will_fix_index();
+  rgw_bucket bucket = op_state.get_bucket();
+
+  if (!fix_index) {
+    set_err_msg(err_msg, "check-objects flag requires fix index enabled");
+    return -EINVAL;
+  }
+
+/*
+  dout(0) << "Checking objects, decreasing bucket 2-phase commit timeout.\n"\
+         << "** Note that timeout will reset only when operation completes successfully **" << dendl;
+*/
+  store->cls_obj_set_bucket_tag_timeout(bucket, BUCKET_TAG_TIMEOUT);
+
+  string prefix;
+  string marker;
+  bool is_truncated = true;
+
+  while (is_truncated) {
+    map<string, RGWObjEnt> result;
+    string ns;
+
+    int r = store->cls_bucket_list(bucket, marker, prefix, 1000, result,
+             &is_truncated, &marker,
+             bucket_object_check_filter);
+
+    if (r == -ENOENT) {
+      break;
+    } else if (r < 0 && r != -ENOENT) {
+      set_err_msg(err_msg, "ERROR: failed operation r=" + cpp_strerror(-r));
+    }
+  }
+
+  store->cls_obj_set_bucket_tag_timeout(bucket, 0);
+
+  return 0;
+}
+
+
+int RGWBucket::check_index(RGWBucketAdminOpState& op_state,
+        map<RGWObjCategory, RGWBucketStats>& existing_stats,
+        map<RGWObjCategory, RGWBucketStats>& calculated_stats,
+        std::string *err_msg)
+{
+  rgw_bucket bucket = op_state.get_bucket();
+  bool fix_index = op_state.will_fix_index();
+
+  int r = store->bucket_check_index(bucket, &existing_stats, &calculated_stats);
+  if (r < 0) {
+    set_err_msg(err_msg, "failed to check index error=" + cpp_strerror(-r));
+    return r;
+  }
+
+  if (fix_index) {
+    r = store->bucket_rebuild_index(bucket);
+    if (r < 0) {
+      set_err_msg(err_msg, "failed to rebuild index err=" + cpp_strerror(-r));
+      return r;
+    }
+  }
+
+  return 0;
+}
+
+int RGWBucket::get_policy(RGWBucketAdminOpState& op_state, ostream& o)
+{
+  std::string object_name = op_state.get_object_name();
+  rgw_bucket bucket = op_state.get_bucket();
+
+  bufferlist bl;
+  rgw_obj obj(bucket, object_name);
+  int ret = store->get_attr(NULL, obj, RGW_ATTR_ACL, bl);
+  if (ret < 0)
+    return ret;
+
+  RGWAccessControlPolicy_S3 policy(g_ceph_context);
+  bufferlist::iterator iter = bl.begin();
+  try {
+    policy.decode(iter);
+  } catch (buffer::error& err) {
+    dout(0) << "ERROR: caught buffer::error, could not decode policy" << dendl;
+    return -EIO;
+  }
+  policy.to_xml(o);
+
+  return 0;
+}
+
+
+int RGWBucketAdminOp::get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  ostream& os)
+{
+   RGWBucket bucket;
+
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  ret = bucket.get_policy(op_state, os);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+/* Wrappers to facilitate RESTful interface */
+
+
+int RGWBucketAdminOp::get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  std::ostringstream policy_stream;
+
+  int ret = get_policy(store, op_state, policy_stream);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  flusher.start(0);
+
+  formatter->dump_string("policy", policy_stream.str());
+
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWBucketAdminOp::unlink(RGWRados *store, RGWBucketAdminOpState& op_state)
+{
+  RGWBucket bucket;
+
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  return bucket.unlink(op_state);
+}
+
+int RGWBucketAdminOp::link(RGWRados *store, RGWBucketAdminOpState& op_state)
+{
+  RGWBucket bucket;
+
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  return bucket.link(op_state);
+
+}
+
+int RGWBucketAdminOp::check_index(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  int ret;
+  map<string, RGWObjEnt> result;
+  map<RGWObjCategory, RGWBucketStats> existing_stats;
+  map<RGWObjCategory, RGWBucketStats> calculated_stats;
+  list<std::string> objs_to_unlink;
+
+  RGWBucket bucket;
+
+  ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+  flusher.start(0);
+
+  ret = bucket.check_bad_index_multipart(op_state, objs_to_unlink);
+  if (ret < 0)
+    return ret;
+
+  dump_mulipart_index_results(objs_to_unlink, formatter);
+  flusher.flush();
+
+  ret = bucket.check_object_index(op_state, result);
+  if (ret < 0)
+    return ret;
+
+  dump_bucket_index(result,  formatter);
+  flusher.flush();
+
+  ret = bucket.check_index(op_state, existing_stats, calculated_stats);
+  if (ret < 0)
+    return ret;
+
+  dump_index_check(existing_stats, calculated_stats, formatter);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWBucketAdminOp::remove_bucket(RGWRados *store, RGWBucketAdminOpState& op_state)
+{
+  RGWBucket bucket;
+
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  return bucket.remove(op_state);
+}
+
+int RGWBucketAdminOp::remove_object(RGWRados *store, RGWBucketAdminOpState& op_state)
+{
+  RGWBucket bucket;
+
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  return bucket.remove_object(op_state);
+}
+
+static int bucket_stats(RGWRados *store, std::string&  bucket_name, Formatter *formatter)
+{
+  RGWBucketInfo bucket_info;
+  rgw_bucket bucket;
+  map<RGWObjCategory, RGWBucketStats> stats;
+
+  int r = store->get_bucket_info(NULL, bucket_name, bucket_info);
+  if (r < 0)
+    return r;
+
+  bucket = bucket_info.bucket;
+
+  int ret = store->get_bucket_stats(bucket, stats);
+  if (ret < 0) {
+    cerr << "error getting bucket stats ret=" << ret << std::endl;
+    return ret;
+  }
+
+  formatter->open_object_section("stats");
+  formatter->dump_string("bucket", bucket.name);
+  formatter->dump_string("pool", bucket.pool);
+
+  formatter->dump_string("id", bucket.bucket_id);
+  formatter->dump_string("marker", bucket.marker);
+  formatter->dump_string("owner", bucket_info.owner);
+  dump_bucket_usage(stats, formatter);
+  formatter->close_section();
+
+  return 0;
+}
+
+
+int RGWBucketAdminOp::info(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWBucket bucket;
+  int ret = bucket.init(store, op_state);
+  if (ret < 0)
+    return ret;
+  string bucket_name = op_state.get_bucket_name();
+
+  Formatter *formatter = flusher.get_formatter();
+  flusher.start(0);
+
+  bool show_stats = op_state.will_fetch_stats();
+  if (op_state.is_user_op()) {
+    formatter->open_array_section("buckets");
+
+    RGWUserBuckets buckets = op_state.get_user_buckets();
+    map<string, RGWBucketEnt>& m = buckets.get_buckets();
+    map<string, RGWBucketEnt>::iterator iter;
+
+    for (iter = m.begin(); iter != m.end(); ++iter) {
+      std::string  obj_name = iter->first;
+      if (show_stats)
+        bucket_stats(store, obj_name, formatter);
+      else
+        formatter->dump_string("bucket", obj_name);
+    }
+
+    formatter->close_section();
+  } else if (!bucket_name.empty()) {
+    bucket_stats(store, bucket_name, formatter);
+  } else {
+    RGWAccessHandle handle;
+
+    if (store->list_buckets_init(&handle) > 0) {
+      RGWObjEnt obj;
+      while (store->list_buckets_next(obj, &handle) >= 0) {
+       formatter->dump_string("bucket", obj.name);
+        if (show_stats)
+          bucket_stats(store, obj.name, formatter);
+      }
+    }
+  }
+
+  flusher.flush();
+
+  return 0;
+}
+
diff --git a/src/rgw/rgw_bucket.h b/src/rgw/rgw_bucket.h
new file mode 100644 (file)
index 0000000..c1a8f60
--- /dev/null
@@ -0,0 +1,225 @@
+#ifndef CEPH_RGW_BUCKET_H
+#define CEPH_RGW_BUCKET_H
+
+#include <string>
+
+#include "include/types.h"
+#include "rgw_common.h"
+#include "rgw_tools.h"
+
+#include "rgw_rados.h"
+
+#include "rgw_string.h"
+
+#include "common/Formatter.h"
+#include "rgw_formats.h"
+
+
+using namespace std;
+
+// define as static when RGWBucket implementation compete
+extern void rgw_get_buckets_obj(string& user_id, string& buckets_obj_id);
+
+
+/**
+ * Store a list of the user's buckets, with associated functinos.
+ */
+class RGWUserBuckets
+{
+  map<string, RGWBucketEnt> buckets;
+
+public:
+  RGWUserBuckets() {}
+  void encode(bufferlist& bl) const {
+    ::encode(buckets, bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+    ::decode(buckets, bl);
+  }
+  /**
+   * Check if the user owns a bucket by the given name.
+   */
+  bool owns(string& name) {
+    map<string, RGWBucketEnt>::iterator iter;
+    iter = buckets.find(name);
+    return (iter != buckets.end());
+  }
+
+  /**
+   * Add a (created) bucket to the user's bucket list.
+   */
+  void add(RGWBucketEnt& bucket) {
+    buckets[bucket.bucket.name] = bucket;
+  }
+
+  /**
+   * Remove a bucket from the user's list by name.
+   */
+  void remove(string& name) {
+    map<string, RGWBucketEnt>::iterator iter;
+    iter = buckets.find(name);
+    if (iter != buckets.end()) {
+      buckets.erase(iter);
+    }
+  }
+
+  /**
+   * Get the user's buckets as a map.
+   */
+  map<string, RGWBucketEnt>& get_buckets() { return buckets; }
+
+  /**
+   * Cleanup data structure
+   */
+  void clear() { buckets.clear(); }
+
+  size_t count() { return buckets.size(); }
+};
+WRITE_CLASS_ENCODER(RGWUserBuckets)
+
+/**
+ * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
+ * Returns: 0 on success, -ERR# on failure.
+ */
+extern int rgw_read_user_buckets(RGWRados *store, string user_id, RGWUserBuckets& buckets, bool need_stats);
+
+/**
+ * Store the set of buckets associated with a user.
+ * This completely overwrites any previously-stored list, so be careful!
+ * Returns 0 on success, -ERR# otherwise.
+ */
+extern int rgw_write_buckets_attr(RGWRados *store, string user_id, RGWUserBuckets& buckets);
+
+extern int rgw_add_bucket(RGWRados *store, string user_id, rgw_bucket& bucket);
+extern int rgw_remove_user_bucket_info(RGWRados *store, string user_id, rgw_bucket& bucket);
+
+extern int rgw_remove_object(RGWRados *store, rgw_bucket& bucket, std::string& object);
+extern int rgw_remove_bucket(RGWRados *store, rgw_bucket& bucket, bool delete_children);
+
+extern void check_bad_user_bucket_mapping(RGWRados *store, const string& user_id, bool fix);
+
+struct RGWBucketAdminOpState {
+  std::string uid;
+  std::string display_name;
+  std::string bucket_name;
+  std::string bucket_id;
+  std::string object_name;
+
+  bool list_buckets;
+  bool stat_buckets;
+  bool check_objects;
+  bool fix_index;
+  bool delete_child_objects;
+  bool bucket_stored;
+
+  rgw_bucket bucket;
+  RGWUserBuckets buckets;
+
+  void set_fetch_stats(bool value) { stat_buckets = value; }
+  void set_check_objects(bool value) { check_objects = value; }
+  void set_fix_index(bool value) { fix_index = value; }
+  void set_delete_children(bool value) { delete_child_objects = value; }
+
+  void set_user_id(std::string& user_id) {
+    if (!user_id.empty())
+      uid = user_id;
+  }
+  void set_bucket_name(std::string& bucket_str) {
+    bucket_name = bucket_str; 
+  }
+  void set_object(std::string& object_str) {
+    object_name = object_str;
+  }
+
+  std::string& get_user_id() { return uid; };
+  std::string& get_user_display_name() { return display_name; };
+  std::string& get_bucket_name() { return bucket_name; };
+  std::string& get_object_name() { return object_name; };
+
+  rgw_bucket& get_bucket() { return bucket; };
+  void set_bucket(rgw_bucket& _bucket) {
+    bucket = _bucket; 
+    bucket_stored = true;
+  }
+
+  RGWUserBuckets& get_user_buckets() { return buckets; };
+  void set_user_buckets(RGWUserBuckets& _buckets) { buckets = _buckets; };
+
+  bool will_fetch_stats() { return stat_buckets; };
+  bool will_fix_index() { return fix_index; };
+  bool will_delete_children() { return delete_child_objects; };
+  bool will_check_objects() { return check_objects; };
+  bool is_user_op() { return !uid.empty(); };
+  bool is_system_op() { return uid.empty(); }; 
+  bool has_bucket_stored() { return bucket_stored; };
+
+  RGWBucketAdminOpState() : list_buckets(false), stat_buckets(false), check_objects(false), 
+                            fix_index(false), delete_child_objects(false),
+                            bucket_stored(false)  {}
+};
+
+/*
+ * A simple wrapper class for administrative bucket operations
+ */
+
+class RGWBucket
+{
+  RGWUserBuckets buckets;
+  RGWRados *store;
+  RGWAccessHandle handle;
+
+  std::string user_id;
+  std::string bucket_name;
+
+  bool failure;
+
+private:
+
+public:
+  RGWBucket() : store(NULL), failure(false) {}
+  int init(RGWRados *storage, RGWBucketAdminOpState& op_state);
+
+  int create_bucket(string bucket_str, string& user_id, string& display_name);
+  
+  int check_bad_index_multipart(RGWBucketAdminOpState& op_state,
+          list<std::string>& objs_to_unlink, std::string *err_msg = NULL);
+
+  int check_object_index(RGWBucketAdminOpState& op_state,
+          map<string, RGWObjEnt> result, std::string *err_msg = NULL);
+
+  int check_index(RGWBucketAdminOpState& op_state,
+          map<RGWObjCategory, RGWBucketStats>& existing_stats,
+          map<RGWObjCategory, RGWBucketStats>& calculated_stats,
+          std::string *err_msg = NULL);
+
+  int remove(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
+  int link(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
+  int unlink(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
+
+  int remove_object(RGWBucketAdminOpState& op_state, std::string *err_msg = NULL);
+  int get_policy(RGWBucketAdminOpState& op_state, ostream& o);
+
+  void clear_failure() { failure = false; };
+};
+
+class RGWBucketAdminOp
+{
+public:
+  static int get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher);
+  static int get_policy(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  ostream& os);
+
+
+  static int unlink(RGWRados *store, RGWBucketAdminOpState& op_state);
+  static int link(RGWRados *store, RGWBucketAdminOpState& op_state);
+
+  static int check_index(RGWRados *store, RGWBucketAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher);
+
+  static int remove_bucket(RGWRados *store, RGWBucketAdminOpState& op_state);
+  static int remove_object(RGWRados *store, RGWBucketAdminOpState& op_state);
+  static int info(RGWRados *store, RGWBucketAdminOpState& op_state, RGWFormatterFlusher& flusher);
+};
+
+#endif
index b1127bb50dab86bbb1ba74d8bc318466e57ae4f8..d9c0a809ac7116f431889192945585045c00d706 100644 (file)
@@ -92,10 +92,9 @@ is_err() const
 }
 
 
-req_state::req_state(CephContext *_cct, struct RGWEnv *e) : cct(_cct), cio(NULL), op(OP_UNKNOWN), 
-                                                            bucket_cors(NULL), has_acl_header(false),
-                                                            os_auth_token(NULL),
-                                                            env(e)
+req_state::req_state(CephContext *_cct, struct RGWEnv *e) : cct(_cct), cio(NULL), op(OP_UNKNOWN),
+                                                           bucket_cors(NULL), has_acl_header(false),
+                                                            os_auth_token(NULL), env(e)
 {
   enable_ops_log = env->conf->enable_ops_log;
   enable_usage_log = env->conf->enable_usage_log;
@@ -236,7 +235,6 @@ bool parse_iso8601(const char *s, struct tm *t)
 {
   memset(t, 0, sizeof(*t));
   const char *p = strptime(s, "%Y-%m-%dT%T", t);
-
   if (!p) {
     dout(0) << "parse_iso8601 failed" << dendl;
     return false;
@@ -432,6 +430,7 @@ int XMLArgs::parse()
 {
   int pos = 0, fpos;
   bool end = false;
+  bool admin_subresource_added = false; 
   if (str[pos] == '?') pos++;
 
   while (!end) {
@@ -458,8 +457,7 @@ int XMLArgs::parse()
           (name.compare("partNumber") == 0) ||
           (name.compare("uploadId") == 0) ||
           (name.compare("versionId") == 0) ||
-          (name.compare("torrent") == 0) ||
-          (name.compare("cors") == 0)) {
+          (name.compare("torrent") == 0)) {
         sub_resources[name] = val;
       } else if (name[0] == 'r') { // root of all evil
         if ((name.compare("response-content-type") == 0) ||
@@ -471,6 +469,17 @@ int XMLArgs::parse()
          sub_resources[name] = val;
          has_resp_modifier = true;
        }
+      } else if  ((name.compare("subuser") == 0) ||
+          (name.compare("key") == 0) ||
+          (name.compare("caps") == 0) ||
+          (name.compare("index") == 0) ||
+          (name.compare("policy") == 0) ||
+          (name.compare("object") == 0)) {
+
+        if (!admin_subresource_added) {
+          sub_resources[name] = "";
+          admin_subresource_added = true;
+        }
       }
     }
 
index b99bdf9890ea3680c5715b5d26b1a4c0f73139c4..c80037520b32422c2185e0a9c52ea4d6ab9396d7 100644 (file)
@@ -38,6 +38,8 @@
 #include "rgw_rest_swift.h"
 #include "rgw_rest_admin.h"
 #include "rgw_rest_usage.h"
+#include "rgw_rest_user.h"
+#include "rgw_rest_bucket.h"
 #include "rgw_swift_auth.h"
 #include "rgw_swift.h"
 #include "rgw_log.h"
@@ -488,6 +490,8 @@ int main(int argc, const char **argv)
   if (apis_map.count("admin") > 0) {
     RGWRESTMgr_Admin *admin_resource = new RGWRESTMgr_Admin;
     admin_resource->register_resource("usage", new RGWRESTMgr_Usage);
+    admin_resource->register_resource("user", new RGWRESTMgr_User);
+    admin_resource->register_resource("bucket", new RGWRESTMgr_Bucket);
     rest.register_resource(g_conf->rgw_admin_entry, admin_resource);
   }
 
index 66945bfe19bdedfe50fd80e3c448aa566ef6c400..43415d453242251bc02a4c753dc535e1fc89c1b9 100644 (file)
@@ -15,6 +15,7 @@
 #include "rgw_acl.h"
 #include "rgw_acl_s3.h"
 #include "rgw_user.h"
+#include "rgw_bucket.h"
 #include "rgw_log.h"
 #include "rgw_multi.h"
 #include "rgw_multi_del.h"
index adf863a441e6ab01791a7249e5ee0e30393a68fd..dab1404c6aabb20d1cae1c0b0a77279315b466c2 100644 (file)
@@ -17,6 +17,7 @@
 #include "rgw_common.h"
 #include "rgw_rados.h"
 #include "rgw_user.h"
+#include "rgw_bucket.h"
 #include "rgw_acl.h"
 #include "rgw_cors.h"
 
index d65deffad4f827e55e2dba8bcae31a9895f0f1db..134a74b49e51b68dfe014dfd2aa36ab837d8c131 100644 (file)
@@ -1573,6 +1573,29 @@ int RGWRados::delete_bucket(rgw_bucket& bucket)
   return 0;
 }
 
+
+int RGWRados::set_bucket_owner(rgw_bucket& bucket, ACLOwner& owner)
+{
+  RGWBucketInfo info;
+  map<string, bufferlist> attrs;
+  int r = get_bucket_info(NULL, bucket.name, info, &attrs);
+  if (r < 0) {
+    ldout(cct, 0) << "NOTICE: get_bucket_info on bucket=" << bucket.name << " returned err=" << r << dendl;
+    return r;
+  }
+
+  info.owner = owner.get_id();
+
+  r = put_bucket_info(bucket.name, info, false, &attrs);
+  if (r < 0) {
+    ldout(cct, 0) << "NOTICE: put_bucket_info on bucket=" << bucket.name << " returned err=" << r << dendl;
+    return r;
+  }
+
+  return 0;
+}
+
+
 int RGWRados::set_buckets_enabled(vector<rgw_bucket>& buckets, bool enabled)
 {
   int ret = 0;
index aa97d1bef3e30fde6f976d79b1d7afe6f4944fc6..bed481573bb43d70f4a1c7ec534ac03549cd56e2 100644 (file)
@@ -561,8 +561,9 @@ public:
    * Returns 0 on success, -ERR# otherwise.
    */  virtual int delete_bucket(rgw_bucket& bucket);
 
-  virtual int set_buckets_enabled(std::vector<rgw_bucket>& buckets, bool enabled);
-  virtual int bucket_suspended(rgw_bucket& bucket, bool *suspended);
+  int set_bucket_owner(rgw_bucket& bucket, ACLOwner& owner);
+  int set_buckets_enabled(std::vector<rgw_bucket>& buckets, bool enabled);
+  int bucket_suspended(rgw_bucket& bucket, bool *suspended);
 
   /** Delete an object.*/
   virtual int delete_obj(void *ctx, rgw_obj& src_obj);
index a3d3a59840d48620b6710994c18969581225ace0..ba38a034c94bd2a4f969111471e9c43eb2c71ed4 100644 (file)
@@ -506,6 +506,46 @@ int RESTArgs::get_int64(struct req_state *s, const string& name, int64_t def_val
   return 0;
 }
 
+int RESTArgs::get_uint32(struct req_state *s, const string& name, uint32_t def_val, uint32_t *val, bool *existed)
+{
+  bool exists;
+  string sval = s->args.get(name, &exists);
+
+  if (existed)
+    *existed = exists;
+
+  if (!exists) {
+    *val = def_val;
+    return 0;
+  }
+
+  int r = stringtoul(sval, val);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
+int RESTArgs::get_int32(struct req_state *s, const string& name, int32_t def_val, int32_t *val, bool *existed)
+{
+  bool exists;
+  string sval = s->args.get(name, &exists);
+
+  if (existed)
+    *existed = exists;
+
+  if (!exists) {
+    *val = def_val;
+    return 0;
+  }
+
+  int r = stringtol(sval, val);
+  if (r < 0)
+    return r;
+
+  return 0;
+}
+
 int RESTArgs::get_time(struct req_state *s, const string& name, const utime_t& def_val, utime_t *val, bool *existed)
 {
   bool exists;
index 6c24f8b2fb0b9a6be7187c81d226c64ccd455560..d8e91a2505405f9eb0705f4f7de6bb90587602b2 100644 (file)
@@ -21,6 +21,8 @@ public:
   static int get_string(struct req_state *s, const string& name, const string& def_val, string *val, bool *existed = NULL);
   static int get_uint64(struct req_state *s, const string& name, uint64_t def_val, uint64_t *val, bool *existed = NULL);
   static int get_int64(struct req_state *s, const string& name, int64_t def_val, int64_t *val, bool *existed = NULL);
+  static int get_uint32(struct req_state *s, const string& name, uint32_t def_val, uint32_t *val, bool *existed = NULL);
+  static int get_int32(struct req_state *s, const string& name, int32_t def_val, int32_t *val, bool *existed = NULL);
   static int get_time(struct req_state *s, const string& name, const utime_t& def_val, utime_t *val, bool *existed = NULL);
   static int get_epoch(struct req_state *s, const string& name, uint64_t def_val, uint64_t *epoch, bool *existed = NULL);
   static int get_bool(struct req_state *s, const string& name, bool def_val, bool *val, bool *existed = NULL);
diff --git a/src/rgw/rgw_rest_bucket.cc b/src/rgw/rgw_rest_bucket.cc
new file mode 100644 (file)
index 0000000..73f85d7
--- /dev/null
@@ -0,0 +1,257 @@
+#include "rgw_op.h"
+#include "rgw_bucket.h"
+#include "rgw_rest_bucket.h"
+
+#include "include/str_list.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+class RGWOp_Bucket_Info : public RGWRESTOp {
+
+public:
+  RGWOp_Bucket_Info() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_READ);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "get_bucket_info"; }
+};
+
+void RGWOp_Bucket_Info::execute()
+{
+  RGWBucketAdminOpState op_state;
+
+  bool fetch_stats;
+
+  std::string uid;
+  std::string bucket;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+  RESTArgs::get_bool(s, "stats", false, &fetch_stats);
+
+
+  op_state.set_user_id(uid);
+  op_state.set_bucket_name(bucket);
+  op_state.set_fetch_stats(fetch_stats);
+
+  http_ret = RGWBucketAdminOp::info(store, op_state, flusher);
+}
+
+class RGWOp_Get_Policy : public RGWRESTOp {
+
+public:
+  RGWOp_Get_Policy() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_READ);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "get_policy"; }
+};
+
+void RGWOp_Get_Policy::execute()
+{
+  RGWBucketAdminOpState op_state;
+
+  std::string bucket;
+  std::string object;
+
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+  RESTArgs::get_string(s, "object", object, &object);
+
+  op_state.set_bucket_name(bucket);
+  op_state.set_object(object);
+
+  http_ret = RGWBucketAdminOp::get_policy(store, op_state, flusher);
+}
+
+class RGWOp_Check_Bucket_Index : public RGWRESTOp {
+
+public:
+  RGWOp_Check_Bucket_Index() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "check_bucket_index"; }
+};
+
+void RGWOp_Check_Bucket_Index::execute()
+{
+  std::string bucket;
+
+  bool fix_index;
+  bool check_objects;
+
+  RGWBucketAdminOpState op_state;
+
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+  RESTArgs::get_bool(s, "fix", false, &fix_index);
+  RESTArgs::get_bool(s, "check-objects", false, &check_objects);
+
+  op_state.set_bucket_name(bucket);
+  op_state.set_fix_index(fix_index);
+  op_state.set_check_objects(check_objects);
+
+  http_ret = RGWBucketAdminOp::check_index(store, op_state, flusher);
+}
+
+class RGWOp_Bucket_Link : public RGWRESTOp {
+
+public:
+  RGWOp_Bucket_Link() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "link_bucket"; }
+};
+
+void RGWOp_Bucket_Link::execute()
+{
+  std::string uid;
+  std::string bucket;
+
+  RGWBucketAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+
+  op_state.set_user_id(uid);
+  op_state.set_bucket_name(bucket);
+
+  http_ret = RGWBucketAdminOp::link(store, op_state);
+}
+
+class RGWOp_Bucket_Unlink : public RGWRESTOp {
+
+public:
+  RGWOp_Bucket_Unlink() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "unlink_bucket"; }
+};
+
+void RGWOp_Bucket_Unlink::execute()
+{
+  std::string uid;
+  std::string bucket;
+
+  RGWBucketAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+
+  op_state.set_user_id(uid);
+  op_state.set_bucket_name(bucket);
+
+  http_ret = RGWBucketAdminOp::unlink(store, op_state);
+}
+
+class RGWOp_Bucket_Remove : public RGWRESTOp {
+
+public:
+  RGWOp_Bucket_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_bucket"; }
+};
+
+void RGWOp_Bucket_Remove::execute()
+{
+  std::string bucket;
+  bool delete_children;
+
+  RGWBucketAdminOpState op_state;
+
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+  RESTArgs::get_bool(s, "purge-objects", false, &delete_children);
+
+  op_state.set_bucket_name(bucket);
+  op_state.set_delete_children(delete_children);
+
+  http_ret = RGWBucketAdminOp::remove_bucket(store, op_state);
+}
+
+class RGWOp_Object_Remove: public RGWRESTOp {
+
+public:
+  RGWOp_Object_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("buckets", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_object"; }
+};
+
+void RGWOp_Object_Remove::execute()
+{
+  std::string bucket;
+  std::string object;
+
+  RGWBucketAdminOpState op_state;
+
+  RESTArgs::get_string(s, "bucket", bucket, &bucket);
+  RESTArgs::get_string(s, "object", object, &object);
+
+  op_state.set_bucket_name(bucket);
+  op_state.set_object(object);
+
+  http_ret = RGWBucketAdminOp::remove_object(store, op_state);
+}
+
+RGWOp *RGWHandler_Bucket::op_get()
+{
+
+  if (s->args.sub_resource_exists("policy"))
+    return new RGWOp_Get_Policy;
+
+  if (s->args.sub_resource_exists("index"))
+    return new RGWOp_Check_Bucket_Index;
+
+  return new RGWOp_Bucket_Info;
+};
+
+RGWOp *RGWHandler_Bucket::op_put()
+{
+  return new RGWOp_Bucket_Link;
+};
+
+RGWOp *RGWHandler_Bucket::op_post()
+{
+  return new RGWOp_Bucket_Unlink;
+};
+
+RGWOp *RGWHandler_Bucket::op_delete()
+{
+  if (s->args.sub_resource_exists("object"))
+    return new RGWOp_Object_Remove;
+
+  return new RGWOp_Bucket_Remove;
+};
+
diff --git a/src/rgw/rgw_rest_bucket.h b/src/rgw/rgw_rest_bucket.h
new file mode 100644 (file)
index 0000000..bb70643
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef CEPH_RGW_REST_BUCKET_H
+#define CEPH_RGW_REST_BUCKET_H
+
+#include "rgw_rest.h"
+#include "rgw_rest_s3.h"
+
+
+class RGWHandler_Bucket : public RGWHandler_Auth_S3 {
+protected:
+  RGWOp *op_get();
+  RGWOp *op_put();
+  RGWOp *op_post();
+  RGWOp *op_delete();
+public:
+  RGWHandler_Bucket() {}
+  virtual ~RGWHandler_Bucket() {}
+
+  int read_permissions(RGWOp*) {
+    return 0;
+  }
+};
+
+class RGWRESTMgr_Bucket : public RGWRESTMgr {
+public:
+  RGWRESTMgr_Bucket() {}
+  virtual ~RGWRESTMgr_Bucket() {}
+
+  RGWHandler *get_handler(struct req_state *s) {
+    return new RGWHandler_Bucket;
+  }
+};
+
+#endif
index 49494a7cdffb7175b998caf6a7d1788bc9ad7168..78efa72aa5545263305cf67c099235978de7f079 100644 (file)
@@ -1798,6 +1798,16 @@ int RGWHandler_ObjStore_S3::init(RGWRados *store, struct req_state *s, RGWClient
   return RGWHandler_ObjStore::init(store, s, cio);
 }
 
+// subresources that we shouldn't include in the uri to sign
+static const char *nonsigned_subresources[] = {
+  "key",
+  "subuser",
+  "caps",
+  "index",
+  "object",
+  "policy",
+  NULL
+};
 
 /*
  * ?get the canonical amazon-style header for something?
@@ -1908,6 +1918,22 @@ static bool get_auth_header(struct req_state *s, string& dest, bool qsr)
 
   string canon_resource;
   get_canon_resource(s, canon_resource);
+
+  map<string, string> sub_resources = s->args.get_sub_resources();
+  map<string, string>::iterator sres_iter;
+
+  for (const char **sres = nonsigned_subresources; *sres; ++sres) {
+    sres_iter = sub_resources.find(*sres);
+    if (sres_iter != sub_resources.end()) {
+      size_t pos = canon_resource.find(*sres) -1;
+      size_t len = strlen(*sres) + 1;
+      std::string sres_val = sres_iter->second;
+      if (!sres_val.empty())
+        len += sres_val.size();
+      canon_resource.erase(pos, len);
+    }
+  }
+
   dest.append(canon_resource);
 
   return true;
diff --git a/src/rgw/rgw_rest_user.cc b/src/rgw/rgw_rest_user.cc
new file mode 100644 (file)
index 0000000..bdcce87
--- /dev/null
@@ -0,0 +1,629 @@
+#include "rgw_op.h"
+#include "rgw_user.h"
+#include "rgw_rest_user.h"
+
+#include "include/str_list.h"
+
+#define dout_subsys ceph_subsys_rgw
+
+class RGWOp_User_Info : public RGWRESTOp {
+
+public:
+  RGWOp_User_Info() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_READ);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "get_user_info"; }
+};
+
+void RGWOp_User_Info::execute()
+{
+  RGWUserAdminOpState op_state;
+
+  std::string uid;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+
+  op_state.set_user_id(uid);
+
+  http_ret = RGWUserAdminOp_User::info(store, op_state, flusher);
+}
+
+class RGWOp_User_Create : public RGWRESTOp {
+
+public:
+  RGWOp_User_Create() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "create_user"; }
+};
+
+void RGWOp_User_Create::execute()
+{
+  std::string uid;
+  std::string display_name;
+  std::string email;
+  std::string access_key;
+  std::string secret_key;
+  std::string key_type_str;
+  std::string caps;
+
+  bool gen_key;
+  bool suspended;
+
+  uint32_t max_buckets;
+  int32_t key_type = KEY_TYPE_UNDEFINED;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "display-name", display_name, &display_name);
+  RESTArgs::get_string(s, "email", email, &email);
+  RESTArgs::get_string(s, "access-key", access_key, &access_key);
+  RESTArgs::get_string(s, "secret-key", secret_key, &secret_key);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+  RESTArgs::get_string(s, "user-caps", caps, &caps);
+  RESTArgs::get_bool(s, "generate-key", true, &gen_key);
+  RESTArgs::get_bool(s, "suspended", false, &suspended);
+  RESTArgs::get_uint32(s, "max-buckets", RGW_DEFAULT_MAX_BUCKETS, &max_buckets);
+
+  // FIXME: don't do double argument checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!display_name.empty())
+    op_state.set_display_name(display_name);
+
+  if (!email.empty())
+    op_state.set_user_email(email);
+
+  if (!caps.empty())
+    op_state.set_caps(caps);
+
+  if (!access_key.empty())
+    op_state.set_access_key(access_key);
+
+  if (!secret_key.empty())
+    op_state.set_secret_key(secret_key);
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+
+    op_state.set_key_type(key_type);
+  }
+
+  if (max_buckets != RGW_DEFAULT_MAX_BUCKETS)
+    op_state.set_max_buckets(max_buckets);
+
+  if (s->args.exists("suspended"))
+    op_state.set_suspension(suspended);
+
+  if (gen_key)
+    op_state.set_generate_key();
+
+  http_ret = RGWUserAdminOp_User::create(store, op_state, flusher);
+}
+
+class RGWOp_User_Modify : public RGWRESTOp {
+
+public:
+  RGWOp_User_Modify() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "modify_user"; }
+};
+
+void RGWOp_User_Modify::execute()
+{
+  std::string uid;
+  std::string display_name;
+  std::string email;
+  std::string access_key;
+  std::string secret_key;
+  std::string key_type_str;
+  std::string caps;
+
+  bool gen_key;
+  bool suspended;
+
+  uint32_t max_buckets;
+  int32_t key_type = KEY_TYPE_UNDEFINED;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "display-name", display_name, &display_name);
+  RESTArgs::get_string(s, "email", email, &email);
+  RESTArgs::get_string(s, "access-key", access_key, &access_key);
+  RESTArgs::get_string(s, "secret-key", secret_key, &secret_key);
+  RESTArgs::get_string(s, "user-caps", caps, &caps);
+  RESTArgs::get_bool(s, "generate-key", false, &gen_key);
+  RESTArgs::get_bool(s, "suspended", false, &suspended);
+  RESTArgs::get_uint32(s, "max-buckets", RGW_DEFAULT_MAX_BUCKETS, &max_buckets);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!display_name.empty())
+    op_state.set_display_name(display_name);
+
+  if (!email.empty())
+    op_state.set_user_email(email);
+
+  if (!caps.empty())
+    op_state.set_caps(caps);
+
+  if (!access_key.empty())
+    op_state.set_access_key(access_key);
+
+  if (!secret_key.empty())
+    op_state.set_secret_key(secret_key);
+
+  if (max_buckets != RGW_DEFAULT_MAX_BUCKETS)
+    op_state.set_max_buckets(max_buckets);
+
+  if (gen_key)
+    op_state.set_generate_key();
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+
+    op_state.set_key_type(key_type);
+  }
+
+  if (s->args.exists("suspended"))
+    op_state.set_suspension(suspended);
+
+  http_ret = RGWUserAdminOp_User::modify(store, op_state, flusher);
+}
+
+class RGWOp_User_Remove : public RGWRESTOp {
+
+public:
+  RGWOp_User_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_user"; }
+};
+
+void RGWOp_User_Remove::execute()
+{
+  std::string uid;
+  bool purge_data;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_bool(s, "purge-data", false, &purge_data);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  op_state.set_purge_data(purge_data);
+
+  http_ret = RGWUserAdminOp_User::remove(store, op_state, flusher);
+}
+
+class RGWOp_Subuser_Create : public RGWRESTOp {
+
+public:
+  RGWOp_Subuser_Create() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "create_subuser"; }
+};
+
+void RGWOp_Subuser_Create::execute()
+{
+  std::string uid;
+  std::string subuser;
+  std::string secret_key;
+  std::string perm_str;
+  std::string key_type_str;
+
+  bool gen_subuser = false; // FIXME placeholder
+  bool gen_secret;
+
+  uint32_t perm_mask = 0;
+  int32_t key_type = KEY_TYPE_SWIFT;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "subuser", subuser, &subuser);
+  RESTArgs::get_string(s, "secret-key", secret_key, &secret_key);
+  RESTArgs::get_string(s, "access", perm_str, &perm_str);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+  //RESTArgs::get_bool(s, "generate-subuser", false, &gen_subuser);
+  RESTArgs::get_bool(s, "generate-secret", false, &gen_secret);
+
+  perm_mask = rgw_str_to_perm(perm_str.c_str());
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!subuser.empty())
+    op_state.set_subuser(subuser);
+
+  if (!secret_key.empty())
+    op_state.set_secret_key(secret_key);
+
+  if (perm_mask != 0)
+    op_state.set_perm(perm_mask);
+
+  op_state.set_generate_subuser(gen_subuser);
+
+  if (gen_secret)
+    op_state.set_gen_secret();
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+  }
+  op_state.set_key_type(key_type);
+
+  http_ret = RGWUserAdminOp_Subuser::create(store, op_state, flusher);
+}
+
+class RGWOp_Subuser_Modify : public RGWRESTOp {
+
+public:
+  RGWOp_Subuser_Modify() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "modify_subuser"; }
+};
+
+void RGWOp_Subuser_Modify::execute()
+{
+  std::string uid;
+  std::string subuser;
+  std::string secret_key;
+  std::string key_type_str;
+  std::string perm_str;
+
+  RGWUserAdminOpState op_state;
+
+  uint32_t perm_mask;
+  int32_t key_type = KEY_TYPE_SWIFT;
+
+  bool gen_secret;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "subuser", subuser, &subuser);
+  RESTArgs::get_string(s, "secret-key", secret_key, &secret_key);
+  RESTArgs::get_string(s, "access", perm_str, &perm_str);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+  RESTArgs::get_bool(s, "generate-secret", false, &gen_secret);
+
+  perm_mask = rgw_str_to_perm(perm_str.c_str());
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!subuser.empty())
+    op_state.set_subuser(subuser);
+
+  if (!secret_key.empty())
+    op_state.set_secret_key(secret_key);
+
+  if (gen_secret)
+    op_state.set_gen_secret();
+
+  if (perm_mask != 0)
+    op_state.set_perm(perm_mask);
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+  }
+  op_state.set_key_type(key_type);
+
+  http_ret = RGWUserAdminOp_Subuser::modify(store, op_state, flusher);
+}
+
+class RGWOp_Subuser_Remove : public RGWRESTOp {
+
+public:
+  RGWOp_Subuser_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_subuser"; }
+};
+
+void RGWOp_Subuser_Remove::execute()
+{
+  std::string uid;
+  std::string subuser;
+  bool purge_keys;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "subuser", subuser, &subuser);
+  RESTArgs::get_bool(s, "purge-keys", true, &purge_keys);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!subuser.empty())
+    op_state.set_subuser(subuser);
+
+  if (purge_keys)
+    op_state.set_purge_keys();
+
+  http_ret = RGWUserAdminOp_Subuser::remove(store, op_state, flusher);
+}
+
+class RGWOp_Key_Create : public RGWRESTOp {
+
+public:
+  RGWOp_Key_Create() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "create_access_key"; }
+};
+
+void RGWOp_Key_Create::execute()
+{
+  std::string uid;
+  std::string subuser;
+  std::string access_key;
+  std::string secret_key;
+  std::string key_type_str;
+
+  int32_t key_type = KEY_TYPE_UNDEFINED;
+  bool gen_key;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "subuser", subuser, &subuser);
+  RESTArgs::get_string(s, "access-key", access_key, &access_key);
+  RESTArgs::get_string(s, "secret-key", secret_key, &secret_key);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+  RESTArgs::get_bool(s, "generate-key", true, &gen_key);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!subuser.empty())
+    op_state.set_subuser(subuser);
+
+  if (!access_key.empty())
+    op_state.set_access_key(access_key);
+
+  if (!secret_key.empty())
+    op_state.set_secret_key(secret_key);
+
+  if (gen_key)
+    op_state.set_generate_key();
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+
+    op_state.set_key_type(key_type);
+  }
+
+  http_ret = RGWUserAdminOp_Key::create(store, op_state, flusher);
+}
+
+class RGWOp_Key_Remove : public RGWRESTOp {
+
+public:
+  RGWOp_Key_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_access_key"; }
+};
+
+void RGWOp_Key_Remove::execute()
+{
+  std::string uid;
+  std::string subuser;
+  std::string access_key;
+  std::string key_type_str;
+
+  int32_t key_type = KEY_TYPE_UNDEFINED;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "subuser", subuser, &subuser);
+  RESTArgs::get_string(s, "access-key", access_key, &access_key);
+  RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!subuser.empty())
+    op_state.set_subuser(subuser);
+
+  if (!access_key.empty())
+    op_state.set_access_key(access_key);
+
+  if (!key_type_str.empty()) {
+    if (key_type_str.compare("swift") == 0)
+      key_type = KEY_TYPE_SWIFT;
+    else if (key_type_str.compare("s3") == 0)
+      key_type = KEY_TYPE_S3;
+
+    op_state.set_key_type(key_type);
+  }
+
+  http_ret = RGWUserAdminOp_Key::remove(store, op_state, flusher);
+}
+
+class RGWOp_Caps_Add : public RGWRESTOp {
+
+public:
+  RGWOp_Caps_Add() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "add_user_caps"; }
+};
+
+void RGWOp_Caps_Add::execute()
+{
+  std::string uid;
+  std::string caps;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "user-caps", caps, &caps);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!caps.empty())
+    op_state.set_caps(caps);
+
+  http_ret = RGWUserAdminOp_Caps::add(store, op_state, flusher);
+}
+
+class RGWOp_Caps_Remove : public RGWRESTOp {
+
+public:
+  RGWOp_Caps_Remove() {}
+
+  int check_caps(RGWUserCaps& caps) {
+    return caps.check_cap("users", RGW_CAP_WRITE);
+  }
+
+  void execute();
+
+  virtual const char *name() { return "remove_user_caps"; }
+};
+
+void RGWOp_Caps_Remove::execute()
+{
+  std::string uid;
+  std::string caps;
+
+  RGWUserAdminOpState op_state;
+
+  RESTArgs::get_string(s, "uid", uid, &uid);
+  RESTArgs::get_string(s, "user-caps", caps, &caps);
+
+  // FIXME: no double checking
+  if (!uid.empty())
+    op_state.set_user_id(uid);
+
+  if (!caps.empty())
+    op_state.set_caps(caps);
+
+  http_ret = RGWUserAdminOp_Caps::remove(store, op_state, flusher);
+}
+
+RGWOp *RGWHandler_User::op_get()
+{
+  return new RGWOp_User_Info;
+};
+
+RGWOp *RGWHandler_User::op_put()
+{
+  if (s->args.sub_resource_exists("subuser"))
+    return new RGWOp_Subuser_Create;
+
+  if (s->args.sub_resource_exists("key"))
+    return new RGWOp_Key_Create;
+
+  if (s->args.sub_resource_exists("caps"))
+    return new RGWOp_Caps_Add;
+
+  return new RGWOp_User_Create;
+};
+
+RGWOp *RGWHandler_User::op_post()
+{
+  if (s->args.sub_resource_exists("subuser"))
+    return new RGWOp_Subuser_Modify;
+
+  return new RGWOp_User_Modify;
+};
+
+RGWOp *RGWHandler_User::op_delete()
+{
+  if (s->args.sub_resource_exists("subuser"))
+    return new RGWOp_Subuser_Remove;
+
+  if (s->args.sub_resource_exists("key"))
+    return new RGWOp_Key_Remove;
+
+  if (s->args.sub_resource_exists("caps"))
+    return new RGWOp_Caps_Remove;
+
+  return new RGWOp_User_Remove;
+};
+
diff --git a/src/rgw/rgw_rest_user.h b/src/rgw/rgw_rest_user.h
new file mode 100644 (file)
index 0000000..1fe29d6
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef CEPH_RGW_REST_USER_H
+#define CEPH_RGW_REST_USER_H
+
+#include "rgw_rest.h"
+#include "rgw_rest_s3.h"
+
+
+class RGWHandler_User : public RGWHandler_Auth_S3 {
+protected:
+  RGWOp *op_get();
+  RGWOp *op_put();
+  RGWOp *op_post();
+  RGWOp *op_delete();
+public:
+  RGWHandler_User() {}
+  virtual ~RGWHandler_User() {}
+
+  int read_permissions(RGWOp*) {
+    return 0;
+  }
+};
+
+class RGWRESTMgr_User : public RGWRESTMgr {
+public:
+  RGWRESTMgr_User() {}
+  virtual ~RGWRESTMgr_User() {}
+
+  RGWHandler *get_handler(struct req_state *s) {
+    return new RGWHandler_User;
+  }
+};
+
+#endif
index 3798c19fb3a783607eb18a27dbc358da2305353e..6aa9fcdea12fa34ad813494a84a98e458818a9eb 100644 (file)
@@ -9,6 +9,12 @@
 
 #include "include/types.h"
 #include "rgw_user.h"
+#include "rgw_string.h"
+
+// until everything is moved from rgw_common
+#include "rgw_common.h"
+
+#include "rgw_bucket.h"
 
 #define dout_subsys ceph_subsys_rgw
 
@@ -84,7 +90,7 @@ int rgw_store_user_info(RGWRados *store, RGWUserInfo& info, RGWUserInfo *old_inf
   if (ret < 0)
     return ret;
 
-  if (info.user_email.size()) {
+  if (!info.user_email.empty()) {
     if (!old_info ||
         old_info->user_email.compare(info.user_email) != 0) { /* only if new index changed */
       ret = rgw_put_system_obj(store, store->zone.user_email_pool, info.user_email, link_bl.c_str(), link_bl.length(), exclusive);
@@ -199,178 +205,6 @@ extern int rgw_get_user_info_by_access_key(RGWRados *store, string& access_key,
   return rgw_get_user_info_from_index(store, access_key, store->zone.user_keys_pool, info);
 }
 
-static void get_buckets_obj(string& user_id, string& buckets_obj_id)
-{
-    buckets_obj_id = user_id;
-    buckets_obj_id += RGW_BUCKETS_OBJ_PREFIX;
-}
-
-static int rgw_read_buckets_from_attr(RGWRados *store, string& user_id, RGWUserBuckets& buckets)
-{
-  bufferlist bl;
-  rgw_obj obj(store->zone.user_uid_pool, user_id);
-  int ret = store->get_attr(NULL, obj, RGW_ATTR_BUCKETS, bl);
-  if (ret)
-    return ret;
-
-  bufferlist::iterator iter = bl.begin();
-  try {
-    buckets.decode(iter);
-  } catch (buffer::error& err) {
-    ldout(store->ctx(), 0) << "ERROR: failed to decode buckets info, caught buffer::error" << dendl;
-    return -EIO;
-  }
-  return 0;
-}
-
-/**
- * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
- * Returns: 0 on success, -ERR# on failure.
- */
-int rgw_read_user_buckets(RGWRados *store, string user_id, RGWUserBuckets& buckets, bool need_stats)
-{
-  int ret;
-  buckets.clear();
-  if (store->supports_omap()) {
-    string buckets_obj_id;
-    get_buckets_obj(user_id, buckets_obj_id);
-    bufferlist bl;
-    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
-    bufferlist header;
-    map<string,bufferlist> m;
-
-    ret = store->omap_get_all(obj, header, m);
-    if (ret == -ENOENT)
-      ret = 0;
-
-    if (ret < 0)
-      return ret;
-
-    for (map<string,bufferlist>::iterator q = m.begin(); q != m.end(); ++q) {
-      bufferlist::iterator iter = q->second.begin();
-      RGWBucketEnt bucket;
-      ::decode(bucket, iter);
-      buckets.add(bucket);
-    }
-  } else {
-    ret = rgw_read_buckets_from_attr(store, user_id, buckets);
-    switch (ret) {
-    case 0:
-      break;
-    case -ENODATA:
-      ret = 0;
-      return 0;
-    default:
-      return ret;
-    }
-  }
-
-  if (need_stats) {
-   map<string, RGWBucketEnt>& m = buckets.get_buckets();
-   int r = store->update_containers_stats(m);
-   if (r < 0)
-     ldout(store->ctx(), 0) << "ERROR: could not get stats for buckets" << dendl;
-
-  }
-  return 0;
-}
-
-/**
- * Store the set of buckets associated with a user on a n xattr
- * not used with all backends
- * This completely overwrites any previously-stored list, so be careful!
- * Returns 0 on success, -ERR# otherwise.
- */
-int rgw_write_buckets_attr(RGWRados *store, string user_id, RGWUserBuckets& buckets)
-{
-  bufferlist bl;
-  buckets.encode(bl);
-
-  rgw_obj obj(store->zone.user_uid_pool, user_id);
-
-  int ret = store->set_attr(NULL, obj, RGW_ATTR_BUCKETS, bl);
-
-  return ret;
-}
-
-int rgw_add_bucket(RGWRados *store, string user_id, rgw_bucket& bucket)
-{
-  int ret;
-   string& bucket_name = bucket.name;
-
-  if (store->supports_omap()) {
-    bufferlist bl;
-
-    RGWBucketEnt new_bucket;
-    new_bucket.bucket = bucket;
-    new_bucket.size = 0;
-    time(&new_bucket.mtime);
-    ::encode(new_bucket, bl);
-
-    string buckets_obj_id;
-    get_buckets_obj(user_id, buckets_obj_id);
-
-    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
-    ret = store->omap_set(obj, bucket_name, bl);
-    if (ret < 0) {
-      ldout(store->ctx(), 0) << "ERROR: error adding bucket to directory: "
-                << cpp_strerror(-ret)<< dendl;
-    }
-  } else {
-    RGWUserBuckets buckets;
-
-    ret = rgw_read_user_buckets(store, user_id, buckets, false);
-    RGWBucketEnt new_bucket;
-
-    switch (ret) {
-    case 0:
-    case -ENOENT:
-    case -ENODATA:
-      new_bucket.bucket = bucket;
-      new_bucket.size = 0;
-      time(&new_bucket.mtime);
-      buckets.add(new_bucket);
-      ret = rgw_write_buckets_attr(store, user_id, buckets);
-      break;
-    default:
-      ldout(store->ctx(), 10) << "rgw_write_buckets_attr returned " << ret << dendl;
-      break;
-    }
-  }
-
-  return ret;
-}
-
-int rgw_remove_user_bucket_info(RGWRados *store, string user_id, rgw_bucket& bucket)
-{
-  int ret;
-
-  if (store->supports_omap()) {
-    bufferlist bl;
-
-    string buckets_obj_id;
-    get_buckets_obj(user_id, buckets_obj_id);
-
-    rgw_obj obj(store->zone.user_uid_pool, buckets_obj_id);
-    ret = store->omap_del(obj, bucket.name);
-    if (ret < 0) {
-      ldout(store->ctx(), 0) << "ERROR: error removing bucket from directory: "
-                << cpp_strerror(-ret)<< dendl;
-    }
-  } else {
-    RGWUserBuckets buckets;
-
-    ret = rgw_read_user_buckets(store, user_id, buckets, false);
-
-    if (ret == 0 || ret == -ENOENT) {
-      buckets.remove(bucket.name);
-      ret = rgw_write_buckets_attr(store, user_id, buckets);
-    }
-  }
-
-  return ret;
-}
-
 int rgw_remove_key_index(RGWRados *store, RGWAccessKey& access_key)
 {
   rgw_obj obj(store->zone.user_keys_pool, access_key.id);
@@ -414,8 +248,8 @@ int rgw_delete_user(RGWRados *store, RGWUserInfo& info) {
   map<string, RGWBucketEnt>& buckets = user_buckets.get_buckets();
   vector<rgw_bucket> buckets_vec;
   for (map<string, RGWBucketEnt>::iterator i = buckets.begin();
-       i != buckets.end();
-       ++i) {
+      i != buckets.end();
+      ++i) {
     RGWBucketEnt& bucket = i->second;
     buckets_vec.push_back(bucket.bucket);
   }
@@ -450,7 +284,7 @@ int rgw_delete_user(RGWRados *store, RGWUserInfo& info) {
   }
 
   string buckets_obj_id;
-  get_buckets_obj(info.user_id, buckets_obj_id);
+  rgw_get_buckets_obj(info.user_id, buckets_obj_id);
   rgw_obj uid_bucks(store->zone.user_uid_pool, buckets_obj_id);
   ldout(store->ctx(), 10) << "removing user buckets index" << dendl;
   ret = store->delete_obj(NULL, uid_bucks);
@@ -469,3 +303,1885 @@ int rgw_delete_user(RGWRados *store, RGWUserInfo& info) {
 
   return 0;
 }
+
+static bool char_is_unreserved_url(char c)
+{
+  if (isalnum(c))
+    return true;
+
+  switch (c) {
+  case '-':
+  case '.':
+  case '_':
+  case '~':
+    return true;
+  default:
+    return false;
+  }
+}
+
+struct rgw_flags_desc {
+  uint32_t mask;
+  const char *str;
+};
+
+static struct rgw_flags_desc rgw_perms[] = {
+ { RGW_PERM_FULL_CONTROL, "full-control" },
+ { RGW_PERM_READ | RGW_PERM_WRITE, "read-write" },
+ { RGW_PERM_READ, "read" },
+ { RGW_PERM_WRITE, "write" },
+ { RGW_PERM_READ_ACP, "read-acp" },
+ { RGW_PERM_WRITE_ACP, "read-acp" },
+ { 0, NULL }
+};
+
+void rgw_perm_to_str(uint32_t mask, char *buf, int len)
+{
+  const char *sep = "";
+  int pos = 0;
+  if (!mask) {
+    snprintf(buf, len, "<none>");
+    return;
+  }
+  while (mask) {
+    uint32_t orig_mask = mask;
+    for (int i = 0; rgw_perms[i].mask; i++) {
+      struct rgw_flags_desc *desc = &rgw_perms[i];
+      if ((mask & desc->mask) == desc->mask) {
+        pos += snprintf(buf + pos, len - pos, "%s%s", sep, desc->str);
+        if (pos == len)
+          return;
+        sep = ", ";
+        mask &= ~desc->mask;
+        if (!mask)
+          return;
+      }
+    }
+    if (mask == orig_mask) // no change
+      break;
+  }
+}
+
+uint32_t rgw_str_to_perm(const char *str)
+{
+  if (strcasecmp(str, "read") == 0)
+    return RGW_PERM_READ;
+  else if (strcasecmp(str, "write") == 0)
+    return RGW_PERM_WRITE;
+  else if (strcasecmp(str, "readwrite") == 0)
+    return RGW_PERM_READ | RGW_PERM_WRITE;
+  else if (strcasecmp(str, "full") == 0)
+    return RGW_PERM_FULL_CONTROL;
+
+  return 0; // better to return no permission
+}
+
+static bool validate_access_key(string& key)
+{
+  const char *p = key.c_str();
+  while (*p) {
+    if (!char_is_unreserved_url(*p))
+      return false;
+    p++;
+  }
+  return true;
+}
+
+static void set_err_msg(std::string *sink, std::string msg)
+{
+  if (sink && !msg.empty())
+    *sink = msg;
+}
+
+static bool remove_old_indexes(RGWRados *store,
+         RGWUserInfo& old_info, RGWUserInfo& new_info, std::string *err_msg)
+{
+  int ret;
+  bool success = true;
+
+  if (!old_info.user_id.empty() && old_info.user_id.compare(new_info.user_id) != 0) {
+    ret = rgw_remove_uid_index(store, old_info.user_id);
+    if (ret < 0 && ret != -ENOENT) {
+      set_err_msg(err_msg, "ERROR: could not remove index for uid " + old_info.user_id);
+      success = false;
+    }
+  }
+
+  if (!old_info.user_email.empty() &&
+      old_info.user_email.compare(new_info.user_email) != 0) {
+    ret = rgw_remove_email_index(store, old_info.user_email);
+  if (ret < 0 && ret != -ENOENT) {
+      set_err_msg(err_msg, "ERROR: could not remove index for email " + old_info.user_email);
+      success = false;
+    }
+  }
+
+  map<string, RGWAccessKey>::iterator old_iter;
+  for (old_iter = old_info.swift_keys.begin(); old_iter != old_info.swift_keys.end(); ++old_iter) {
+    RGWAccessKey& swift_key = old_iter->second;
+    map<string, RGWAccessKey>::iterator new_iter = new_info.swift_keys.find(swift_key.id);
+    if (new_iter == new_info.swift_keys.end()) {
+      ret = rgw_remove_swift_name_index(store, swift_key.id);
+      if (ret < 0 && ret != -ENOENT) {
+        set_err_msg(err_msg, "ERROR: could not remove index for swift_name " + swift_key.id);
+        success = false;
+      }
+    }
+  }
+
+  return success;
+}
+
+/*
+ * Dump either the full user info or a subset to a formatter.
+ *
+ * NOTE: It is the caller's respnsibility to ensure that the
+ * formatter is flushed at the correct time.
+ */
+
+
+static void dump_subusers_info(Formatter *f, RGWUserInfo &info)
+{
+  map<string, RGWSubUser>::iterator uiter;
+
+  f->open_array_section("subusers");
+  for (uiter = info.subusers.begin(); uiter != info.subusers.end(); ++uiter) {
+    RGWSubUser& u = uiter->second;
+    f->open_object_section("user");
+    f->dump_format("id", "%s:%s", info.user_id.c_str(), u.name.c_str());
+    char buf[256];
+    rgw_perm_to_str(u.perm_mask, buf, sizeof(buf));
+    f->dump_string("permissions", buf);
+    f->close_section();
+  }
+  f->close_section();
+}
+
+static void dump_access_keys_info(Formatter *f, RGWUserInfo &info)
+{
+  map<string, RGWAccessKey>::iterator kiter;
+  f->open_array_section("keys");
+  for (kiter = info.access_keys.begin(); kiter != info.access_keys.end(); ++kiter) {
+    RGWAccessKey& k = kiter->second;
+    const char *sep = (k.subuser.empty() ? "" : ":");
+    const char *subuser = (k.subuser.empty() ? "" : k.subuser.c_str());
+    f->open_object_section("key");
+    f->dump_format("user", "%s%s%s", info.user_id.c_str(), sep, subuser);
+    f->dump_string("access_key", k.id);
+    f->dump_string("secret_key", k.key);
+    f->close_section();
+  }
+  f->close_section();
+}
+
+static void dump_swift_keys_info(Formatter *f, RGWUserInfo &info)
+{
+  map<string, RGWAccessKey>::iterator kiter;
+  f->open_array_section("swift_keys");
+  for (kiter = info.swift_keys.begin(); kiter != info.swift_keys.end(); ++kiter) {
+    RGWAccessKey& k = kiter->second;
+    const char *sep = (k.subuser.empty() ? "" : ":");
+    const char *subuser = (k.subuser.empty() ? "" : k.subuser.c_str());
+    f->open_object_section("key");
+    f->dump_format("user", "%s%s%s", info.user_id.c_str(), sep, subuser);
+    f->dump_string("secret_key", k.key);
+    f->close_section();
+  }
+  f->close_section();
+}
+
+static void dump_user_info(Formatter *f, RGWUserInfo &info)
+{
+  f->open_object_section("user_info");
+
+  f->dump_string("user_id", info.user_id);
+  f->dump_string("display_name", info.display_name);
+  f->dump_string("email", info.user_email);
+  f->dump_int("suspended", (int)info.suspended);
+  f->dump_int("max_buckets", (int)info.max_buckets);
+
+  dump_subusers_info(f, info);
+  dump_access_keys_info(f, info);
+  dump_swift_keys_info(f, info);
+  info.caps.dump(f);
+
+  f->close_section();
+}
+
+
+RGWAccessKeyPool::RGWAccessKeyPool(RGWUser* usr)
+{
+  user = usr;
+
+  if (!user) {
+    keys_allowed = false;
+    return;
+  }
+
+  keys_allowed = true;
+
+  store = user->get_store();
+}
+
+RGWAccessKeyPool::~RGWAccessKeyPool()
+{
+
+}
+
+int RGWAccessKeyPool::init(RGWUserAdminOpState& op_state)
+{
+  if (!op_state.is_initialized()) {
+    keys_allowed = false;
+    return -EINVAL;
+  }
+
+  std::string uid = op_state.get_user_id();
+  if (uid.compare(RGW_USER_ANON_ID) == 0) {
+    keys_allowed = false;
+    return -EACCES;
+  }
+
+  swift_keys = op_state.get_swift_keys();
+  access_keys = op_state.get_access_keys();
+
+  keys_allowed = true;
+
+  return 0;
+}
+
+/*
+ * Do a fairly exhaustive search for an existing key matching the parameters
+ * given. Also handles the case where no key type was specified and updates
+ * the operation state if needed.
+ */
+
+bool RGWAccessKeyPool::check_existing_key(RGWUserAdminOpState& op_state)
+{
+  bool existing_key = false;
+
+  int key_type = op_state.get_key_type();
+  std::string kid = op_state.get_access_key();
+  std::map<std::string, RGWAccessKey>::iterator kiter;
+  std::string swift_kid = op_state.build_default_swift_kid();
+
+  RGWUserInfo dup_info;
+
+  if (kid.empty() && swift_kid.empty())
+    return false;
+
+  switch (key_type) {
+  case KEY_TYPE_SWIFT:
+    kiter = swift_keys->find(kid);
+
+    existing_key = (kiter != swift_keys->end());
+    if (existing_key)
+      break;
+
+    if (swift_kid.empty())
+      return false;
+
+    kiter = swift_keys->find(swift_kid);
+
+    existing_key = (kiter != swift_keys->end());
+    if (existing_key)
+      op_state.set_access_key(swift_kid);
+
+    break;
+  case KEY_TYPE_S3:
+    kiter = access_keys->find(kid);
+    existing_key = (kiter != access_keys->end());
+
+    break;
+  default:
+    kiter = access_keys->find(kid);
+
+    existing_key = (kiter != access_keys->end());
+    if (existing_key) {
+      op_state.set_key_type(KEY_TYPE_S3);
+      break;
+    }
+
+    kiter = swift_keys->find(kid);
+
+    existing_key = (kiter != swift_keys->end());
+    if (existing_key) {
+      op_state.set_key_type(KEY_TYPE_SWIFT);
+      break;
+    }
+
+    // handle the case where the access key was not provided in user:key format
+    if (swift_kid.empty())
+      return false;
+
+    kiter = swift_keys->find(swift_kid);
+
+    existing_key = (kiter != swift_keys->end());
+    if (existing_key) {
+      op_state.set_access_key(swift_kid);
+      op_state.set_key_type(KEY_TYPE_SWIFT);
+    }
+  }
+
+  op_state.set_existing_key(existing_key);
+
+  return existing_key;
+}
+
+int RGWAccessKeyPool::check_op(RGWUserAdminOpState& op_state,
+     std::string *err_msg)
+{
+  std::string subprocess_msg;
+  RGWUserInfo dup_info;
+
+  if (!op_state.is_populated()) {
+    set_err_msg(err_msg, "user info was not populated");
+    return -EINVAL;
+  }
+
+  if (!keys_allowed) {
+    set_err_msg(err_msg, "keys not allowed for this user");
+    return -EACCES;
+  }
+
+  std::string access_key = op_state.get_access_key();
+  std::string secret_key = op_state.get_secret_key();
+
+  /* see if the access key or secret key was specified */
+  if (!op_state.will_gen_access() && access_key.empty()) {
+    set_err_msg(err_msg, "empty access key");
+    return -EINVAL;
+  }
+
+  // don't check for secret key because we may be doing a removal
+
+  check_existing_key(op_state);
+
+  // if a key type wasn't specified set it to s3
+  if (op_state.get_key_type() < 0)
+    op_state.set_key_type(KEY_TYPE_S3);
+
+  return 0;
+}
+
+// Generate a new random key
+int RGWAccessKeyPool::generate_key(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  std::string duplicate_check_id;
+  std::string id;
+  std::string key;
+
+  std::pair<std::string, RGWAccessKey> key_pair;
+  RGWAccessKey new_key;
+  RGWUserInfo duplicate_check;
+
+  int ret = 0;
+  int key_type = op_state.get_key_type();
+  bool gen_access = op_state.will_gen_access();
+  bool gen_secret = op_state.will_gen_secret();
+  std::string subuser = op_state.get_subuser();
+
+  if (!keys_allowed) {
+    set_err_msg(err_msg, "access keys not allowed for this user");
+    return -EACCES;
+  }
+
+  if (op_state.has_existing_key()) {
+    set_err_msg(err_msg, "cannot create existing key");
+    return -EEXIST;
+  }
+
+  if (!gen_access) {
+    id = op_state.get_access_key();
+  }
+
+  if (!id.empty()) {
+    switch (key_type) {
+    case KEY_TYPE_SWIFT:
+      if (rgw_get_user_info_by_swift(store, id, duplicate_check) >= 0) {
+        set_err_msg(err_msg, "existing swift key in RGW system:" + id);
+        return -EEXIST;
+      }
+    case KEY_TYPE_S3:
+      if (rgw_get_user_info_by_access_key(store, id, duplicate_check) >= 0) {
+        set_err_msg(err_msg, "existing S3 key in RGW system:" + id);
+        return -EEXIST;
+      }
+    }
+  }
+
+  if (op_state.has_subuser())
+    new_key.subuser = op_state.get_subuser();
+
+  if (!gen_secret) {
+    key = op_state.get_secret_key();
+  } else if (gen_secret) {
+    char secret_key_buf[SECRET_KEY_LEN + 1];
+
+    ret = gen_rand_base64(g_ceph_context, secret_key_buf, sizeof(secret_key_buf));
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to generate secret key");
+      return ret;
+    }
+
+    key = secret_key_buf;
+  }
+
+  // Generate the access key
+  if (key_type == KEY_TYPE_S3 && gen_access) {
+    char public_id_buf[PUBLIC_ID_LEN + 1];
+
+    do {
+      int id_buf_size = sizeof(public_id_buf);
+      ret = gen_rand_alphanumeric_upper(g_ceph_context,
+               public_id_buf, id_buf_size);
+
+      if (ret < 0) {
+        set_err_msg(err_msg, "unable to generate access key");
+        return ret;
+      }
+
+      id = public_id_buf;
+      if (!validate_access_key(id))
+        continue;
+
+    } while (!rgw_get_user_info_by_access_key(store, id, duplicate_check));
+  }
+
+  if (key_type == KEY_TYPE_SWIFT && gen_access) {
+    id = op_state.build_default_swift_kid();
+    if (id.empty()) {
+      set_err_msg(err_msg, "empty swift access key");
+      return -EINVAL;
+    }
+
+    // check that the access key doesn't exist
+    if (rgw_get_user_info_by_swift(store, id, duplicate_check) >= 0) {
+      set_err_msg(err_msg, "cannot create existing swift key");
+      return -EEXIST;
+    }
+  }
+
+  // finally create the new key
+  new_key.id = id;
+  new_key.key = key;
+
+  key_pair.first = id;
+  key_pair.second = new_key;
+
+  if (key_type == KEY_TYPE_S3) {
+    access_keys->insert(key_pair);
+  } else if (key_type == KEY_TYPE_SWIFT) {
+    swift_keys->insert(key_pair);
+  }
+
+  return 0;
+}
+
+// modify an existing key
+int RGWAccessKeyPool::modify_key(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  std::string id = op_state.get_access_key();
+  std::string key = op_state.get_secret_key();
+  int key_type = op_state.get_key_type();
+
+  RGWAccessKey modify_key;
+
+  pair<string, RGWAccessKey> key_pair;
+  map<std::string, RGWAccessKey>::iterator kiter;
+
+  if (id.empty()) {
+    set_err_msg(err_msg, "no access key specified");
+    return -EINVAL;
+  }
+
+  if (!op_state.has_existing_key()) {
+    set_err_msg(err_msg, "key does not exist");
+    return -EINVAL;
+  }
+
+  key_pair.first = id;
+
+  if (key_type == KEY_TYPE_SWIFT) {
+    kiter = swift_keys->find(id);
+    modify_key = kiter->second;
+  } else if (key_type == KEY_TYPE_S3) {
+    kiter = access_keys->find(id);
+    modify_key = kiter->second;
+  } else {
+    set_err_msg(err_msg, "invalid key type");
+    return -EINVAL;
+  }
+
+  if (op_state.will_gen_secret()) {
+    char secret_key_buf[SECRET_KEY_LEN + 1];
+
+    int ret;
+    int key_buf_size = sizeof(secret_key_buf);
+    ret  = gen_rand_base64(g_ceph_context, secret_key_buf, key_buf_size);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to generate secret key");
+      return ret;
+    }
+
+    key = secret_key_buf;
+  }
+
+  if (key.empty()) {
+      set_err_msg(err_msg, "empty secret key");
+      return  -EINVAL;
+  }
+
+  // update the access key with the new secret key
+  modify_key.key = key;
+
+  key_pair.second = modify_key;
+
+
+  if (key_type == KEY_TYPE_S3) {
+    (*access_keys)[id] = modify_key;
+  } else if (key_type == KEY_TYPE_SWIFT) {
+    (*swift_keys)[id] = modify_key;
+  }
+
+  return 0;
+}
+
+int RGWAccessKeyPool::execute_add(RGWUserAdminOpState& op_state,
+         std::string *err_msg, bool defer_user_update)
+{
+  int ret = 0;
+
+  std::string subprocess_msg;
+  int key_op = GENERATE_KEY;
+
+  // set the op
+  if (op_state.has_existing_key())
+    key_op = MODIFY_KEY;
+
+  switch (key_op) {
+  case GENERATE_KEY:
+    ret = generate_key(op_state, &subprocess_msg);
+    break;
+  case MODIFY_KEY:
+    ret = modify_key(op_state, &subprocess_msg);
+    break;
+  }
+
+  if (ret < 0)
+    return ret;
+
+  // store the updated info
+  if (!defer_user_update)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWAccessKeyPool::add(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return add(op_state, err_msg, false);
+}
+
+int RGWAccessKeyPool::add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  int ret; 
+  std::string subprocess_msg;
+
+  ret = check_op(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to parse request, " + subprocess_msg);
+    return ret;
+  }
+
+  ret = execute_add(op_state, &subprocess_msg, defer_user_update);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to add access key, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWAccessKeyPool::execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  int ret = 0;
+
+  int key_type = op_state.get_key_type();
+  std::string id = op_state.get_access_key();
+  map<std::string, RGWAccessKey>::iterator kiter;
+  map<std::string, RGWAccessKey> *keys_map;
+
+  if (!op_state.has_existing_key()) {
+    set_err_msg(err_msg, "unable to find access key");
+    return -EINVAL;
+  }
+
+  if (key_type == KEY_TYPE_S3) {
+    keys_map = access_keys;
+  } else if (key_type == KEY_TYPE_SWIFT) {
+    keys_map = swift_keys;
+  } else {
+    keys_map = NULL;
+    set_err_msg(err_msg, "invalid access key");
+    return -EINVAL;
+  }
+
+  kiter = keys_map->find(id);
+  if (kiter == keys_map->end()) {
+    set_err_msg(err_msg, "key not found");
+    return -EINVAL;
+  }
+
+  rgw_remove_key_index(store, kiter->second);
+  keys_map->erase(kiter);
+
+  if (!defer_user_update)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWAccessKeyPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return remove(op_state, err_msg, false);
+}
+
+int RGWAccessKeyPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  int ret;
+
+  std::string subprocess_msg;
+
+  ret = check_op(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to parse request, " + subprocess_msg);
+    return ret;
+  }
+
+  ret = execute_remove(op_state, &subprocess_msg, defer_user_update);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove access key, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+RGWSubUserPool::RGWSubUserPool(RGWUser *usr)
+{
+   if (!usr)
+    subusers_allowed = false;
+
+  subusers_allowed = true;
+
+  store = usr->get_store();
+  user = usr;
+}
+
+RGWSubUserPool::~RGWSubUserPool()
+{
+
+}
+
+int RGWSubUserPool::init(RGWUserAdminOpState& op_state)
+{
+  if (!op_state.is_initialized()) {
+    subusers_allowed = false;
+    return -EINVAL;
+  }
+
+  std::string uid = op_state.get_user_id();
+  if (uid.compare(RGW_USER_ANON_ID) == 0) {
+    subusers_allowed = false;
+    return -EACCES;
+  }
+
+  subuser_map = op_state.get_subusers();
+  if (subuser_map == NULL) {
+    subusers_allowed = false;
+    return -EINVAL;
+  }
+
+  subusers_allowed = true;
+
+  return 0;
+}
+
+bool RGWSubUserPool::exists(std::string subuser)
+{
+  if (subuser.empty())
+    return false;
+
+  if (!subuser_map)
+    return false;
+
+  if (subuser_map->count(subuser))
+    return true;
+
+  return false;
+}
+
+int RGWSubUserPool::check_op(RGWUserAdminOpState& op_state,
+        std::string *err_msg)
+{
+  bool existing = false;
+  string subprocess_msg;
+  std::string subuser = op_state.get_subuser();
+
+  if (!op_state.is_populated()) {
+    set_err_msg(err_msg, "user info was not populated");
+    return -EINVAL;
+  }
+
+  if (!subusers_allowed) {
+    set_err_msg(err_msg, "subusers not allowed for this user");
+    return -EACCES;
+  }
+
+  if (subuser.empty() && !op_state.will_gen_subuser()) {
+    set_err_msg(err_msg, "empty subuser name");
+    return -EINVAL;
+  }
+
+  // check if the subuser exists
+  if (!subuser.empty())
+    existing = exists(subuser);
+
+  op_state.set_existing_subuser(existing);
+
+  return 0;
+}
+
+int RGWSubUserPool::execute_add(RGWUserAdminOpState& op_state,
+        std::string *err_msg, bool defer_user_update)
+{
+  int ret = 0;
+  std::string subprocess_msg;
+
+  RGWSubUser subuser;
+  std::pair<std::string, RGWSubUser> subuser_pair;
+  std::string subuser_str = op_state.get_subuser();
+
+  subuser_pair.first = subuser_str;
+
+  // no duplicates
+  if (op_state.has_existing_subuser()) {
+    set_err_msg(err_msg, "subuser exists");
+    return -EEXIST;
+  }
+
+  // assumes key should be created
+  if (op_state.has_key_op()) {
+    ret = user->keys.add(op_state, &subprocess_msg, true);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to create subuser key, " + subprocess_msg);
+      return ret;
+    }
+  }
+
+  // create the subuser
+  subuser.name = subuser_str;
+
+  if (op_state.has_subuser_perm())
+    subuser.perm_mask = op_state.get_subuser_perm();
+
+  // insert the subuser into user info
+  subuser_pair.second = subuser;
+  subuser_map->insert(subuser_pair);
+
+  // attempt to save the subuser
+  if (!defer_user_update)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWSubUserPool::add(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return add(op_state, err_msg, false);
+}
+
+int RGWSubUserPool::add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  std::string subprocess_msg;
+  int ret;
+
+  ret = check_op(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to parse request, " + subprocess_msg);
+    return ret;
+  }
+
+  ret = execute_add(op_state, &subprocess_msg, defer_user_update);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to create subuser, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWSubUserPool::execute_remove(RGWUserAdminOpState& op_state,
+        std::string *err_msg, bool defer_user_update)
+{
+  int ret = 0;
+  std::string subprocess_msg;
+
+  std::string subuser_str = op_state.get_subuser();
+
+  map<std::string, RGWSubUser>::iterator siter;
+  siter = subuser_map->find(subuser_str);
+
+  if (!op_state.has_existing_subuser()) {
+    set_err_msg(err_msg, "subuser not found: " + subuser_str);
+    return -EINVAL;
+  }
+
+  if (op_state.will_purge_keys()) {
+    // error would be non-existance so don't check
+    user->keys.remove(op_state, &subprocess_msg, true);
+  }
+
+  //remove the subuser from the user info
+  subuser_map->erase(siter);
+
+  // attempt to save the subuser
+  if (!defer_user_update)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWSubUserPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return remove(op_state, err_msg, false);
+}
+
+int RGWSubUserPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  std::string subprocess_msg;
+  int ret;
+
+  ret = check_op(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to parse request, " + subprocess_msg);
+    return ret;
+  }
+
+  ret = execute_remove(op_state, &subprocess_msg, defer_user_update);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove subuser, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWSubUserPool::execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  int ret = 0;
+  std::string subprocess_msg;
+  std::map<std::string, RGWSubUser>::iterator siter;
+  std::pair<std::string, RGWSubUser> subuser_pair;
+
+  std::string subuser_str = op_state.get_subuser();
+  RGWSubUser subuser;
+
+  if (!op_state.has_existing_subuser()) {
+    set_err_msg(err_msg, "subuser does not exist");
+    return -EINVAL;
+  }
+
+  subuser_pair.first = subuser_str;
+
+  siter = subuser_map->find(subuser_str);
+  subuser = siter->second;
+
+  if (op_state.has_key_op()) {
+    ret = user->keys.add(op_state, &subprocess_msg, true);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to create subuser keys, " + subprocess_msg);
+      return ret;
+    }
+  }
+
+  if (op_state.has_subuser_perm())
+    subuser.perm_mask = op_state.get_subuser_perm();
+
+  subuser_pair.second = subuser;
+
+  subuser_map->erase(siter);
+  subuser_map->insert(subuser_pair);
+
+  // attempt to save the subuser
+  if (!defer_user_update)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWSubUserPool::modify(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return RGWSubUserPool::modify(op_state, err_msg, false);
+}
+
+int RGWSubUserPool::modify(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_user_update)
+{
+  std::string subprocess_msg;
+  int ret;
+
+  RGWSubUser subuser;
+
+  ret = check_op(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to parse request, " + subprocess_msg);
+    return ret;
+  }
+
+  ret = execute_modify(op_state, &subprocess_msg, defer_user_update);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to modify subuser, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+RGWUserCapPool::RGWUserCapPool(RGWUser *usr)
+{
+  user = usr;
+
+  if (!user) {
+    caps_allowed = false;
+  }
+}
+
+RGWUserCapPool::~RGWUserCapPool()
+{
+
+}
+
+int RGWUserCapPool::init(RGWUserAdminOpState& op_state)
+{
+  if (!op_state.is_initialized()) {
+    caps_allowed = false;
+    return -EINVAL;
+  }
+
+  std::string uid = op_state.get_user_id();
+  if (uid == RGW_USER_ANON_ID) {
+    caps_allowed = false;
+    return -EACCES;
+  }
+
+  caps = op_state.get_caps_obj();
+  if (!caps) {
+    caps_allowed = false;
+    return -EINVAL;
+  }
+
+  caps_allowed = true;
+
+  return 0;
+}
+
+int RGWUserCapPool::add(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return add(op_state, err_msg, false);
+}
+
+int RGWUserCapPool::add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save)
+{
+  int ret = 0;
+  std::string subprocess_msg;
+  std::string caps_str = op_state.get_caps();
+
+  if (!op_state.is_populated()) {
+    set_err_msg(err_msg, "user info was not populated");
+    return -EINVAL;
+  }
+
+  if (!caps_allowed) {
+    set_err_msg(err_msg, "caps not allowed for this user");
+    return -EACCES;
+  }
+
+  if (caps_str.empty()) {
+    set_err_msg(err_msg, "empty user caps");
+    return -EINVAL;
+  }
+
+  int r = caps->add_from_string(caps_str);
+  if (r < 0) {
+    set_err_msg(err_msg, "unable to add caps: " + caps_str);
+    return r;
+  }
+
+  if (!defer_save)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUserCapPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  return remove(op_state, err_msg, false);
+}
+
+int RGWUserCapPool::remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save)
+{
+  int ret = 0;
+  std::string subprocess_msg;
+
+  std::string caps_str = op_state.get_caps();
+
+  if (!op_state.is_populated()) {
+    set_err_msg(err_msg, "user info was not populated");
+    return -EINVAL;
+  }
+
+  if (!caps_allowed) {
+    set_err_msg(err_msg, "caps not allowed for this user");
+    return -EACCES;
+  }
+
+  if (caps_str.empty()) {
+    set_err_msg(err_msg, "empty user caps");
+    return -EINVAL;
+  }
+
+  int r = caps->remove_from_string(caps_str);
+  if (r < 0) {
+    set_err_msg(err_msg, "unable to remove caps: " + caps_str);
+    return r;
+  }
+
+  if (!defer_save)
+    ret = user->update(op_state, err_msg);
+
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+RGWUser::RGWUser() : caps(this), keys(this), subusers(this)
+{
+  init_default();
+}
+
+int RGWUser::init(RGWRados *storage, RGWUserAdminOpState& op_state)
+{
+  init_default();
+
+  int ret = init_storage(storage);
+  if (ret < 0)
+    return ret;
+
+  ret = init(op_state);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+RGWUser::~RGWUser()
+{
+}
+
+void RGWUser::init_default()
+{
+  // use anonymous user info as a placeholder
+  rgw_get_anon_user(old_info);
+  user_id = RGW_USER_ANON_ID;
+
+  clear_populated();
+}
+
+int RGWUser::init_storage(RGWRados *storage)
+{
+  if (!storage) {
+    return -EINVAL;
+  }
+
+  store = storage;
+
+  clear_populated();
+
+  /* API wrappers */
+  keys = RGWAccessKeyPool(this);
+  caps = RGWUserCapPool(this);
+  subusers = RGWSubUserPool(this);
+
+  return 0;
+}
+
+int RGWUser::init(RGWUserAdminOpState& op_state)
+{
+  bool found = false;
+  std::string swift_user;
+  std::string uid = op_state.get_user_id();
+  std::string user_email = op_state.get_user_email();
+  std::string access_key = op_state.get_access_key();
+  std::string subuser = op_state.get_subuser();
+
+  int key_type = op_state.get_key_type();
+  if (key_type == KEY_TYPE_SWIFT) {
+    swift_user = op_state.get_access_key();
+    access_key.clear();
+  }
+
+  RGWUserInfo user_info;
+
+  clear_populated();
+
+  if (uid.empty() && !subuser.empty()) {
+    size_t pos = subuser.find(':');
+    if (pos != string::npos) {
+      uid = subuser.substr(0, pos);
+      op_state.set_user_id(uid);
+    }
+  }
+
+  if (!uid.empty() && (uid.compare(RGW_USER_ANON_ID) != 0))
+    found = (rgw_get_user_info_by_uid(store, uid, user_info) >= 0);
+
+  if (!user_email.empty() && !found)
+    found = (rgw_get_user_info_by_email(store, user_email, user_info) >= 0);
+
+  if (!swift_user.empty() && !found)
+    found = (rgw_get_user_info_by_swift(store, swift_user, user_info) >= 0);
+
+  if (!access_key.empty() && !found)
+    found = (rgw_get_user_info_by_access_key(store, access_key, user_info) >= 0);
+
+  op_state.set_existing_user(found);
+  if (found) {
+    op_state.set_user_info(user_info);
+    op_state.set_populated();
+
+    old_info = user_info;
+    set_populated();
+  }
+
+  user_id = user_info.user_id;
+  op_state.set_initialized();
+
+  // this may have been called by a helper object
+  int ret = init_members(op_state);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUser::init_members(RGWUserAdminOpState& op_state)
+{
+  int ret = 0;
+
+  ret = keys.init(op_state);
+  if (ret < 0)
+    return ret;
+
+  ret = subusers.init(op_state);
+  if (ret < 0)
+    return ret;
+
+  ret = caps.init(op_state);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUser::update(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  int ret;
+  std::string subprocess_msg;
+  RGWUserInfo user_info = op_state.get_user_info();
+
+  if (!store) {
+    set_err_msg(err_msg, "couldn't initialize storage");
+    return -EINVAL;
+  }
+
+  if (is_populated()) {
+    ret = rgw_store_user_info(store, user_info, &old_info, false);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to store user info");
+      return ret;
+    }
+
+    ret = remove_old_indexes(store, old_info, user_info, &subprocess_msg);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to remove old user info, " + subprocess_msg);
+      return ret;
+    }
+  } else {
+    ret = rgw_store_user_info(store, user_info, NULL, false);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to store user info");
+      return ret;
+    }
+  }
+
+  old_info = user_info;
+  set_populated();
+
+  return 0;
+}
+
+int RGWUser::check_op(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  std::string subprocess_msg;
+  bool same_id;
+  bool populated;
+  //bool existing_email = false; // this check causes a fault
+  std::string op_id = op_state.get_user_id();
+  std::string op_email = op_state.get_user_email();
+
+  RGWUserInfo user_info;
+
+  same_id = (user_id.compare(op_id) == 0);
+  populated = is_populated();
+
+  if (op_id.compare(RGW_USER_ANON_ID) == 0) {
+    set_err_msg(err_msg, "unable to perform operations on the anoymous user");
+    return -EINVAL;
+  }
+
+  if (populated && !same_id) {
+    set_err_msg(err_msg, "user id mismatch, operation id: " + op_id\
+            + " does not match: " + user_id);
+
+    return -EINVAL;
+  }
+
+  return 0;
+}
+
+int RGWUser::execute_add(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  std::string subprocess_msg;
+  int ret = 0;
+  bool defer_user_update = true;
+
+  RGWUserInfo user_info;
+
+  std::string uid = op_state.get_user_id();
+  std::string user_email = op_state.get_user_email();
+  std::string display_name = op_state.get_display_name();
+
+  // fail if the user exists already
+  if (op_state.has_existing_user()) {
+    if ((user_email.empty() || old_info.user_email == user_email) &&
+        old_info.display_name == display_name) {
+      return execute_modify(op_state, err_msg);
+    }
+
+    set_err_msg(err_msg, "user: " + op_state.user_id + " exists");
+
+    return -EEXIST;
+  }
+
+  // fail if the user_info has already been populated
+  if (op_state.is_populated()) {
+    set_err_msg(err_msg, "cannot overwrite already populated user");
+    return -EEXIST;
+  }
+
+  // fail if the display name was not included
+  if (display_name.empty()) {
+    set_err_msg(err_msg, "no display name specified");
+    return -EINVAL;
+  }
+
+  // fail if the user email is a duplicate
+  if (op_state.has_existing_email()) {
+    set_err_msg(err_msg, "duplicate email provided");
+    return -EEXIST;
+  }
+
+  // set the user info
+  user_id = uid;
+  user_info.user_id = user_id;
+  user_info.display_name = display_name;
+
+  if (!user_email.empty())
+    user_info.user_email = user_email;
+
+  user_info.max_buckets = op_state.get_max_buckets();
+  user_info.suspended = op_state.get_suspension_status();
+
+  // update the request
+  op_state.set_user_info(user_info);
+  op_state.set_populated();
+
+  // 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;
+    }
+  }
+
+  // see if we need to add some caps
+  if (op_state.has_caps_op()) {
+    ret = caps.add(op_state, &subprocess_msg, defer_user_update);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to add user capabilities, " + subprocess_msg);
+      return ret;
+    }
+  }
+
+  ret = update(op_state, err_msg);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUser::add(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_add(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to create user, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWUser::execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  int ret;
+
+  bool purge_data = op_state.will_purge_data();
+  std::string uid = op_state.get_user_id();
+  RGWUserInfo user_info = op_state.get_user_info();
+
+  if (!op_state.has_existing_user()) {
+    set_err_msg(err_msg, "user does not exist");
+    return -EINVAL;
+  }
+
+  RGWUserBuckets buckets;
+  ret = rgw_read_user_buckets(store, uid, buckets, false);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to read user bucket info");
+    return ret;
+  }
+
+  map<std::string, RGWBucketEnt>& m = buckets.get_buckets();
+  if (!m.empty() && !purge_data) {
+    set_err_msg(err_msg, "must specify purge data to remove user with buckets");
+    return -EEXIST; // change to code that maps to 409: conflict
+  }
+
+  if (!m.empty()) {
+    std::map<std::string, RGWBucketEnt>::iterator it;
+    for (it = m.begin(); it != m.end(); ++it) {
+      ret = rgw_remove_bucket(store, ((*it).second).bucket, true);
+      if (ret < 0) {
+        set_err_msg(err_msg, "unable to delete user data");
+        return ret;
+      }
+    }
+  }
+
+  ret = rgw_delete_user(store, user_info);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove user from RADOS");
+    return ret;
+  }
+
+  op_state.clear_populated();
+  clear_populated();
+
+  return 0;
+}
+
+int RGWUser::remove(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_remove(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to remove user, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWUser::execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg)
+{
+  bool same_email = false;
+  bool populated = op_state.is_populated();
+  bool defer_user_update = true;
+  int ret = 0;
+  std::string subprocess_msg;
+  std::string op_email = op_state.get_user_email();
+  std::string display_name = op_state.get_display_name();
+
+  RGWUserInfo user_info;
+  RGWUserInfo duplicate_check;
+
+  // ensure that the user info has been populated or is populate-able
+  if (!op_state.has_existing_user() && !populated) {
+    set_err_msg(err_msg, "user not found");
+    return -ENOENT;
+  }
+
+  // if the user hasn't already been populated...attempt to
+  if (!populated) {
+    ret = init(op_state);
+    if (ret < 0) {
+      set_err_msg(err_msg, "unable to retrieve user info");
+      return ret;
+    }
+  }
+
+  // ensure that we can modify the user's attributes
+  if (user_id == RGW_USER_ANON_ID) {
+    set_err_msg(err_msg, "unable to modify anonymous user's info");
+    return -EACCES;
+  }
+
+  user_info = old_info;
+
+  std::string old_email = old_info.user_email;
+  if (!op_email.empty()) {
+    same_email = (old_email.compare(op_email) == 0);
+
+  // make sure we are not adding a duplicate email
+    if (!same_email) {
+      ret = rgw_get_user_info_by_email(store, op_email, duplicate_check);
+      if (ret >= 0 && duplicate_check.user_id != user_id) {
+        set_err_msg(err_msg, "cannot add duplicate email");
+        return -EEXIST;
+      }
+    }
+    user_info.user_email = op_email;
+  }
+
+
+  // update the remaining user info
+  if (!display_name.empty())
+    user_info.display_name = display_name;
+
+  // will be set to RGW_DEFAULT_MAX_BUCKETS by default
+  uint32_t max_buckets = op_state.get_max_buckets();
+
+  ldout(store->ctx(), 0) << "max_buckets=" << max_buckets << " specified=" << op_state.max_buckets_specified << dendl;
+
+  if (op_state.max_buckets_specified)
+    user_info.max_buckets = max_buckets;
+
+  if (op_state.has_suspension_op()) {
+    __u8 suspended = op_state.get_suspension_status();
+    user_info.suspended = suspended;
+
+    RGWUserBuckets buckets;
+
+    if (user_id.empty()) {
+      set_err_msg(err_msg, "empty user id passed...aborting");
+      return -EINVAL;
+    }
+
+    ret = rgw_read_user_buckets(store, user_id, buckets, false);
+    if (ret < 0) {
+      set_err_msg(err_msg, "could not get buckets for uid:  " + user_id);
+      return ret;
+    }
+
+    map<string, RGWBucketEnt>& m = buckets.get_buckets();
+    map<string, RGWBucketEnt>::iterator iter;
+
+    vector<rgw_bucket> bucket_names;
+    for (iter = m.begin(); iter != m.end(); ++iter) {
+      RGWBucketEnt obj = iter->second;
+      bucket_names.push_back(obj.bucket);
+    }
+
+    ret = store->set_buckets_enabled(bucket_names, !suspended);
+    if (ret < 0) {
+      set_err_msg(err_msg, "failed to change pool");
+      return ret;
+    }
+  }
+  op_state.set_user_info(user_info);
+
+  // if we're supposed to modify keys, do so
+  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 or modify keys, " + subprocess_msg);
+      return ret;
+    }
+  }
+
+  ret = update(op_state, err_msg);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUser::modify(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_modify(op_state, &subprocess_msg);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to modify user, " + subprocess_msg);
+    return ret;
+  }
+
+  return 0;
+}
+
+int RGWUser::info(RGWUserAdminOpState& op_state, RGWUserInfo& fetched_info, std::string *err_msg)
+{
+  int ret = init(op_state);
+  if (ret < 0) {
+    set_err_msg(err_msg, "unable to fetch user info");
+    return ret;
+  }
+
+  fetched_info = op_state.get_user_info();
+
+  return 0;
+}
+
+int RGWUser::info(RGWUserInfo& fetched_info, std::string *err_msg)
+{
+  if (!is_populated()) {
+    set_err_msg(err_msg, "no user info saved");
+    return -EINVAL;
+  }
+
+  fetched_info = old_info;
+
+  return 0;
+}
+
+int RGWUserAdminOp_User::info(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  if (!op_state.has_existing_user())
+    return -ENOENT;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  dump_user_info(formatter, info);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_User::create(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.add(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  dump_user_info(formatter, info);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_User::modify(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.modify(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  dump_user_info(formatter, info);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_User::remove(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+
+  ret = user.remove(op_state, NULL);
+
+  return ret;
+}
+
+int RGWUserAdminOp_Subuser::create(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.subusers.add(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  dump_subusers_info(formatter, info);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_Subuser::modify(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.subusers.modify(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+  
+  flusher.start(0);
+
+  dump_subusers_info(formatter, info);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_Subuser::remove(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+
+  ret = user.subusers.remove(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUserAdminOp_Key::create(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.keys.add(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  int key_type = op_state.get_key_type();
+
+  if (key_type == KEY_TYPE_SWIFT)
+    dump_swift_keys_info(formatter, info);
+
+  else if (key_type == KEY_TYPE_S3)
+    dump_access_keys_info(formatter, info);
+
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_Key::remove(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+
+  ret = user.keys.remove(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  return 0;
+}
+
+int RGWUserAdminOp_Caps::add(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.caps.add(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  info.caps.dump(formatter);
+  flusher.flush();
+
+  return 0;
+}
+
+int RGWUserAdminOp_Caps::remove(RGWRados *store, RGWUserAdminOpState& op_state,
+                  RGWFormatterFlusher& flusher)
+{
+  RGWUserInfo info;
+  RGWUser user;
+  int ret = user.init(store, op_state);
+  if (ret < 0)
+    return ret;
+
+  Formatter *formatter = flusher.get_formatter();
+
+  ret = user.caps.remove(op_state, NULL);
+  if (ret < 0)
+    return ret;
+
+  ret = user.info(info, NULL);
+  if (ret < 0)
+    return ret;
+
+  flusher.start(0);
+
+  info.caps.dump(formatter);
+  flusher.flush();
+
+  return 0;
+}
index d8ae3c3f2ec74e0f55f0c54eb9adb8537e1919b1..d9a17e3fa62c112e30e8ec20ce4042bd0e8ee81e 100644 (file)
@@ -7,10 +7,21 @@
 #include "rgw_common.h"
 #include "rgw_tools.h"
 
+#include "rgw_rados.h"
+
+#include "rgw_string.h"
+
+#include "common/Formatter.h"
+#include "rgw_formats.h"
+
 using namespace std;
 
 #define RGW_USER_ANON_ID "anonymous"
 
+#define SECRET_KEY_LEN 40
+#define PUBLIC_ID_LEN 20
+#define RAND_SUBUSER_LEN 5
+
 /**
  * A string wrapper that includes encode/decode functions
  * for easily accessing a UID in all forms
@@ -19,7 +30,7 @@ struct RGWUID
 {
   string user_id;
   void encode(bufferlist& bl) const {
-     ::encode(user_id, bl);
+    ::encode(user_id, bl);
   }
   void decode(bufferlist::iterator& bl) {
     ::decode(user_id, bl);
@@ -68,80 +79,545 @@ extern int rgw_delete_user(RGWRados *store, RGWUserInfo& user);
 /**
  * Store a list of the user's buckets, with associated functinos.
  */
-class RGWUserBuckets
-{
-  map<string, RGWBucketEnt> buckets;
-
-public:
-  RGWUserBuckets() {}
-  void encode(bufferlist& bl) const {
-     ::encode(buckets, bl);
-  }
-  void decode(bufferlist::iterator& bl) {
-    ::decode(buckets, bl);
-  }
-  /**
-   * Check if the user owns a bucket by the given name.
-   */
-  bool owns(string& name) {
-    map<string, RGWBucketEnt>::iterator iter;
-    iter = buckets.find(name);
-    return (iter != buckets.end());
-  }
-
-  /**
-   * Add a (created) bucket to the user's bucket list.
-   */
-  void add(RGWBucketEnt& bucket) {
-    buckets[bucket.bucket.name] = bucket;
-  }
-
-  /**
-   * Remove a bucket from the user's list by name.
-   */
-  void remove(string& name) {
-    map<string, RGWBucketEnt>::iterator iter;
-    iter = buckets.find(name);
-    if (iter != buckets.end()) {
-      buckets.erase(iter);
-    }
-  }
-
-  /**
-   * Get the user's buckets as a map.
-   */
-  map<string, RGWBucketEnt>& get_buckets() { return buckets; }
-
-  /**
-   * Cleanup data structure
-   */
-  void clear() { buckets.clear(); }
-
-  size_t count() { return buckets.size(); }
-};
-WRITE_CLASS_ENCODER(RGWUserBuckets)
 
 /**
  * Get all the buckets owned by a user and fill up an RGWUserBuckets with them.
  * Returns: 0 on success, -ERR# on failure.
  */
-extern int rgw_read_user_buckets(RGWRados *store, string user_id, RGWUserBuckets& buckets, bool need_stats);
+//extern int rgw_read_user_buckets(RGWRados *store, string user_id, RGWUserBuckets& buckets, bool need_stats);
 
 /**
  * Store the set of buckets associated with a user.
  * This completely overwrites any previously-stored list, so be careful!
  * Returns 0 on success, -ERR# otherwise.
  */
-extern int rgw_write_buckets_attr(RGWRados *store, string user_id, RGWUserBuckets& buckets);
+//extern int rgw_write_buckets_attr(RGWRados *store, string user_id, RGWUserBuckets& buckets);
 
-extern int rgw_add_bucket(RGWRados *store, string user_id, rgw_bucket& bucket);
-extern int rgw_remove_user_bucket_info(RGWRados *store, string user_id, rgw_bucket& bucket);
+//extern int rgw_add_bucket(RGWRados *store, string user_id, rgw_bucket& bucket);
+//extern int rgw_remove_user_bucket_info(RGWRados *store, string user_id, rgw_bucket& bucket);
 
 /*
  * remove the different indexes
 */
+ */
 extern int rgw_remove_key_index(RGWRados *store, RGWAccessKey& access_key);
 extern int rgw_remove_uid_index(RGWRados *store, string& uid);
 extern int rgw_remove_email_index(RGWRados *store, string& email);
 extern int rgw_remove_swift_name_index(RGWRados *store, string& swift_name);
+
+/*
+ * An RGWUser class along with supporting classes created
+ * to support the creation of an RESTful administrative API
+ */
+
+extern void rgw_perm_to_str(uint32_t mask, char *buf, int len);
+extern uint32_t rgw_str_to_perm(const char *str);
+
+enum ObjectKeyType {
+  KEY_TYPE_SWIFT,
+  KEY_TYPE_S3,
+  KEY_TYPE_UNDEFINED
+};
+
+enum RGWKeyPoolOp {
+  GENERATE_KEY,
+  MODIFY_KEY
+};
+
+enum RGWUserId {
+  RGW_USER_ID,
+  RGW_SWIFT_USERNAME,
+  RGW_USER_EMAIL,
+  RGW_ACCESS_KEY,
+};
+
+struct RGWUserAdminOpState {
+  // user attributes
+  RGWUserInfo info;
+  std::string user_id;
+  std::string user_email;
+  std::string display_name;
+  uint32_t max_buckets;
+  __u8 suspended;
+  std::string caps;
+
+  // subuser attributes
+  std::string subuser;
+  uint32_t perm_mask;
+
+  // key_attributes
+  std::string id; // access key
+  std::string key; // secret key
+  int32_t key_type;
+
+  // operation attributes
+  bool existing_user;
+  bool existing_key;
+  bool existing_subuser;
+  bool existing_email;
+  bool subuser_specified;
+  bool gen_secret;
+  bool gen_access;
+  bool gen_subuser;
+  bool id_specified;
+  bool key_specified;
+  bool type_specified;
+  bool purge_data;
+  bool purge_keys;
+  bool display_name_specified;
+  bool user_email_specified;
+  bool max_buckets_specified;
+  bool perm_specified;
+  bool caps_specified;
+  bool suspension_op;
+  bool key_op;
+
+  // req parameters
+  bool populated;
+  bool initialized;
+  bool key_params_checked;
+  bool subuser_params_checked;
+  bool user_params_checked;
+
+  void set_access_key(std::string& access_key) {
+    if (access_key.empty())
+      return;
+
+    id = access_key;
+    id_specified = true;
+    gen_access = false;
+    key_op = true;
+  }
+  void set_secret_key(std::string& secret_key) {
+    if (secret_key.empty())
+      return;
+
+    key = secret_key;
+    key_specified = true;
+    gen_secret = false;
+    key_op = true;
+  }
+  void set_user_id(std::string& id) {
+    if (id.empty())
+      return;
+
+    user_id = id;
+  }
+  void set_user_email(std::string& email) {
+    if (email.empty())
+      return;
+
+    user_email = email;
+    user_email_specified = true;
+  }
+  void set_display_name(std::string& name) {
+    if (name.empty())
+      return;
+
+    display_name = name;
+    display_name_specified = true;
+  }
+  void set_subuser(std::string& _subuser) {
+    if (_subuser.empty())
+      return;
+
+    size_t pos = _subuser.find(":");
+
+    if (pos != string::npos) {
+      user_id = _subuser.substr(0, pos);
+      subuser = _subuser.substr(pos+1);
+    } else {
+      subuser = _subuser;
+    }
+
+    subuser_specified = true;
+    gen_access = true;
+  }
+  void set_caps(std::string& _caps) {
+    if (_caps.empty())
+      return;
+
+    caps = _caps;
+    caps_specified = true;
+  }
+  void set_perm(uint32_t perm) {
+    perm_mask = perm;
+    perm_specified = true;
+  }
+  void set_key_type(int32_t type) {
+    key_type = type;
+    type_specified = true;
+  }
+  void set_suspension(__u8 is_suspended) {
+    suspended = is_suspended;
+    suspension_op = true;
+  }
+  void set_user_info(RGWUserInfo& user_info) {
+    user_id = user_info.user_id;
+    info = user_info;
+  }
+  void set_max_buckets(uint32_t mb) {
+    max_buckets = mb;
+    max_buckets_specified = true;
+  }
+  void set_gen_access() {
+    gen_access = true;
+    key_op = true;
+  }
+  void set_gen_secret() {
+    gen_secret = true;
+    key_op = true;
+  }
+  void set_generate_key() {
+    if (id.empty())
+      gen_access = true;
+    if (key.empty())
+      gen_secret = true;
+    key_op = true;
+  }
+  void clear_generate_key() {
+    gen_access = false;
+    gen_secret = false;
+  }
+  void set_purge_keys() {
+    purge_keys = true;
+    key_op = true;
+  }
+
+  bool is_populated() { return populated; };
+  bool is_initialized() { return initialized; };
+  bool has_existing_user() { return existing_user; };
+  bool has_existing_key() { return existing_key; };
+  bool has_existing_subuser() { return existing_subuser; };
+  bool has_existing_email() { return existing_email; };
+  bool has_subuser() { return subuser_specified; };
+  bool has_key_op() { return key_op; };
+  bool has_caps_op() { return caps_specified; };
+  bool has_suspension_op() { return suspension_op; };
+  bool has_subuser_perm() { return perm_specified; };
+  bool will_gen_access() { return gen_access; };
+  bool will_gen_secret() { return gen_secret; };
+  bool will_gen_subuser() { return gen_subuser; };
+  bool will_purge_keys() { return purge_keys; };
+  bool will_purge_data() { return purge_data; };
+  bool will_generate_subuser() { return gen_subuser; };
+  void set_populated() { populated = true; };
+  void clear_populated() { populated = false; };
+  void set_initialized() { initialized = true; };
+  void set_existing_user(bool flag) { existing_user = flag; };
+  void set_existing_key(bool flag) { existing_key = flag; };
+  void set_existing_subuser(bool flag) { existing_subuser = flag; };
+  void set_existing_email(bool flag) { existing_email = flag; };
+  void set_purge_data(bool flag) { purge_data = flag; };
+  void set_generate_subuser(bool flag) { gen_subuser = flag; };
+  __u8 get_suspension_status() { return suspended; };
+  int32_t get_key_type() {return key_type; };
+  uint32_t get_subuser_perm() { return perm_mask; };
+  uint32_t get_max_buckets() { return max_buckets; };
+
+  std::string get_user_id() { return user_id; };
+  std::string get_subuser() { return subuser; };
+  std::string get_access_key() { return id; };
+  std::string get_secret_key() { return key; };
+  std::string get_caps() { return caps; };
+  std::string get_user_email() { return user_email; };
+  std::string get_display_name() { return display_name; };
+
+  RGWUserInfo&  get_user_info() { return info; };
+
+  map<std::string, RGWAccessKey> *get_swift_keys() { return &info.swift_keys; };
+  map<std::string, RGWAccessKey> *get_access_keys() { return &info.access_keys; };
+  map<std::string, RGWSubUser> *get_subusers() { return &info.subusers; };
+
+  RGWUserCaps *get_caps_obj() { return &info.caps; };
+
+  std::string build_default_swift_kid() {
+    if (user_id.empty() || subuser.empty())
+      return "";
+
+    std::string kid = user_id;
+    kid.append(":");
+    kid.append(subuser);
+
+    return kid;
+  }
+
+  std::string generate_subuser() {
+    if (user_id.empty())
+      return "";
+
+    std::string generated_subuser = user_id;
+    std::string rand_suffix;
+
+    int sub_buf_size = RAND_SUBUSER_LEN + 1;
+    char sub_buf[RAND_SUBUSER_LEN + 1];
+
+    if (gen_rand_alphanumeric_upper(g_ceph_context, sub_buf, sub_buf_size) < 0)
+      return "";
+
+    rand_suffix = sub_buf;
+    if (rand_suffix.empty())
+      return "";
+
+    generated_subuser.append(rand_suffix);
+    subuser = generated_subuser;
+
+    return generated_subuser;
+  }
+
+  RGWUserAdminOpState()
+  {
+    user_id = RGW_USER_ANON_ID;
+    display_name = "";
+    user_email = "";
+    id = "";
+    key = "";
+
+    max_buckets = RGW_DEFAULT_MAX_BUCKETS;
+    key_type = -1;
+    perm_mask = 0;
+    suspended = 0;
+
+    existing_user = false;
+    existing_key = false;
+    existing_subuser = false;
+    existing_email = false;
+    subuser_specified = false;
+    caps_specified = false;
+    purge_keys = false;
+    gen_secret = false;
+    gen_access = false;
+    gen_subuser = false;
+    id_specified = false;
+    key_specified = false;
+    type_specified = false;
+    purge_data = false;
+    purge_keys = false;
+    display_name_specified = false;
+    user_email_specified = false;
+    max_buckets_specified = false;
+    perm_specified = false;
+    suspension_op = false;
+    key_op = false;
+    populated = false;
+    initialized = false;
+  }
+};
+
+class RGWUser;
+
+class RGWAccessKeyPool
+{
+  RGWUser *user;
+
+  std::map<std::string, int, ltstr_nocase> key_type_map;
+  std::string user_id;
+  RGWRados *store;
+
+  map<std::string, RGWAccessKey> *swift_keys;
+  map<std::string, RGWAccessKey> *access_keys;
+
+  // we don't want to allow keys for the anonymous user or a null user
+  bool keys_allowed;
+
+private:
+  int create_key(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int generate_key(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int modify_key(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  int check_key_owner(RGWUserAdminOpState& op_state);
+  bool check_existing_key(RGWUserAdminOpState& op_state);
+  int check_op(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  /* API Contract Fulfilment */
+  int execute_add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+public:
+  RGWAccessKeyPool(RGWUser* usr);
+  ~RGWAccessKeyPool();
+
+  int init(RGWUserAdminOpState& op_state);
+
+  /* API Contracted Methods */
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  friend class RGWUser;
+  friend class RGWSubUserPool;
+};
+
+class RGWSubUserPool
+{
+  RGWUser *user;
+
+  string user_id;
+  RGWRados *store;
+  bool subusers_allowed;
+
+  map<string, RGWSubUser> *subuser_map;
+
+private:
+  int check_op(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  /* API Contract Fulfillment */
+  int execute_add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int modify(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+public:
+  RGWSubUserPool(RGWUser *user);
+  ~RGWSubUserPool();
+
+  bool exists(std::string subuser);
+  int init(RGWUserAdminOpState& op_state);
+
+  /* API contracted methods */
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int modify(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  friend class RGWUser;
+};
+
+class RGWUserCapPool
+{
+  RGWUserCaps *caps;
+  bool caps_allowed;
+  RGWUser *user;
+
+private:
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg, bool defer_save);
+
+public:
+  RGWUserCapPool(RGWUser *user);
+  ~RGWUserCapPool();
+
+  int init(RGWUserAdminOpState& op_state);
+
+  /* API contracted methods */
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  friend class RGWUser;
+};
+
+class RGWUser
+{
+
+private:
+  RGWUserInfo old_info;
+  RGWRados *store;
+
+  string user_id;
+  bool info_stored;
+
+  void set_populated() { info_stored = true; };
+  void clear_populated() { info_stored = false; };
+  bool is_populated() { return info_stored; };
+
+  int check_op(RGWUserAdminOpState&  req, std::string *err_msg);
+  int update(RGWUserAdminOpState& op_state, std::string *err_msg);
+
+  void clear_members();
+  void init_default();
+
+  /* API Contract Fulfillment */
+  int execute_add(RGWUserAdminOpState& op_state, std::string *err_msg);
+  int execute_remove(RGWUserAdminOpState& op_state, std::string *err_msg);
+  int execute_modify(RGWUserAdminOpState& op_state, std::string *err_msg);
+
+public:
+  RGWUser();
+  ~RGWUser();
+
+  int init(RGWRados *storage, RGWUserAdminOpState& op_state);
+
+  int init_storage(RGWRados *storage);
+  int init(RGWUserAdminOpState& op_state);
+  int init_members(RGWUserAdminOpState& op_state);
+
+  RGWRados *get_store() { return store; };
+
+  /* API Contracted Members */
+  RGWUserCapPool caps;
+  RGWAccessKeyPool keys;
+  RGWSubUserPool subusers;
+
+  /* API Contracted Methods */
+  int add(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+  int remove(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  /* remove an already populated RGWUser */
+  int remove(std::string *err_msg = NULL);
+
+  int modify(RGWUserAdminOpState& op_state, std::string *err_msg = NULL);
+
+  /* retrieve info from an existing user in the RGW system */
+  int info(RGWUserAdminOpState& op_state, RGWUserInfo& fetched_info, std::string *err_msg = NULL);
+
+  /* info from an already populated RGWUser */
+  int info (RGWUserInfo& fetched_info, std::string *err_msg = NULL);
+
+  friend class RGWAccessKeyPool;
+  friend class RGWSubUserPool;
+  friend class RGWUserCapPool;
+};
+
+/* Wrapers for admin API functionality */
+
+class RGWUserAdminOp_User
+{
+public:
+  static int info(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int create(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int modify(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int remove(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+};
+
+class RGWUserAdminOp_Subuser
+{
+public:
+  static int create(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int modify(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int remove(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+};
+
+class RGWUserAdminOp_Key
+{
+public:
+  static int create(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int remove(RGWRados *store,
+                  RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+};
+
+class RGWUserAdminOp_Caps
+{
+public:
+  static int add(RGWRados *store,
+                 RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+
+  static int remove(RGWRados *store,
+                 RGWUserAdminOpState& op_state, RGWFormatterFlusher& flusher);
+};
+
 #endif