return 0;
}
+bool is_non_s3_op(RGWOpType op_type)
+{
+ if (op_type == RGW_STS_GET_SESSION_TOKEN ||
+ op_type == RGW_STS_ASSUME_ROLE ||
+ op_type == RGW_STS_ASSUME_ROLE_WEB_IDENTITY ||
+ op_type == RGW_OP_CREATE_ROLE ||
+ op_type == RGW_OP_DELETE_ROLE ||
+ op_type == RGW_OP_GET_ROLE ||
+ op_type == RGW_OP_MODIFY_ROLE ||
+ op_type == RGW_OP_LIST_ROLES ||
+ op_type == RGW_OP_PUT_ROLE_POLICY ||
+ op_type == RGW_OP_GET_ROLE_POLICY ||
+ op_type == RGW_OP_LIST_ROLE_POLICIES ||
+ op_type == RGW_OP_DELETE_ROLE_POLICY ||
+ op_type == RGW_OP_PUT_USER_POLICY ||
+ op_type == RGW_OP_GET_USER_POLICY ||
+ op_type == RGW_OP_LIST_USER_POLICIES ||
+ op_type == RGW_OP_DELETE_USER_POLICY ||
+ op_type == RGW_OP_CREATE_OIDC_PROVIDER ||
+ op_type == RGW_OP_DELETE_OIDC_PROVIDER ||
+ op_type == RGW_OP_GET_OIDC_PROVIDER ||
+ op_type == RGW_OP_LIST_OIDC_PROVIDERS ||
+ op_type == RGW_OP_PUBSUB_TOPIC_CREATE ||
+ op_type == RGW_OP_PUBSUB_TOPICS_LIST ||
+ op_type == RGW_OP_PUBSUB_TOPIC_GET ||
+ op_type == RGW_OP_PUBSUB_TOPIC_DELETE ||
+ op_type == RGW_OP_TAG_ROLE ||
+ op_type == RGW_OP_LIST_ROLE_TAGS ||
+ op_type == RGW_OP_UNTAG_ROLE) {
+ return true;
+ }
+ return false;
+}
+
int parse_v4_credentials(const req_info& info, /* in */
std::string_view& access_key_id, /* out */
std::string_view& credential_scope, /* out */
static constexpr char AWS4_STREAMING_PAYLOAD_HASH[] = \
"STREAMING-AWS4-HMAC-SHA256-PAYLOAD";
+bool is_non_s3_op(RGWOpType op_type);
+
int parse_v4_credentials(const req_info& info, /* in */
std::string_view& access_key_id, /* out */
std::string_view& credential_scope, /* out */
{ "iam:DeleteOIDCProvider", iamDeleteOIDCProvider},
{ "iam:GetOIDCProvider", iamGetOIDCProvider},
{ "iam:ListOIDCProviders", iamListOIDCProviders},
+ { "iam:TagRole", iamTagRole},
+ { "iam:ListRoleTags", iamListRoleTags},
+ { "iam:UntagRole", iamUntagRole},
{ "sts:AssumeRole", stsAssumeRole},
{ "sts:AssumeRoleWithWebIdentity", stsAssumeRoleWithWebIdentity},
{ "sts:GetSessionToken", stsGetSessionToken},
case iamListOIDCProviders:
return "iam:ListOIDCProviders";
+ case iamTagRole:
+ return "iam:TagRole";
+
+ case iamListRoleTags:
+ return "iam:ListRoleTags";
+
+ case iamUntagRole:
+ return "iam:UntagRole";
+
case stsAssumeRole:
return "sts:AssumeRole";
static constexpr std::uint64_t iamDeleteOIDCProvider = s3All + 15;
static constexpr std::uint64_t iamGetOIDCProvider = s3All + 16;
static constexpr std::uint64_t iamListOIDCProviders = s3All + 17;
-static constexpr std::uint64_t iamAll = s3All + 18;
+static constexpr std::uint64_t iamTagRole = s3All + 18;
+static constexpr std::uint64_t iamListRoleTags = s3All + 19;
+static constexpr std::uint64_t iamUntagRole = s3All + 20;
+static constexpr std::uint64_t iamAll = s3All + 21;
static constexpr std::uint64_t stsAssumeRole = iamAll + 1;
static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = iamAll + 2;
RGW_OP_GET_ROLE_POLICY,
RGW_OP_LIST_ROLE_POLICIES,
RGW_OP_DELETE_ROLE_POLICY,
+ RGW_OP_TAG_ROLE,
+ RGW_OP_LIST_ROLE_TAGS,
+ RGW_OP_UNTAG_ROLE,
RGW_OP_PUT_BUCKET_POLICY,
RGW_OP_GET_BUCKET_POLICY,
RGW_OP_DELETE_BUCKET_POLICY,
return new RGWGetOIDCProvider;
if (action.compare("DeleteOpenIDConnectProvider") == 0)
return new RGWDeleteOIDCProvider;
+ if (action.compare("TagRole") == 0)
+ return new RGWTagRole;
+ if (action.compare("ListRoleTags") == 0)
+ return new RGWListRoleTags;
+ if (action.compare("UntagRole") == 0)
+ return new RGWUntagRole;
}
return nullptr;
// vim: ts=8 sw=2 smarttab ft=cpp
#include <errno.h>
+#include <regex>
#include "common/errno.h"
#include "common/Formatter.h"
return 0;
}
+int RGWRestRole::parse_tags()
+{
+ vector<string> keys, vals;
+ auto val_map = s->info.args.get_params();
+ const regex pattern_key("Tags.member.([0-9]+).Key");
+ const regex pattern_value("Tags.member.([0-9]+).Value");
+ for (auto& v : val_map) {
+ string key_index="", value_index="";
+ for(sregex_iterator it = sregex_iterator(
+ v.first.begin(), v.first.end(), pattern_key);
+ it != sregex_iterator(); it++) {
+ smatch match;
+ match = *it;
+ key_index = match.str(1);
+ ldout(s->cct, 20) << "Key index: " << match.str(1) << dendl;
+ if (!key_index.empty()) {
+ int index = stoi(key_index);
+ auto pos = keys.begin() + (index-1);
+ keys.insert(pos, v.second);
+ }
+ }
+ for(sregex_iterator it = sregex_iterator(
+ v.first.begin(), v.first.end(), pattern_value);
+ it != sregex_iterator(); it++) {
+ smatch match;
+ match = *it;
+ value_index = match.str(1);
+ ldout(s->cct, 20) << "Value index: " << match.str(1) << dendl;
+ if (!value_index.empty()) {
+ int index = stoi(value_index);
+ auto pos = vals.begin() + (index-1);
+ vals.insert(pos, v.second);
+ }
+ }
+ }
+ if (keys.size() != vals.size()) {
+ ldout(s->cct, 0) << "No. of keys doesn't match with no. of values in tags" << dendl;
+ return -EINVAL;
+ }
+ for (size_t i = 0; i < keys.size(); i++) {
+ tags.emplace(keys[i], vals[i]);
+ ldout(s->cct, 0) << "Tag Key: " << keys[i] << " Tag Value is: " << vals[i] << dendl;
+ }
+ return 0;
+}
+
void RGWRestRole::send_response()
{
if (op_ret) {
return -ERR_MALFORMED_DOC;
}
+ int ret = parse_tags();
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (tags.size() > 50) {
+ ldout(s->cct, 0) << "No. tags is greater than 50" << dendl;
+ return -EINVAL;
+ }
+
return 0;
}
}
std::string user_tenant = s->user->get_tenant();
RGWRole role(s->cct, store->getRados()->pctl, role_name, role_path, trust_policy,
- user_tenant, max_session_duration);
+ user_tenant, max_session_duration, tags);
if (!user_tenant.empty() && role.get_tenant() != user_tenant) {
ldpp_dout(this, 20) << "ERROR: the tenant provided in the role name does not match with the tenant of the user creating the role"
<< dendl;
s->formatter->close_section();
s->formatter->close_section();
}
+
+int RGWTagRole::get_params()
+{
+ role_name = s->info.args.get("RoleName");
+
+ if (role_name.empty()) {
+ ldout(s->cct, 0) << "ERROR: Role name is empty" << dendl;
+ return -EINVAL;
+ }
+ int ret = parse_tags();
+ if (ret < 0) {
+ return ret;
+ }
+
+ return 0;
+}
+
+void RGWTagRole::execute(optional_yield y)
+{
+ op_ret = get_params();
+ if (op_ret < 0) {
+ return;
+ }
+
+ op_ret = _role.set_tags(this, tags);
+ if (op_ret == 0) {
+ op_ret = _role.update(this, y);
+ }
+
+ if (op_ret == 0) {
+ s->formatter->open_object_section("TagRoleResponse");
+ s->formatter->open_object_section("ResponseMetadata");
+ s->formatter->dump_string("RequestId", s->trans_id);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
+
+int RGWListRoleTags::get_params()
+{
+ role_name = s->info.args.get("RoleName");
+
+ if (role_name.empty()) {
+ ldout(s->cct, 0) << "ERROR: Role name is empty" << dendl;
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+void RGWListRoleTags::execute(optional_yield y)
+{
+ op_ret = get_params();
+ if (op_ret < 0) {
+ return;
+ }
+
+ boost::optional<multimap<string,string>> tag_map = _role.get_tags();
+ s->formatter->open_object_section("ListRoleTagsResponse");
+ s->formatter->open_object_section("ListRoleTagsResult");
+ if (tag_map) {
+ s->formatter->open_array_section("Tags");
+ for (const auto& it : tag_map.get()) {
+ s->formatter->open_object_section("Key");
+ encode_json("Key", it.first, s->formatter);
+ s->formatter->close_section();
+ s->formatter->open_object_section("Value");
+ encode_json("Value", it.second, s->formatter);
+ s->formatter->close_section();
+ }
+ s->formatter->close_section();
+ }
+ s->formatter->close_section();
+ s->formatter->open_object_section("ResponseMetadata");
+ s->formatter->dump_string("RequestId", s->trans_id);
+ s->formatter->close_section();
+ s->formatter->close_section();
+}
+
+int RGWUntagRole::get_params()
+{
+ role_name = s->info.args.get("RoleName");
+
+ if (role_name.empty()) {
+ ldout(s->cct, 0) << "ERROR: Role name is empty" << dendl;
+ return -EINVAL;
+ }
+
+ auto val_map = s->info.args.get_params();
+ for (auto& it : val_map) {
+ if (it.first.find("TagKeys.member.") != string::npos) {
+ tagKeys.emplace_back(it.second);
+ }
+ }
+ return 0;
+}
+
+void RGWUntagRole::execute(optional_yield y)
+{
+ op_ret = get_params();
+ if (op_ret < 0) {
+ return;
+ }
+
+ _role.erase_tags(tagKeys);
+ op_ret = _role.update(this, y);
+
+ if (op_ret == 0) {
+ s->formatter->open_object_section("UntagRoleResponse");
+ s->formatter->open_object_section("ResponseMetadata");
+ s->formatter->dump_string("RequestId", s->trans_id);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
\ No newline at end of file
string perm_policy;
string path_prefix;
string max_session_duration;
+ std::multimap<std::string,std::string> tags;
+ std::vector<std::string> tagKeys;
RGWRole _role;
public:
int verify_permission(optional_yield y) override;
void send_response() override;
virtual uint64_t get_op() = 0;
+ int parse_tags();
};
class RGWRoleRead : public RGWRestRole {
RGWOpType get_type() override { return RGW_OP_DELETE_ROLE_POLICY; }
uint64_t get_op() override { return rgw::IAM::iamDeleteRolePolicy; }
};
+
+class RGWTagRole : public RGWRoleWrite {
+public:
+ RGWTagRole() = default;
+ void execute(optional_yield y) override;
+ int get_params();
+ const char* name() const override { return "tag_role"; }
+ RGWOpType get_type() override { return RGW_OP_TAG_ROLE; }
+ uint64_t get_op() override { return rgw::IAM::iamTagRole; }
+};
+
+class RGWListRoleTags : public RGWRoleRead {
+public:
+ RGWListRoleTags() = default;
+ void execute(optional_yield y) override;
+ int get_params();
+ const char* name() const override { return "list_role_tags"; }
+ RGWOpType get_type() override { return RGW_OP_LIST_ROLE_TAGS; }
+ uint64_t get_op() override { return rgw::IAM::iamListRoleTags; }
+};
+
+class RGWUntagRole : public RGWRoleWrite {
+public:
+ RGWUntagRole() = default;
+ void execute(optional_yield y) override;
+ int get_params();
+ const char* name() const override { return "untag_role"; }
+ RGWOpType get_type() override { return RGW_OP_UNTAG_ROLE; }
+ uint64_t get_op() override { return rgw::IAM::iamUntagRole; }
+};
throw -EPERM;
}
- bool is_non_s3_op = false;
- if (s->op_type == RGW_STS_GET_SESSION_TOKEN ||
- s->op_type == RGW_STS_ASSUME_ROLE ||
- s->op_type == RGW_STS_ASSUME_ROLE_WEB_IDENTITY ||
- s->op_type == RGW_OP_CREATE_ROLE ||
- s->op_type == RGW_OP_DELETE_ROLE ||
- s->op_type == RGW_OP_GET_ROLE ||
- s->op_type == RGW_OP_MODIFY_ROLE ||
- s->op_type == RGW_OP_LIST_ROLES ||
- s->op_type == RGW_OP_PUT_ROLE_POLICY ||
- s->op_type == RGW_OP_GET_ROLE_POLICY ||
- s->op_type == RGW_OP_LIST_ROLE_POLICIES ||
- s->op_type == RGW_OP_DELETE_ROLE_POLICY ||
- s->op_type == RGW_OP_PUT_USER_POLICY ||
- s->op_type == RGW_OP_GET_USER_POLICY ||
- s->op_type == RGW_OP_LIST_USER_POLICIES ||
- s->op_type == RGW_OP_DELETE_USER_POLICY ||
- s->op_type == RGW_OP_CREATE_OIDC_PROVIDER ||
- s->op_type == RGW_OP_DELETE_OIDC_PROVIDER ||
- s->op_type == RGW_OP_GET_OIDC_PROVIDER ||
- s->op_type == RGW_OP_LIST_OIDC_PROVIDERS ||
- s->op_type == RGW_OP_PUBSUB_TOPIC_CREATE ||
- s->op_type == RGW_OP_PUBSUB_TOPICS_LIST ||
- s->op_type == RGW_OP_PUBSUB_TOPIC_GET ||
- s->op_type == RGW_OP_PUBSUB_TOPIC_DELETE) {
- is_non_s3_op = true;
- }
+ bool is_non_s3_op = rgw::auth::s3::is_non_s3_op(s->op_type);
const char* exp_payload_hash = nullptr;
string payload_hash;
auto svc = ctl->svc;
auto obj_ctx = ctl->svc->sysobj->init_obj_ctx();
+
+ if (!this->tags.empty()) {
+ bufferlist bl_tags;
+ encode(this->tags, bl_tags);
+ map<string, bufferlist> attrs;
+ attrs.emplace("tagging", bl_tags);
+ return rgw_put_system_obj(dpp, obj_ctx, svc->zone->get_zone_params().roles_pool, oid, bl, exclusive, nullptr, real_time(), y, &attrs);
+ }
+
return rgw_put_system_obj(dpp, obj_ctx, svc->zone->get_zone_params().roles_pool, oid,
bl, exclusive, NULL, real_time(), y, NULL);
}
encode_json("CreateDate", creation_date, f);
encode_json("MaxSessionDuration", max_session_duration, f);
encode_json("AssumeRolePolicyDocument", trust_policy, f);
+ if (!tags.empty()) {
+ f->open_array_section("Tags");
+ for (const auto& it : tags) {
+ f->open_object_section("Key");
+ encode_json("Key", it.first, f);
+ f->close_section();
+ f->open_object_section("Value");
+ encode_json("Value", it.second, f);
+ f->close_section();
+ }
+ f->close_section();
+ }
}
void RGWRole::decode_json(JSONObj *obj)
bufferlist bl;
auto obj_ctx = svc->sysobj->init_obj_ctx();
- int ret = rgw_get_system_obj(obj_ctx, pool, oid, bl, NULL, NULL, y, dpp);
+ map<string, bufferlist> attrs;
+
+ int ret = rgw_get_system_obj(obj_ctx, pool, oid, bl, NULL, NULL, y, dpp, &attrs, nullptr, boost::none, true);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: failed reading role info from pool: " << pool.name <<
": " << id << ": " << cpp_strerror(-ret) << dendl;
return -EIO;
}
+ auto it = attrs.find("tagging");
+ if (it != attrs.end()) {
+ bufferlist bl_tags = it->second;
+ try {
+ using ceph::decode;
+ auto iter = bl_tags.cbegin();
+ decode(tags, iter);
+ } catch (buffer::error& err) {
+ ldpp_dout(dpp, 0) << "ERROR: failed to decode attrs" << id << dendl;
+ return -EIO;
+ }
+ }
+
return 0;
}
return 0;
}
+int RGWRole::set_tags(const DoutPrefixProvider* dpp, const multimap<string,string>& tags_map)
+{
+ for (auto& it : tags_map) {
+ this->tags.emplace(it.first, it.second);
+ }
+ if (this->tags.size() > 50) {
+ ldpp_dout(dpp, 0) << "No. of tags is greater than 50" << dendl;
+ return -EINVAL;
+ }
+ return 0;
+}
+
+boost::optional<multimap<string,string>> RGWRole::get_tags()
+{
+ if(this->tags.empty()) {
+ return boost::none;
+ }
+ return this->tags;
+}
+
+void RGWRole::erase_tags(const vector<string>& tagKeys)
+{
+ for (auto& it : tagKeys) {
+ this->tags.erase(it);
+ }
+}
+
const string& RGWRole::get_names_oid_prefix()
{
return role_name_oid_prefix;
map<string, string> perm_policy_map;
string tenant;
uint64_t max_session_duration;
+ std::multimap<std::string,std::string> tags;
int store_info(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y);
int store_name(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y);
string path,
string trust_policy,
string tenant,
- string max_session_duration_str="")
+ string max_session_duration_str="",
+ std::multimap<std::string,std::string> tags={})
: cct(cct),
ctl(ctl),
name(std::move(name)),
path(std::move(path)),
trust_policy(std::move(trust_policy)),
- tenant(std::move(tenant)) {
+ tenant(std::move(tenant)),
+ tags(std::move(tags)) {
if (this->path.empty())
this->path = "/";
extract_name_tenant(this->name);
}
void decode(bufferlist::const_iterator& bl) {
- DECODE_START(2, bl);
+ DECODE_START(3, bl);
decode(id, bl);
decode(name, bl);
decode(path, bl);
vector<string> get_role_policy_names();
int get_role_policy(const string& policy_name, string& perm_policy);
int delete_policy(const string& policy_name);
+ int set_tags(const DoutPrefixProvider* dpp, const std::multimap<std::string,std::string>& tags_map);
+ boost::optional<std::multimap<std::string,std::string>> get_tags();
+ void erase_tags(const std::vector<std::string>& tagKeys);
void dump(Formatter *f) const;
void decode_json(JSONObj *obj);
int rgw_get_system_obj(RGWSysObjectCtx& obj_ctx, const rgw_pool& pool, const string& key, bufferlist& bl,
RGWObjVersionTracker *objv_tracker, real_time *pmtime, optional_yield y, const DoutPrefixProvider *dpp, map<string, bufferlist> *pattrs,
rgw_cache_entry_info *cache_info,
- boost::optional<obj_version> refresh_version)
+ boost::optional<obj_version> refresh_version, bool raw_attrs)
{
bufferlist::iterator iter;
int request_len = READ_CHUNK_LEN;
int ret = rop.set_attrs(pattrs)
.set_last_mod(pmtime)
.set_objv_tracker(objv_tracker)
+ .set_raw_attrs(raw_attrs)
.stat(y, dpp);
if (ret < 0)
return ret;
int rgw_get_system_obj(RGWSysObjectCtx& obj_ctx, const rgw_pool& pool, const string& key, bufferlist& bl,
RGWObjVersionTracker *objv_tracker, real_time *pmtime, optional_yield y, const DoutPrefixProvider *dpp, map<string, bufferlist> *pattrs = NULL,
rgw_cache_entry_info *cache_info = NULL,
- boost::optional<obj_version> refresh_version = boost::none);
+ boost::optional<obj_version> refresh_version = boost::none, bool raw_attrs=false);
int rgw_delete_system_obj(const DoutPrefixProvider *dpp,
RGWSI_SysObj *sysobj_svc, const rgw_pool& pool, const string& oid,
RGWObjVersionTracker *objv_tracker, optional_yield y);