// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #include "common/ceph_json.h" #include "rgw_op.h" #include "rgw_user.h" #include "rgw_rest_user.h" #include "include/str_list.h" #include "include/assert.h" #define dout_subsys ceph_subsys_rgw class RGWOp_User_Info : public RGWRESTOp { public: RGWOp_User_Info() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_READ); } void execute() override; const string name() override { return "get_user_info"; } }; void RGWOp_User_Info::execute() { RGWUserAdminOpState op_state; std::string uid_str; bool fetch_stats; bool sync_stats; RESTArgs::get_string(s, "uid", uid_str, &uid_str); // if uid was not supplied in rest argument, error out now, otherwise we'll // end up initializing anonymous user, for which keys.init will eventually // return -EACESS if (uid_str.empty()){ http_ret=-EINVAL; return; } rgw_user uid(uid_str); RESTArgs::get_bool(s, "stats", false, &fetch_stats); RESTArgs::get_bool(s, "sync", false, &sync_stats); op_state.set_user_id(uid); op_state.set_fetch_stats(fetch_stats); op_state.set_sync_stats(sync_stats); http_ret = RGWUserAdminOp_User::info(store, op_state, flusher); } class RGWOp_User_Create : public RGWRESTOp { public: RGWOp_User_Create() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "create_user"; } }; void RGWOp_User_Create::execute() { std::string uid_str; std::string display_name; std::string email; std::string access_key; std::string secret_key; std::string key_type_str; std::string caps; std::string tenant_name; bool gen_key; bool suspended; bool system; bool exclusive; int32_t max_buckets; int32_t default_max_buckets = s->cct->_conf->rgw_user_max_buckets; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); 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_string(s, "tenant", tenant_name, &tenant_name); RESTArgs::get_bool(s, "generate-key", true, &gen_key); RESTArgs::get_bool(s, "suspended", false, &suspended); RESTArgs::get_int32(s, "max-buckets", default_max_buckets, &max_buckets); RESTArgs::get_bool(s, "system", false, &system); RESTArgs::get_bool(s, "exclusive", false, &exclusive); if (!s->user->system && system) { ldout(s->cct, 0) << "cannot set system flag by non-system user" << dendl; http_ret = -EINVAL; return; } if (!tenant_name.empty()) { uid.tenant = tenant_name; } // TODO: validate required args are passed in. (for eg. uid and display_name here) op_state.set_user_id(uid); op_state.set_display_name(display_name); op_state.set_user_email(email); op_state.set_caps(caps); op_state.set_access_key(access_key); op_state.set_secret_key(secret_key); if (!key_type_str.empty()) { int32_t key_type = KEY_TYPE_UNDEFINED; 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 != default_max_buckets) op_state.set_max_buckets(max_buckets); if (s->info.args.exists("suspended")) op_state.set_suspension(suspended); if (s->info.args.exists("system")) op_state.set_system(system); if (s->info.args.exists("exclusive")) op_state.set_exclusive(exclusive); if (gen_key) op_state.set_generate_key(); RGWQuotaInfo bucket_quota; RGWQuotaInfo user_quota; if (s->cct->_conf->rgw_bucket_default_quota_max_objects >= 0) { bucket_quota.max_objects = s->cct->_conf->rgw_bucket_default_quota_max_objects; bucket_quota.enabled = true; } if (s->cct->_conf->rgw_bucket_default_quota_max_size >= 0) { bucket_quota.max_size = s->cct->_conf->rgw_bucket_default_quota_max_size; bucket_quota.enabled = true; } if (s->cct->_conf->rgw_user_default_quota_max_objects >= 0) { user_quota.max_objects = s->cct->_conf->rgw_user_default_quota_max_objects; user_quota.enabled = true; } if (s->cct->_conf->rgw_user_default_quota_max_size >= 0) { user_quota.max_size = s->cct->_conf->rgw_user_default_quota_max_size; user_quota.enabled = true; } if (bucket_quota.enabled) { op_state.set_bucket_quota(bucket_quota); } if (user_quota.enabled) { op_state.set_user_quota(user_quota); } http_ret = RGWUserAdminOp_User::create(store, op_state, flusher); } class RGWOp_User_Modify : public RGWRESTOp { public: RGWOp_User_Modify() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "modify_user"; } }; void RGWOp_User_Modify::execute() { std::string uid_str; 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; bool system; bool email_set; bool quota_set; int32_t max_buckets; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); RESTArgs::get_string(s, "display-name", display_name, &display_name); RESTArgs::get_string(s, "email", email, &email, &email_set); 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_int32(s, "max-buckets", RGW_DEFAULT_MAX_BUCKETS, &max_buckets, "a_set); RESTArgs::get_string(s, "key-type", key_type_str, &key_type_str); RESTArgs::get_bool(s, "system", false, &system); if (!s->user->system && system) { ldout(s->cct, 0) << "cannot set system flag by non-system user" << dendl; http_ret = -EINVAL; return; } op_state.set_user_id(uid); op_state.set_display_name(display_name); if (email_set) op_state.set_user_email(email); op_state.set_caps(caps); op_state.set_access_key(access_key); op_state.set_secret_key(secret_key); if (quota_set) op_state.set_max_buckets(max_buckets); if (gen_key) op_state.set_generate_key(); if (!key_type_str.empty()) { int32_t key_type = KEY_TYPE_UNDEFINED; 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->info.args.exists("suspended")) op_state.set_suspension(suspended); if (s->info.args.exists("system")) op_state.set_system(system); http_ret = RGWUserAdminOp_User::modify(store, op_state, flusher); } class RGWOp_User_Remove : public RGWRESTOp { public: RGWOp_User_Remove() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "remove_user"; } }; void RGWOp_User_Remove::execute() { std::string uid_str; bool purge_data; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "create_subuser"; } }; void RGWOp_Subuser_Create::execute() { std::string uid_str; std::string subuser; std::string secret_key; std::string access_key; std::string perm_str; std::string key_type_str; bool gen_subuser = false; // FIXME placeholder bool gen_secret; bool gen_access; uint32_t perm_mask = 0; int32_t key_type = KEY_TYPE_SWIFT; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); 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, "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); RESTArgs::get_bool(s, "gen-access-key", false, &gen_access); perm_mask = rgw_str_to_perm(perm_str.c_str()); op_state.set_perm(perm_mask); op_state.set_user_id(uid); op_state.set_subuser(subuser); op_state.set_access_key(access_key); op_state.set_secret_key(secret_key); op_state.set_generate_subuser(gen_subuser); if (gen_access) op_state.set_gen_access(); 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "modify_subuser"; } }; void RGWOp_Subuser_Modify::execute() { std::string uid_str; 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_str, &uid_str); rgw_user uid(uid_str); 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()); op_state.set_perm(perm_mask); op_state.set_user_id(uid); 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 (!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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "remove_subuser"; } }; void RGWOp_Subuser_Remove::execute() { std::string uid_str; std::string subuser; bool purge_keys; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); RESTArgs::get_string(s, "subuser", subuser, &subuser); RESTArgs::get_bool(s, "purge-keys", true, &purge_keys); op_state.set_user_id(uid); 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "create_access_key"; } }; void RGWOp_Key_Create::execute() { std::string uid_str; std::string subuser; std::string access_key; std::string secret_key; std::string key_type_str; bool gen_key; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); 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); op_state.set_user_id(uid); op_state.set_subuser(subuser); op_state.set_access_key(access_key); op_state.set_secret_key(secret_key); if (gen_key) op_state.set_generate_key(); if (!key_type_str.empty()) { int32_t key_type = KEY_TYPE_UNDEFINED; 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "remove_access_key"; } }; void RGWOp_Key_Remove::execute() { std::string uid_str; std::string subuser; std::string access_key; std::string key_type_str; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); 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); op_state.set_user_id(uid); op_state.set_subuser(subuser); op_state.set_access_key(access_key); if (!key_type_str.empty()) { int32_t key_type = KEY_TYPE_UNDEFINED; 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "add_user_caps"; } }; void RGWOp_Caps_Add::execute() { std::string uid_str; std::string caps; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); RESTArgs::get_string(s, "user-caps", caps, &caps); op_state.set_user_id(uid); 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) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "remove_user_caps"; } }; void RGWOp_Caps_Remove::execute() { std::string uid_str; std::string caps; RGWUserAdminOpState op_state; RESTArgs::get_string(s, "uid", uid_str, &uid_str); rgw_user uid(uid_str); RESTArgs::get_string(s, "user-caps", caps, &caps); op_state.set_user_id(uid); op_state.set_caps(caps); http_ret = RGWUserAdminOp_Caps::remove(store, op_state, flusher); } struct UserQuotas { RGWQuotaInfo bucket_quota; RGWQuotaInfo user_quota; UserQuotas() {} explicit UserQuotas(RGWUserInfo& info) : bucket_quota(info.bucket_quota), user_quota(info.user_quota) {} void dump(Formatter *f) const { encode_json("bucket_quota", bucket_quota, f); encode_json("user_quota", user_quota, f); } void decode_json(JSONObj *obj) { JSONDecoder::decode_json("bucket_quota", bucket_quota, obj); JSONDecoder::decode_json("user_quota", user_quota, obj); } }; class RGWOp_Quota_Info : public RGWRESTOp { public: RGWOp_Quota_Info() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_READ); } void execute() override; const string name() override { return "get_quota_info"; } }; void RGWOp_Quota_Info::execute() { RGWUserAdminOpState op_state; std::string uid_str; std::string quota_type; RESTArgs::get_string(s, "uid", uid_str, &uid_str); RESTArgs::get_string(s, "quota-type", quota_type, "a_type); if (uid_str.empty()) { http_ret = -EINVAL; return; } rgw_user uid(uid_str); bool show_all = quota_type.empty(); bool show_bucket = show_all || (quota_type == "bucket"); bool show_user = show_all || (quota_type == "user"); if (!(show_all || show_bucket || show_user)) { http_ret = -EINVAL; return; } op_state.set_user_id(uid); RGWUser user; http_ret = user.init(store, op_state); if (http_ret < 0) return; if (!op_state.has_existing_user()) { http_ret = -ERR_NO_SUCH_USER; return; } RGWUserInfo info; string err_msg; http_ret = user.info(info, &err_msg); if (http_ret < 0) return; flusher.start(0); if (show_all) { UserQuotas quotas(info); encode_json("quota", quotas, s->formatter); } else if (show_user) { encode_json("user_quota", info.user_quota, s->formatter); } else { encode_json("bucket_quota", info.bucket_quota, s->formatter); } flusher.flush(); } class RGWOp_Quota_Set : public RGWRESTOp { public: RGWOp_Quota_Set() {} int check_caps(RGWUserCaps& caps) override { return caps.check_cap("users", RGW_CAP_WRITE); } void execute() override; const string name() override { return "set_quota_info"; } }; /** * set quota * * two different ways to set the quota info: as json struct in the message body or via http params. * * as json: * * PUT /admin/user?uid=["a-type=] * * whereas quota-type is optional and is either user, or bucket * * if quota-type is not specified then we expect to get a structure that contains both quotas, * otherwise we'll only get the relevant configuration. * * E.g., if quota type not specified: * { * "user_quota" : { * "max_size_kb" : 4096, * "max_objects" : -1, * "enabled" : false * }, * "bucket_quota" : { * "max_size_kb" : 1024, * "max_objects" : -1, * "enabled" : true * } * } * * * or if quota type is specified: * { * "max_size_kb" : 4096, * "max_objects" : -1, * "enabled" : false * } * * Another option is not to pass any body and set the following http params: * * * max-size-kb= * max-objects= * enabled[={true,false}] * * all params are optionals and default to the current settings. With this type of configuration the * quota-type param is mandatory. * */ void RGWOp_Quota_Set::execute() { RGWUserAdminOpState op_state; std::string uid_str; std::string quota_type; RESTArgs::get_string(s, "uid", uid_str, &uid_str); RESTArgs::get_string(s, "quota-type", quota_type, "a_type); if (uid_str.empty()) { http_ret = -EINVAL; return; } rgw_user uid(uid_str); bool set_all = quota_type.empty(); bool set_bucket = set_all || (quota_type == "bucket"); bool set_user = set_all || (quota_type == "user"); if (!(set_all || set_bucket || set_user)) { ldout(store->ctx(), 20) << "invalid quota type" << dendl; http_ret = -EINVAL; return; } bool use_http_params; if (s->content_length > 0) { use_http_params = false; } else { const char *encoding = s->info.env->get("HTTP_TRANSFER_ENCODING"); use_http_params = (!encoding || strcmp(encoding, "chunked") != 0); } if (use_http_params && set_all) { ldout(store->ctx(), 20) << "quota type was not specified, can't set all quotas via http headers" << dendl; http_ret = -EINVAL; return; } op_state.set_user_id(uid); RGWUser user; http_ret = user.init(store, op_state); if (http_ret < 0) { ldout(store->ctx(), 20) << "failed initializing user info: " << http_ret << dendl; return; } if (!op_state.has_existing_user()) { http_ret = -ERR_NO_SUCH_USER; return; } #define QUOTA_INPUT_MAX_LEN 1024 if (set_all) { UserQuotas quotas; if ((http_ret = rgw_rest_get_json_input(store->ctx(), s, quotas, QUOTA_INPUT_MAX_LEN, NULL)) < 0) { ldout(store->ctx(), 20) << "failed to retrieve input" << dendl; return; } op_state.set_user_quota(quotas.user_quota); op_state.set_bucket_quota(quotas.bucket_quota); } else { RGWQuotaInfo quota; if (!use_http_params) { bool empty; http_ret = rgw_rest_get_json_input(store->ctx(), s, quota, QUOTA_INPUT_MAX_LEN, &empty); if (http_ret < 0) { ldout(store->ctx(), 20) << "failed to retrieve input" << dendl; if (!empty) return; /* was probably chunked input, but no content provided, configure via http params */ use_http_params = true; } } if (use_http_params) { RGWUserInfo info; string err_msg; http_ret = user.info(info, &err_msg); if (http_ret < 0) { ldout(store->ctx(), 20) << "failed to get user info: " << http_ret << dendl; return; } RGWQuotaInfo *old_quota; if (set_user) { old_quota = &info.user_quota; } else { old_quota = &info.bucket_quota; } RESTArgs::get_int64(s, "max-objects", old_quota->max_objects, "a.max_objects); RESTArgs::get_int64(s, "max-size", old_quota->max_size, "a.max_size); int64_t max_size_kb; bool has_max_size_kb = false; RESTArgs::get_int64(s, "max-size-kb", 0, &max_size_kb, &has_max_size_kb); if (has_max_size_kb) { quota.max_size = max_size_kb * 1024; } RESTArgs::get_bool(s, "enabled", old_quota->enabled, "a.enabled); } if (set_user) { op_state.set_user_quota(quota); } else { op_state.set_bucket_quota(quota); } } string err; http_ret = user.modify(op_state, &err); if (http_ret < 0) { ldout(store->ctx(), 20) << "failed updating user info: " << http_ret << ": " << err << dendl; return; } } RGWOp *RGWHandler_User::op_get() { if (s->info.args.sub_resource_exists("quota")) return new RGWOp_Quota_Info; return new RGWOp_User_Info; } RGWOp *RGWHandler_User::op_put() { if (s->info.args.sub_resource_exists("subuser")) return new RGWOp_Subuser_Create; if (s->info.args.sub_resource_exists("key")) return new RGWOp_Key_Create; if (s->info.args.sub_resource_exists("caps")) return new RGWOp_Caps_Add; if (s->info.args.sub_resource_exists("quota")) return new RGWOp_Quota_Set; return new RGWOp_User_Create; } RGWOp *RGWHandler_User::op_post() { if (s->info.args.sub_resource_exists("subuser")) return new RGWOp_Subuser_Modify; return new RGWOp_User_Modify; } RGWOp *RGWHandler_User::op_delete() { if (s->info.args.sub_resource_exists("subuser")) return new RGWOp_Subuser_Remove; if (s->info.args.sub_resource_exists("key")) return new RGWOp_Key_Remove; if (s->info.args.sub_resource_exists("caps")) return new RGWOp_Caps_Remove; return new RGWOp_User_Remove; }