From 1ae181da179f51fe09b3601961c69c48e4afbf38 Mon Sep 17 00:00:00 2001 From: Casey Bodley Date: Mon, 5 Feb 2024 13:57:16 -0500 Subject: [PATCH] radosgw-admin: add commands for managed policy Signed-off-by: Casey Bodley (cherry picked from commit 93522666a0d0ea6f6840107254344fcdf3d89dfc) --- src/rgw/rgw_admin.cc | 245 +++++++++++++++++++++++++++++- src/test/cli/radosgw-admin/help.t | 7 + 2 files changed, 249 insertions(+), 3 deletions(-) diff --git a/src/rgw/rgw_admin.cc b/src/rgw/rgw_admin.cc index 0ad5a65b429ae..420323f8328c1 100644 --- a/src/rgw/rgw_admin.cc +++ b/src/rgw/rgw_admin.cc @@ -144,6 +144,9 @@ void usage() cout << " user check check user info\n"; cout << " user stats show user stats as accounted by quota subsystem\n"; cout << " user list list users\n"; + cout << " user policy attach attach a managed policy\n"; + cout << " user policy detach detach a managed policy\n"; + cout << " user policy list attached list attached managed policies\n"; cout << " caps add add user capabilities\n"; cout << " caps rm remove user capabilities\n"; cout << " subuser create create a new subuser\n" ; @@ -301,6 +304,9 @@ void usage() cout << " role-policy list list policies attached to a role\n"; cout << " role-policy get get the specified inline policy document embedded with the given role\n"; cout << " role-policy delete remove policy attached to a role\n"; + cout << " role policy attach attach a managed policy\n"; + cout << " role policy detach detach a managed policy\n"; + cout << " role policy list attached list attached managed policies\n"; cout << " role update update max_session_duration of a role\n"; cout << " reshard add schedule a resharding of a bucket\n"; cout << " reshard list list all bucket resharding or scheduled to be resharded\n"; @@ -485,6 +491,7 @@ void usage() cout << " --policy-doc permission policy document\n"; cout << " --path-prefix path prefix for filtering roles\n"; cout << " --description Role description\n"; + cout << " --policy-arn ARN of a managed policy\n"; cout << "\nMFA options:\n"; cout << " --totp-serial a string that represents the ID of a TOTP token\n"; cout << " --totp-seed the secret seed that is used to calculate the TOTP\n"; @@ -656,6 +663,9 @@ enum class OPT { USER_CHECK, USER_STATS, USER_LIST, + USER_POLICY_ATTACH, + USER_POLICY_DETACH, + USER_POLICY_LIST_ATTACHED, SUBUSER_CREATE, SUBUSER_MODIFY, SUBUSER_RM, @@ -830,6 +840,9 @@ enum class OPT { ROLE_POLICY_LIST, ROLE_POLICY_GET, ROLE_POLICY_DELETE, + ROLE_POLICY_ATTACH, + ROLE_POLICY_DETACH, + ROLE_POLICY_LIST_ATTACHED, ROLE_UPDATE, RESHARD_ADD, RESHARD_LIST, @@ -881,6 +894,9 @@ static SimpleCmd::Commands all_cmds = { { "user check", OPT::USER_CHECK }, { "user stats", OPT::USER_STATS }, { "user list", OPT::USER_LIST }, + { "user policy attach", OPT::USER_POLICY_ATTACH }, + { "user policy detach", OPT::USER_POLICY_DETACH }, + { "user policy list attached", OPT::USER_POLICY_LIST_ATTACHED }, { "subuser create", OPT::SUBUSER_CREATE }, { "subuser modify", OPT::SUBUSER_MODIFY }, { "subuser rm", OPT::SUBUSER_RM }, @@ -1069,6 +1085,9 @@ static SimpleCmd::Commands all_cmds = { { "role-policy get", OPT::ROLE_POLICY_GET }, { "role policy delete", OPT::ROLE_POLICY_DELETE }, { "role-policy delete", OPT::ROLE_POLICY_DELETE }, + { "role policy attach", OPT::ROLE_POLICY_ATTACH }, + { "role policy detach", OPT::ROLE_POLICY_DETACH }, + { "role policy list attached", OPT::ROLE_POLICY_LIST_ATTACHED }, { "role update", OPT::ROLE_UPDATE }, { "reshard bucket", OPT::BUCKET_RESHARD }, { "reshard add", OPT::RESHARD_ADD }, @@ -1151,7 +1170,7 @@ static void show_perm_policy(string perm_policy, Formatter* formatter) formatter->flush(cout); } -static void show_policy_names(std::vector policy_names, Formatter* formatter) +static void show_policy_names(const std::vector& policy_names, Formatter* formatter) { formatter->open_array_section("PolicyNames"); for (const auto& it : policy_names) { @@ -1161,6 +1180,16 @@ static void show_policy_names(std::vector policy_names, Formatter* forma formatter->flush(cout); } +static void show_policy_arns(const boost::container::flat_set& arns, + Formatter* formatter) +{ + formatter->open_array_section("AttachedPolicies"); + for (const auto& arn : arns) { + formatter->dump_string("PolicyArn", arn); + } + formatter->close_section(); +} + static void show_reshard_status( const list& status, Formatter *formatter) { @@ -3352,6 +3381,7 @@ int main(int argc, const char **argv) std::string api_name; std::string role_name, path, assume_role_doc, policy_name, perm_policy_doc, path_prefix, max_session_duration; std::string description; + std::string policy_arn; std::string redirect_zone; bool redirect_zone_set = false; list endpoints; @@ -4005,6 +4035,8 @@ int main(int argc, const char **argv) perm_policy_doc = val; } else if (ceph_argparse_witharg(args, i, &val, "--path-prefix", (char*)NULL)) { path_prefix = val; + } else if (ceph_argparse_witharg(args, i, &val, "--policy-arn", (char*)NULL)) { + policy_arn = val; } else if (ceph_argparse_witharg(args, i, &val, "--max-session-duration", (char*)NULL)) { max_session_duration = val; } else if (ceph_argparse_witharg(args, i, &val, "--description", (char*)NULL)) { @@ -4220,6 +4252,8 @@ int main(int argc, const char **argv) std::set readonly_ops_list = { OPT::USER_INFO, OPT::USER_STATS, + OPT::USER_LIST, + OPT::USER_POLICY_LIST_ATTACHED, OPT::ACCOUNT_GET, OPT::ACCOUNT_STATS, OPT::ACCOUNT_LIST, @@ -4281,6 +4315,7 @@ int main(int argc, const char **argv) OPT::ROLE_LIST, OPT::ROLE_POLICY_LIST, OPT::ROLE_POLICY_GET, + OPT::ROLE_POLICY_LIST_ATTACHED, OPT::RESHARD_LIST, OPT::RESHARD_STATUS, OPT::PUBSUB_TOPIC_LIST, @@ -4374,6 +4409,9 @@ int main(int argc, const char **argv) && opt_cmd != OPT::ROLE_POLICY_LIST && opt_cmd != OPT::ROLE_POLICY_GET && opt_cmd != OPT::ROLE_POLICY_DELETE + && opt_cmd != OPT::ROLE_POLICY_ATTACH + && opt_cmd != OPT::ROLE_POLICY_DETACH + && opt_cmd != OPT::ROLE_POLICY_LIST_ATTACHED && opt_cmd != OPT::ROLE_UPDATE && opt_cmd != OPT::RESHARD_ADD && opt_cmd != OPT::RESHARD_CANCEL @@ -6425,7 +6463,9 @@ int main(int argc, const char **argv) OPT::MFA_REMOVE, OPT::MFA_RESYNC, OPT::CAPS_ADD, OPT::CAPS_RM, OPT::ROLE_CREATE, OPT::ROLE_DELETE, - OPT::ROLE_POLICY_PUT, OPT::ROLE_POLICY_DELETE}; + OPT::ROLE_POLICY_PUT, OPT::ROLE_POLICY_DELETE, + OPT::ROLE_POLICY_ATTACH, OPT::ROLE_POLICY_DETACH, + OPT::USER_POLICY_ATTACH, OPT::USER_POLICY_DETACH}; bool print_warning_message = (non_master_ops_list.find(opt_cmd) != non_master_ops_list.end() && non_master_cmd); @@ -7020,7 +7060,97 @@ int main(int argc, const char **argv) cout << "Policy: " << policy_name << " successfully deleted for role: " << role_name << std::endl; return 0; - } + } + case OPT::ROLE_POLICY_ATTACH: + { + if (role_name.empty()) { + cerr << "role name is empty" << std::endl; + return EINVAL; + } + if (policy_arn.empty()) { + cerr << "policy arn is empty" << std::endl; + return EINVAL; + } + try { + if (!rgw::IAM::get_managed_policy(g_ceph_context, policy_arn)) { + cerr << "unrecognized policy arn " << policy_arn << std::endl; + return ENOENT; + } + } catch (rgw::IAM::PolicyParseException& e) { + cerr << "failed to parse managed policy: " << e.what() << std::endl; + return EINVAL; + } + + std::unique_ptr role = driver->get_role(role_name, tenant, account_id); + ret = role->get(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + if (role->get_info().account_id.empty()) { + std::cerr << "Managed policies are only supported for account roles" << std::endl; + return EINVAL; + } + + auto &policies = role->get_info().managed_policies; + const bool inserted = policies.arns.insert(policy_arn).second; + if (!inserted) { + cout << "That managed policy is already attached." << std::endl; + return EEXIST; + } + ret = role->update(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + cout << "Managed policy attached successfully" << std::endl; + return 0; + } + case OPT::ROLE_POLICY_DETACH: + { + if (role_name.empty()) { + cerr << "role name is empty" << std::endl; + return EINVAL; + } + if (policy_arn.empty()) { + cerr << "policy arn is empty" << std::endl; + return EINVAL; + } + + std::unique_ptr role = driver->get_role(role_name, tenant, account_id); + ret = role->get(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + // insert the policy arn. if it's already there, just return success + auto &policies = role->get_info().managed_policies; + auto i = policies.arns.find(policy_arn); + if (i == policies.arns.end()) { + cout << "That managed policy is not attached." << std::endl; + return ENOENT; + } + policies.arns.erase(i); + + ret = role->update(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + cout << "Managed policy detached successfully" << std::endl; + return 0; + } + case OPT::ROLE_POLICY_LIST_ATTACHED: + { + if (role_name.empty()) { + cerr << "ERROR: Role name is empty" << std::endl; + return EINVAL; + } + std::unique_ptr role = driver->get_role(role_name, tenant, account_id); + ret = role->get(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + show_policy_arns(role->get_info().managed_policies.arns, formatter.get()); + formatter->flush(cout); + return 0; + } case OPT::ROLE_UPDATE: { if (role_name.empty()) { @@ -8952,6 +9082,115 @@ next: formatter->flush(cout); } + if (opt_cmd == OPT::USER_POLICY_ATTACH) { + if (rgw::sal::User::empty(user)) { + cerr << "ERROR: uid not specified" << std::endl; + return EINVAL; + } + if (policy_arn.empty()) { + cerr << "policy arn is empty" << std::endl; + return EINVAL; + } + ret = user->load_user(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + if (user->get_info().account_id.empty()) { + std::cerr << "Managed policies are only supported for account users" << std::endl; + return EINVAL; + } + + try { + if (!rgw::IAM::get_managed_policy(g_ceph_context, policy_arn)) { + cerr << "unrecognized policy arn " << policy_arn << std::endl; + return ENOENT; + } + } catch (rgw::IAM::PolicyParseException& e) { + cerr << "failed to parse managed policy: " << e.what() << std::endl; + return EINVAL; + } + + rgw::IAM::ManagedPolicies policies; + auto& attrs = user->get_attrs(); + if (auto it = attrs.find(RGW_ATTR_MANAGED_POLICY); it != attrs.end()) { + decode(policies, it->second); + } + const bool inserted = policies.arns.insert(policy_arn).second; + if (!inserted) { + cout << "That managed policy is already attached." << std::endl; + return EEXIST; + } + + bufferlist in_bl; + encode(policies, in_bl); + attrs[RGW_ATTR_MANAGED_POLICY] = in_bl; + + ret = user->store_user(dpp(), null_yield, false); + if (ret < 0) { + return -ret; + } + cout << "Managed policy attached successfully" << std::endl; + return 0; + } + if (opt_cmd == OPT::USER_POLICY_DETACH) { + if (rgw::sal::User::empty(user)) { + cerr << "ERROR: uid not specified" << std::endl; + return EINVAL; + } + if (policy_arn.empty()) { + cerr << "policy arn is empty" << std::endl; + return EINVAL; + } + ret = user->load_user(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + + rgw::IAM::ManagedPolicies policies; + auto& attrs = user->get_attrs(); + if (auto it = attrs.find(RGW_ATTR_MANAGED_POLICY); it != attrs.end()) { + decode(policies, it->second); + } + + auto i = policies.arns.find(policy_arn); + if (i == policies.arns.end()) { + cout << "That managed policy is not attached." << std::endl; + return ENOENT; + } + policies.arns.erase(i); + + bufferlist in_bl; + encode(policies, in_bl); + attrs[RGW_ATTR_MANAGED_POLICY] = in_bl; + + ret = user->store_user(dpp(), null_yield, false); + if (ret < 0) { + return -ret; + } + cout << "Managed policy detached successfully" << std::endl; + return 0; + } + if (opt_cmd == OPT::USER_POLICY_LIST_ATTACHED) { + if (rgw::sal::User::empty(user)) { + cerr << "ERROR: uid not specified" << std::endl; + return -EINVAL; + } + ret = user->load_user(dpp(), null_yield); + if (ret < 0) { + return -ret; + } + + rgw::IAM::ManagedPolicies policies; + auto& attrs = user->get_attrs(); + if (auto it = attrs.find(RGW_ATTR_MANAGED_POLICY); it != attrs.end()) { + decode(policies, it->second); + } + + show_policy_arns(policies.arns, formatter.get()); + formatter->flush(cout); + return 0; + } + if (opt_cmd == OPT::METADATA_GET) { int ret = static_cast(driver)->ctl()->meta.mgr->get(metadata_key, formatter.get(), null_yield, dpp()); if (ret < 0) { diff --git a/src/test/cli/radosgw-admin/help.t b/src/test/cli/radosgw-admin/help.t index 46fb15c8146f4..af29e827591dd 100644 --- a/src/test/cli/radosgw-admin/help.t +++ b/src/test/cli/radosgw-admin/help.t @@ -11,6 +11,9 @@ user check check user info user stats show user stats as accounted by quota subsystem user list list users + user policy attach attach a managed policy + user policy detach detach a managed policy + user policy list attached list attached managed policies caps add add user capabilities caps rm remove user capabilities subuser create create a new subuser @@ -168,6 +171,9 @@ role-policy list list policies attached to a role role-policy get get the specified inline policy document embedded with the given role role-policy delete remove policy attached to a role + role policy attach attach a managed policy + role policy detach detach a managed policy + role policy list attached list attached managed policies role update update max_session_duration of a role reshard add schedule a resharding of a bucket reshard list list all bucket resharding or scheduled to be resharded @@ -357,6 +363,7 @@ --policy-doc permission policy document --path-prefix path prefix for filtering roles --description Role description + --policy-arn ARN of a managed policy MFA options: --totp-serial a string that represents the ID of a TOTP token -- 2.39.5