]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: add user caps
authorYehuda Sadeh <yehuda@inktank.com>
Tue, 25 Sep 2012 17:55:19 +0000 (10:55 -0700)
committerYehuda Sadeh <yehuda@inktank.com>
Mon, 8 Oct 2012 18:24:57 +0000 (11:24 -0700)
User info now holds caps map, which contains a mapping between
a freestyle cap name string to permissions bitfield (read, write).

Signed-off-by: Yehuda Sadeh <yehuda@inktank.com>
src/rgw/rgw_admin.cc
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/test/cli/radosgw-admin/help.t

index b4d6fe33a98e66fc5c25036cf9839bce47897e45..d67598de61506e944a6ddce89328aecd47253922 100644 (file)
@@ -39,6 +39,8 @@ void _usage()
   cerr << "  user rm                    remove user\n";
   cerr << "  user suspend               suspend a user\n";
   cerr << "  user enable                reenable user after suspension\n";
+  cerr << "  caps add                   add user capabilities\n";
+  cerr << "  caps rm                    remove user capabilities\n";
   cerr << "  subuser create             create a new subuser\n" ;
   cerr << "  subuser modify             modify subuser\n";
   cerr << "  subuser rm                 remove subuser\n";
@@ -97,6 +99,7 @@ void _usage()
   cerr << "   --skip-zero-entries       log show only dumps entries that don't have zero value\n";
   cerr << "                             in one of the numeric field\n";
   cerr << "   --categories=<list>       comma separated list of categories, used in usage show\n";
+  cerr << "   --caps=<caps>             list of caps (e.g., \"usage=read, write; user=read\"\n";
   cerr << "   --yes-i-really-mean-it    required for certain operations\n";
   cerr << "\n";
   cerr << "<date> := \"YYYY-MM-DD[ hh:mm:ss]\"\n";
@@ -147,6 +150,8 @@ enum {
   OPT_OBJECT_RM,
   OPT_GC_LIST,
   OPT_GC_PROCESS,
+  OPT_CAPS_ADD,
+  OPT_CAPS_RM,
 };
 
 static uint32_t str_to_perm(const char *str)
@@ -220,6 +225,7 @@ static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more)
       strcmp(cmd, "usage") == 0 ||
       strcmp(cmd, "object") == 0 ||
       strcmp(cmd, "temp") == 0 ||
+      strcmp(cmd, "caps") == 0 ||
       strcmp(cmd, "gc") == 0) {
     *need_more = true;
     return 0;
@@ -285,6 +291,11 @@ static int get_cmd(const char *cmd, const char *prev_cmd, bool *need_more)
   } else if (strcmp(prev_cmd, "temp") == 0) {
     if (strcmp(cmd, "remove") == 0)
       return OPT_TEMP_REMOVE;
+  } else if (strcmp(prev_cmd, "caps") == 0) {
+    if (strcmp(cmd, "add") == 0)
+      return OPT_CAPS_ADD;
+    if (strcmp(cmd, "rm") == 0)
+      return OPT_CAPS_RM;
   } else if (strcmp(prev_cmd, "pool") == 0) {
     if (strcmp(cmd, "add") == 0)
       return OPT_POOL_ADD;
@@ -383,6 +394,8 @@ static void show_user_info(RGWUserInfo& info, Formatter *formatter)
   }
   formatter->close_section();
 
+  info.caps.dump(formatter);
+
   formatter->close_section();
   formatter->flush(cout);
   cout << std::endl;
@@ -661,6 +674,7 @@ int main(int argc, char **argv)
   int delete_child_objects = false;
   int max_buckets = -1;
   map<string, bool> categories;
+  string caps;
 
   std::string val;
   std::ostringstream errs;
@@ -755,6 +769,8 @@ int main(int argc, char **argv)
       // do nothing
     } else if (ceph_argparse_binary_flag(args, i, &yes_i_really_mean_it, NULL, "--yes-i-really-mean-it", (char*)NULL)) {
       // do nothing
+    } else if (ceph_argparse_witharg(args, i, &val, "--caps", (char*)NULL)) {
+      caps = val;
     } else {
       ++i;
     }
@@ -819,7 +835,8 @@ int main(int argc, char **argv)
 
   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_KEY_CREATE || opt_cmd == OPT_KEY_RM || opt_cmd == OPT_USER_RM ||
+                   opt_cmd == OPT_CAPS_ADD || opt_cmd == OPT_CAPS_RM);
 
   RGWStoreManager store_manager;
   store = store_manager.init(g_ceph_context, false);
@@ -970,6 +987,8 @@ int main(int argc, char **argv)
   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)
@@ -999,6 +1018,18 @@ int main(int argc, char **argv)
       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;
+      }
     }
     if (!display_name.empty())
       info.display_name = display_name;
index 07b181a9ba12827c48b5e987d46e4adaa53e90e2..7e1a3d9680757c5edc03c986aa573ff05aeeaf38 100644 (file)
@@ -9,6 +9,7 @@
 #include "common/Clock.h"
 #include "common/Formatter.h"
 #include "common/perf_counters.h"
+#include "include/str_list.h"
 #include "auth/Crypto.h"
 
 #include <sstream>
@@ -521,3 +522,176 @@ bool url_decode(string& src_str, string& dest_str)
 
   return true;
 }
+
+static struct {
+  const char *type_name;
+  int perm;
+} cap_names[] = { {"*",     RGW_CAP_ALL},
+                  {"read",  RGW_CAP_READ},
+                 {"write", RGW_CAP_WRITE},
+                 {NULL, 0} };
+
+int RGWUserCaps::parse_cap_perm(const string& str, uint32_t *perm)
+{
+  list<string> strs;
+  get_str_list(str, strs);
+  list<string>::iterator iter;
+  uint32_t v = 0;
+  for (iter = strs.begin(); iter != strs.end(); ++iter) {
+    string& s = *iter;
+    for (int i = 0; cap_names[i].type_name; i++) {
+      if (s.compare(cap_names[i].type_name) == 0)
+        v |= cap_names[i].perm;
+    }
+  }
+
+  *perm = v;
+  return 0;
+}
+
+static void trim_whitespace(const string& src, string& dst)
+{
+  const char *spacestr = " \t\n\r\f\v";
+  int start = src.find_first_not_of(spacestr);
+  if (start < 0)
+    return;
+
+  int end = src.find_last_not_of(spacestr);
+  dst = src.substr(start, end - start + 1);
+}
+
+int RGWUserCaps::get_cap(const string& cap, string& type, uint32_t *pperm)
+{
+  int pos = cap.find('=');
+  if (pos >= 0) {
+    trim_whitespace(cap.substr(0, pos), type);
+  }
+
+  if (type.size() == 0)
+    return -EINVAL;
+
+  string cap_perm;
+  uint32_t perm = 0;
+  if (pos < (int)cap.size() - 1) {
+    cap_perm = cap.substr(pos + 1);
+    int r = parse_cap_perm(cap_perm, &perm);
+    if (r < 0)
+      return r;
+  }
+
+  *pperm = perm;
+
+  return 0;
+}
+
+int RGWUserCaps::add_cap(const string& cap)
+{
+  uint32_t perm;
+  string type;
+
+  int r = get_cap(cap, type, &perm);
+  if (r < 0)
+    return r;
+
+  caps[type] |= perm;
+
+  return 0;
+}
+
+int RGWUserCaps::remove_cap(const string& cap)
+{
+  uint32_t perm;
+  string type;
+
+  int r = get_cap(cap, type, &perm);
+  if (r < 0)
+    return r;
+
+  map<string, uint32_t>::iterator iter = caps.find(type);
+  if (iter == caps.end())
+    return 0;
+
+  uint32_t& old_perm = iter->second;
+  old_perm &= ~perm;
+  if (!old_perm)
+    caps.erase(iter);
+
+  return 0;
+}
+
+int RGWUserCaps::add_from_string(const string& str)
+{
+  int start = 0;
+  int end;
+  do {
+    end = str.find(';', start);
+    if (end < 0)
+      end = str.size();
+
+    int r = add_cap(str.substr(start, end - start));
+    if (r < 0)
+      return r;
+
+    start = end + 1;
+  } while (start < (int)str.size());
+
+  return 0;
+}
+
+int RGWUserCaps::remove_from_string(const string& str)
+{
+  int start = 0;
+  int end;
+  do {
+    end = str.find(';', start);
+    if (end < 0)
+      end = str.size();
+
+    int r = remove_cap(str.substr(start, end - start));
+    if (r < 0)
+      return r;
+
+    start = end + 1;
+  } while (start < (int)str.size());
+
+  return 0;
+}
+
+void RGWUserCaps::dump(Formatter *f) const
+{
+  f->open_array_section("caps");
+  map<string, uint32_t>::const_iterator iter;
+  for (iter = caps.begin(); iter != caps.end(); ++iter)
+  {
+    f->open_object_section("cap");
+    f->dump_string("type", iter->first);
+    uint32_t perm = iter->second;
+    string perm_str;
+    for (int i=0; cap_names[i].type_name; i++) {
+      if ((perm & cap_names[i].perm) == cap_names[i].perm) {
+       if (perm_str.size())
+         perm_str.append(", ");
+
+       perm_str.append(cap_names[i].type_name);
+       perm &= ~cap_names[i].perm;
+      }
+    }
+    if (perm_str.empty())
+      perm_str = "<none>";
+
+    f->dump_string("perm", perm_str);
+    f->close_section();
+  }
+
+  f->close_section();
+}
+
+bool RGWUserCaps::check_cap(const string& cap, uint32_t perm)
+{
+  map<string, uint32_t>::iterator iter = caps.find(cap);
+  if (iter == caps.end())
+    return false;
+
+  return (iter->second & perm) == perm;
+}
+
index 43819bcb0b540c5b5c50f70fc3c1517ffe62d392..3535b985ff6022fd0ccdd1de6ea474b03e06f183 100644 (file)
@@ -65,6 +65,10 @@ using ceph::crypto::MD5;
 #define RGW_FORMAT_XML          1
 #define RGW_FORMAT_JSON         2
 
+#define RGW_CAP_READ            0x1
+#define RGW_CAP_WRITE           0x2
+#define RGW_CAP_ALL             (RGW_CAP_READ | RGW_CAP_WRITE)
+
 #define RGW_REST_SWIFT          0x1
 #define RGW_REST_SWIFT_AUTH     0x2
 
@@ -309,6 +313,33 @@ struct RGWSubUser {
 };
 WRITE_CLASS_ENCODER(RGWSubUser);
 
+class RGWUserCaps
+{
+  map<string, uint32_t> caps;
+
+  int get_cap(const string& cap, string& type, uint32_t *perm);
+  int parse_cap_perm(const string& str, uint32_t *perm);
+  int add_cap(const string& cap);
+  int remove_cap(const string& cap);
+public:
+  int add_from_string(const string& str);
+  int remove_from_string(const string& str);
+
+  void encode(bufferlist& bl) const {
+     ENCODE_START(1, 1, bl);
+     ::encode(caps, bl);
+     ENCODE_FINISH(bl);
+  }
+  void decode(bufferlist::iterator& bl) {
+     DECODE_START(1, bl);
+     ::decode(caps, bl);
+     DECODE_FINISH(bl);
+  }
+  bool check_cap(const string& cap, uint32_t perm);
+  void dump(Formatter *f) const;
+};
+WRITE_CLASS_ENCODER(RGWUserCaps);
+
 
 struct RGWUserInfo
 {
@@ -321,11 +352,12 @@ struct RGWUserInfo
   map<string, RGWSubUser> subusers;
   __u8 suspended;
   uint32_t max_buckets;
+  RGWUserCaps caps;
 
   RGWUserInfo() : auid(0), suspended(0), max_buckets(RGW_DEFAULT_MAX_BUCKETS) {}
 
   void encode(bufferlist& bl) const {
-     ENCODE_START(10, 9, bl);
+     ENCODE_START(11, 9, bl);
      ::encode(auid, bl);
      string access_key;
      string secret_key;
@@ -355,10 +387,11 @@ struct RGWUserInfo
      ::encode(suspended, bl);
      ::encode(swift_keys, bl);
      ::encode(max_buckets, bl);
+     ::encode(caps, bl);
      ENCODE_FINISH(bl);
   }
   void decode(bufferlist::iterator& bl) {
-     DECODE_START_LEGACY_COMPAT_LEN_32(10, 9, 9, bl);
+     DECODE_START_LEGACY_COMPAT_LEN_32(11, 9, 9, bl);
      if (struct_v >= 2) ::decode(auid, bl);
      else auid = CEPH_AUTH_UID_DEFAULT;
      string access_key;
@@ -397,6 +430,9 @@ struct RGWUserInfo
     } else {
       max_buckets = RGW_DEFAULT_MAX_BUCKETS;
     }
+    if (struct_v >= 11) {
+      ::decode(caps, bl);
+    }
     DECODE_FINISH(bl);
   }
   void dump(Formatter *f) const;
index c17e5056713e0d8b6aa5c00147bbaa7079696580..cf4c9690680577390995d569fee434163654d4f7 100644 (file)
@@ -7,6 +7,8 @@
     user rm                    remove user
     user suspend               suspend a user
     user enable                reenable user after suspension
+    caps add                   add user capabilities
+    caps rm                    remove user capabilities
     subuser create             create a new subuser
     subuser modify             modify subuser
     subuser rm                 remove subuser
@@ -65,6 +67,7 @@
      --skip-zero-entries       log show only dumps entries that don't have zero value
                                in one of the numeric field
      --categories=<list>       comma separated list of categories, used in usage show
+     --caps=<caps>             list of caps (e.g., "usage=read, write; user=read"
      --yes-i-really-mean-it    required for certain operations
   
   <date> := "YYYY-MM-DD[ hh:mm:ss]"