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;
}
user_tenant,
role_path,
trust_policy,
- max_session_duration);
+ 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;
return;
}
op_ret = role->create(s, true, y);
-
if (op_ret == -EEXIST) {
op_ret = -ERR_ROLE_EXISTS;
}
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
std::string perm_policy;
std::string path_prefix;
std::string max_session_duration;
+ std::multimap<std::string,std::string> tags;
+ std::vector<std::string> tagKeys;
std::unique_ptr<rgw::sal::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;
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)
this->trust_policy = trust_policy;
}
+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;
std::map<std::string, std::string> perm_policy_map;
std::string tenant;
uint64_t max_session_duration;
+ std::multimap<std::string,std::string> tags;
public:
virtual int store_info(const DoutPrefixProvider *dpp, bool exclusive, optional_yield y) = 0;
std::string tenant,
std::string path="",
std::string trust_policy="",
- std::string max_session_duration_str="")
+ std::string max_session_duration_str="",
+ std::multimap<std::string,std::string> tags={})
: 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);
std::vector<std::string> get_role_policy_names();
int get_role_policy(const DoutPrefixProvider* dpp, const std::string& policy_name, std::string& perm_policy);
int delete_policy(const DoutPrefixProvider* dpp, const std::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);
std::string tenant,
std::string path="",
std::string trust_policy="",
- std::string max_session_duration_str="") = 0;
+ std::string max_session_duration_str="",
+ std::multimap<std::string,std::string> tags={}) = 0;
virtual std::unique_ptr<RGWRole> get_role(std::string id) = 0;
virtual int get_roles(const DoutPrefixProvider *dpp,
optional_yield y,
std::string tenant,
std::string path,
std::string trust_policy,
- std::string max_session_duration_str)
+ std::string max_session_duration_str,
+ std::multimap<std::string,std::string> tags)
{
RGWRole* p = nullptr;
return std::unique_ptr<RGWRole>(p);
virtual std::string get_host_id() { return ""; }
virtual std::unique_ptr<LuaScriptManager> get_lua_script_manager() override;
- virtual std::unique_ptr<RGWRole> get_role(string name,
- string tenant,
- string path="",
- string trust_policy="",
- string max_session_duration_str="") override;
+ virtual std::unique_ptr<RGWRole> get_role(std::string name,
+ std::string tenant,
+ std::string path="",
+ std::string trust_policy="",
+ std::string max_session_duration_str="",
+ std::multimap<std::string,std::string> tags={}) override;
virtual std::unique_ptr<RGWRole> get_role(std::string id) override;
virtual int get_roles(const DoutPrefixProvider *dpp,
optional_yield y,
std::string tenant,
std::string path,
std::string trust_policy,
- std::string max_session_duration_str)
+ std::string max_session_duration_str,
+ std::multimap<std::string,std::string> tags)
{
- return std::make_unique<RadosRole>(this, name, tenant, path, trust_policy, max_session_duration_str);
+ return std::make_unique<RadosRole>(this, name, tenant, path, trust_policy, max_session_duration_str, tags);
}
std::unique_ptr<RGWRole> RadosStore::get_role(std::string id)
bufferlist bl;
encode(*this, bl);
+ 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, store->get_zone()->get_params().roles_pool, oid, bl, exclusive, nullptr, real_time(), y, &attrs);
+ }
+
return rgw_put_system_obj(dpp, obj_ctx, store->get_zone()->get_params().roles_pool, oid, bl, exclusive, nullptr, real_time(), y);
}
std::string oid = get_info_oid_prefix() + id;
bufferlist bl;
- int ret = rgw_get_system_obj(obj_ctx, store->get_zone()->get_params().roles_pool, oid, bl, nullptr, nullptr, null_yield, dpp);
+ map<string, bufferlist> attrs;
+ int ret = rgw_get_system_obj(obj_ctx, store->get_zone()->get_params().roles_pool, oid, bl, nullptr, nullptr, null_yield, dpp, &attrs, nullptr, boost::none, true);
if (ret < 0) {
ldpp_dout(dpp, 0) << "ERROR: failed reading role info from Role pool: " << id << ": " << cpp_strerror(-ret) << dendl;
return ret;
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;
}
std::string tenant,
std::string path="",
std::string trust_policy="",
- std::string max_session_duration_str="") override;
+ std::string max_session_duration_str="",
+ std::multimap<std::string,std::string> tags={}) override;
virtual std::unique_ptr<RGWRole> get_role(std::string id) override;
virtual int get_roles(const DoutPrefixProvider *dpp,
optional_yield y,
std::string tenant,
std::string path,
std::string trust_policy,
- std::string max_session_duration) : RGWRole(name, tenant, path, trust_policy, max_session_duration), store(_store) {}
+ std::string max_session_duration,
+ std::multimap<std::string,std::string> tags) : RGWRole(name, tenant, path, trust_policy, max_session_duration, tags), store(_store) {}
RadosRole(RadosStore* _store, std::string id) : RGWRole(id), store(_store) {}
~RadosRole() = default;
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 std::string& key, bufferlist& bl,
RGWObjVersionTracker *objv_tracker, real_time *pmtime, optional_yield y, const DoutPrefixProvider *dpp, std::map<std::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 std::string& oid,
RGWObjVersionTracker *objv_tracker, optional_yield y);