From d22aa6c91d236af568b81a971aa4e9f24fc2e396 Mon Sep 17 00:00:00 2001 From: Yehuda Sadeh Date: Tue, 25 Sep 2012 10:55:19 -0700 Subject: [PATCH] rgw: add user caps 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 --- src/rgw/rgw_admin.cc | 33 +++++- src/rgw/rgw_common.cc | 174 ++++++++++++++++++++++++++++++ src/rgw/rgw_common.h | 40 ++++++- src/test/cli/radosgw-admin/help.t | 3 + 4 files changed, 247 insertions(+), 3 deletions(-) diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index b4d6fe33a98e6..d67598de61506 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -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= comma separated list of categories, used in usage show\n"; + cerr << " --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 << " := \"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 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; diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 07b181a9ba128..7e1a3d9680757 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -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 @@ -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 strs; + get_str_list(str, strs); + list::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::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::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 = ""; + + f->dump_string("perm", perm_str); + f->close_section(); + } + + f->close_section(); +} + +bool RGWUserCaps::check_cap(const string& cap, uint32_t perm) +{ + map::iterator iter = caps.find(cap); + if (iter == caps.end()) + return false; + + return (iter->second & perm) == perm; +} + diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 43819bcb0b540..3535b985ff602 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -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 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 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; diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index c17e5056713e0..cf4c969068057 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -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= comma separated list of categories, used in usage show + --caps= list of caps (e.g., "usage=read, write; user=read" --yes-i-really-mean-it required for certain operations := "YYYY-MM-DD[ hh:mm:ss]" -- 2.39.5