OPTION(rgw_s3_auth_use_sts, OPT_BOOL) // should we try to use sts for s3?
OPTION(rgw_sts_max_session_duration, OPT_U64) // Max duration in seconds for which the session token is valid.
OPTION(fake_statfs_for_testing, OPT_INT) // Set a value for kb and compute kb_used from total of num_bytes
+OPTION(rgw_sts_token_introspection_url, OPT_STR) // url for introspecting web tokens
+OPTION(rgw_sts_client_id, OPT_STR) // Client Id
+OPTION(rgw_sts_client_secret, OPT_STR) // Client Secret
"ListBucketVersions(max-keys), "
"ListBucketMultiPartUploads(max-uploads), "
"ListMultipartUploadParts(max-parts)"),
+
+ Option("rgw_sts_token_introspection_url", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+ .set_default("")
+ .set_description("STS Web Token introspection URL")
+ .set_long_description("URL for introspecting an STS Web Token."),
+
+ Option("rgw_sts_client_id", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+ .set_default("")
+ .set_description("Client Id")
+ .set_long_description("Client Id needed for introspecting a Web Token."),
+
+ Option("rgw_sts_client_secret", Option::TYPE_STR, Option::LEVEL_ADVANCED)
+ .set_default("")
+ .set_description("Client Secret")
+ .set_long_description("Client Secret needed for introspecting a Web Token."),
});
}
auth_stack.push_back(std::make_pair(std::cref(engine), ctrl_flag));
}
+void rgw::auth::WebIdentityApplier::to_str(std::ostream& out) const
+{
+ out << "rgw::auth::WebIdentityApplier(sub =" << token_claims.sub
+ << ", user_name=" << token_claims.user_name
+ << ", aud =" << token_claims.aud
+ << ", provider_id =" << token_claims.iss << ")";
+}
+
+string rgw::auth::WebIdentityApplier::get_idp_url() const
+{
+ string idp_url = token_claims.iss;
+ auto pos = idp_url.find("http://");
+ if (pos == std::string::npos) {
+ pos = idp_url.find("https://");
+ if (pos != std::string::npos) {
+ idp_url.erase(pos, 8);
+ }
+ } else {
+ idp_url.erase(pos, 7);
+ }
+ return idp_url;
+}
+
+void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const
+{
+ s->info.args.append("sub", token_claims.sub);
+ s->info.args.append("aud", token_claims.aud);
+ s->info.args.append("provider_id", token_claims.iss);
+
+ string idp_url = get_idp_url();
+ string condition = idp_url + ":app_id";
+ s->env.emplace(condition, token_claims.aud);
+}
+
+bool rgw::auth::WebIdentityApplier::is_identity(const idset_t& ids) const
+{
+ if (ids.size() > 1) {
+ return false;
+ }
+
+ for (auto id : ids) {
+ string idp_url = get_idp_url();
+ if (id.is_oidc_provider() && id.get_idp_url() == idp_url) {
+ return true;
+ }
+ }
+ return false;
+}
/* rgw::auth::RemoteAuthApplier */
uint32_t rgw::auth::RemoteApplier::get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const
#include "rgw_common.h"
#include "rgw_keystone.h"
+#include "rgw_web_idp.h"
#define RGW_USER_ANON_ID "anonymous"
* Each new Strategy should be exposed to it. */
class StrategyRegistry;
+class WebIdentityApplier : public IdentityApplier {
+protected:
+ CephContext* const cct;
+ RGWRados* const store;
+ rgw::web_idp::WebTokenClaims token_claims;
+
+ string get_idp_url() const;
+
+public:
+ WebIdentityApplier( CephContext* const cct,
+ RGWRados* const store,
+ const rgw::web_idp::WebTokenClaims& token_claims)
+ : cct(cct),
+ store(store),
+ token_claims(token_claims) {
+ }
+
+ void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override {
+ user_info.user_id = rgw_user(token_claims.sub);
+ user_info.display_name = token_claims.user_name;
+ }
+
+ void modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const override;
+
+ uint32_t get_perms_from_aclspec(const DoutPrefixProvider* dpp, const aclspec_t& aclspec) const override {
+ return RGW_PERM_NONE;
+ }
+
+ bool is_admin_of(const rgw_user& uid) const override {
+ return false;
+ }
+
+ bool is_owner_of(const rgw_user& uid) const override {
+ return false;
+ }
+
+ uint32_t get_perm_mask() const override {
+ return RGW_PERM_NONE;
+ }
+
+ void to_str(std::ostream& out) const override;
+
+ bool is_identity(const idset_t& ids) const override;
+
+ uint32_t get_identity_type() const override {
+ return TYPE_WEB;
+ }
+
+ string get_acct_name() const override {
+ return token_claims.user_name;
+ }
+
+ struct Factory {
+ virtual ~Factory() {}
+
+ virtual aplptr_t create_apl_web_identity( CephContext* cct,
+ const req_state* s,
+ const rgw::web_idp::WebTokenClaims& token) const = 0;
+ };
+};
+
/* rgw::auth::RemoteApplier targets those authentication engines which don't
* need to ask the RADOS store while performing the auth process. Instead,
* they obtain credentials from an external source like Keystone or LDAP.
#include "rgw_auth.h"
#include "rgw_auth_s3.h"
#include "rgw_swift_auth.h"
+#include "rgw_rest_sts.h"
namespace rgw {
namespace auth {
rgw::auth::swift::DefaultStrategy swift_strategy;
+ rgw::auth::sts::DefaultStrategy sts_strategy;
+
public:
StrategyRegistry(CephContext* const cct,
RGWRados* const store)
: s3_main_strategy(cct, store),
s3_post_strategy(cct, store),
- swift_strategy(cct, store) {
+ swift_strategy(cct, store),
+ sts_strategy(cct, store) {
}
const s3_main_strategy_t& get_s3_main() const {
return swift_strategy;
}
+ const rgw::auth::sts::DefaultStrategy& get_sts() const {
+ return sts_strategy;
+ }
+
static std::shared_ptr<StrategyRegistry>
create(CephContext* const cct,
RGWRados* const store) {
#include "common/armor.h"
#include "common/utf8.h"
+#include "rgw_rest_s3.h"
#include "rgw_auth_s3.h"
#include "rgw_common.h"
#include "rgw_client_io.h"
#include "common/sstring.hh"
#include "rgw_common.h"
#include "rgw_rest_s3.h"
-
#include "rgw_auth.h"
#include "rgw_auth_filters.h"
#include "rgw_auth_keystone.h"
rgw::auth::RoleApplier(cct, role_name, user_id, role_policies));
return aplptr_t(new decltype(apl)(std::move(apl)));
}
+
public:
STSAuthStrategy(CephContext* const cct,
RGWRados* const store,
namespace rgw {
namespace auth {
class Principal {
- enum types { User, Role, Tenant, Wildcard };
+ enum types { User, Role, Tenant, Wildcard, OidcProvider };
types t;
rgw_user u;
+ string idp_url;
explicit Principal(types t)
: t(t) {}
Principal(types t, std::string&& n, std::string i)
: t(t), u(std::move(n), std::move(i)) {}
+ Principal(string&& idp_url)
+ : t(OidcProvider), idp_url(std::move(idp_url)) {}
+
public:
static Principal wildcard() {
return Principal(Tenant, std::move(t), {});
}
+ static Principal oidc_provider(string&& idp_url) {
+ return Principal(std::move(idp_url));
+ }
+
bool is_wildcard() const {
return t == Wildcard;
}
return t == Tenant;
}
+ bool is_oidc_provider() const {
+ return t == OidcProvider;
+ }
+
const std::string& get_tenant() const {
return u.tenant;
}
return u.id;
}
+ const string& get_idp_url() const {
+ return idp_url;
+ }
+
bool operator ==(const Principal& o) const {
return (t == o.t) && (u == o.u);
}
rgw_http_errors rgw_http_sts_errors({
{ ERR_PACKED_POLICY_TOO_LARGE, {400, "PackedPolicyTooLarge" }},
+ { ERR_INVALID_IDENTITY_TOKEN, {400, "InvalidIdentityToken" }},
});
int rgw_perf_start(CephContext *cct)
// STS Errors
#define ERR_PACKED_POLICY_TOO_LARGE 2400
+#define ERR_INVALID_IDENTITY_TOKEN 2401
#ifndef UINT32_MAX
#define UINT32_MAX (0xffffffffu)
/* sts specific*/
RGW_STS_ASSUME_ROLE,
RGW_STS_GET_SESSION_TOKEN,
+ RGW_STS_ASSUME_ROLE_WEB_IDENTITY,
/* pubsub */
RGW_OP_PUBSUB_TOPIC_CREATE,
RGW_OP_PUBSUB_TOPICS_LIST,
TYPE_RGW=1,
TYPE_KEYSTONE=2,
TYPE_LDAP=3,
- TYPE_ROLE=4
+ TYPE_ROLE=4,
+ TYPE_WEB=5,
};
static string RGW_STORAGE_CLASS_STANDARD = "STANDARD";
std::regex_constants::ECMAScript |
std::regex_constants::optimize);
static const regex rx_no_wild(
- "arn:([^:*]*):([^:*]*):([^:*]*):([^:*]*):([^:*]*)",
+ "arn:([^:*]*):([^:*]*):([^:*]*):([^:*]*):(.*)",
std::regex_constants::ECMAScript |
std::regex_constants::optimize);
{ "iam:ListRolePolicies", iamListRolePolicies},
{ "iam:DeleteRolePolicy", iamDeleteRolePolicy},
{ "sts:AssumeRole", stsAssumeRole},
+ { "sts:AssumeRoleWithWebIdentity", stsAssumeRoleWithWebIdentity},
};
struct PolicyParser;
// Do nothing for now.
} else if (t == TokenID::CanonicalUser) {
- // AWS ARNs
- } else if (t == TokenID::AWS) {
+ } // AWS and Federated ARNs
+ else if (t == TokenID::AWS || t == TokenID::Federated) {
if (auto a = ARN::parse(s)) {
if (a->resource == "root") {
return Principal::tenant(std::move(a->account));
return Principal::role(std::move(a->account),
match[2]);
}
+
+ if (match[1] == "oidc-provider") {
+ return Principal::oidc_provider(std::move(match[2]));
+ }
}
} else {
if (std::none_of(s.begin(), s.end(),
Effect Statement::eval_principal(const Environment& e,
boost::optional<const rgw::auth::Identity&> ida) const {
- if (ida && (!ida->is_identity(princ) || ida->is_identity(noprinc))) {
- return Effect::Deny;
+ if (ida) {
+ if (princ.empty() && noprinc.empty()) {
+ return Effect::Deny;
+ }
+ if (!princ.empty() && !ida->is_identity(princ)) {
+ return Effect::Deny;
+ } else if (!noprinc.empty() && ida->is_identity(noprinc)) {
+ return Effect::Deny;
+ }
}
return Effect::Allow;
}
+Effect Statement::eval_conditions(const Environment& e) const {
+ if (std::all_of(conditions.begin(),
+ conditions.end(),
+ [&e](const Condition& c) { return c.eval(e);})) {
+ return Effect::Allow;
+ }
+ return Effect::Deny;
+}
+
namespace {
const char* action_bit_string(uint64_t action) {
switch (action) {
case stsAssumeRole:
return "sts:AssumeRole";
+
+ case stsAssumeRoleWithWebIdentity:
+ return "sts:AssumeRoleWithWebIdentity";
}
return "s3Invalid";
}
return allowed ? Effect::Allow : Effect::Deny;
}
+Effect Policy::eval_conditions(const Environment& e) const {
+ auto allowed = false;
+ for (auto& s : statements) {
+ auto g = s.eval_conditions(e);
+ if (g == Effect::Deny) {
+ return g;
+ } else if (g == Effect::Allow) {
+ allowed = true;
+ }
+ }
+ return allowed ? Effect::Allow : Effect::Deny;
+}
+
ostream& operator <<(ostream& m, const Policy& p) {
m << "{ Version: "
<< (p.version == Version::v2008_10_17 ? "2008-10-17" : "2012-10-17");
static constexpr std::uint64_t iamDeleteRolePolicy = 67;
static constexpr std::uint64_t iamAll = 68;
static constexpr std::uint64_t stsAssumeRole = 69;
-static constexpr std::uint64_t stsAll = 70;
+static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = 70;
+static constexpr std::uint64_t stsAll = 71;
static constexpr std::uint64_t s3Count = s3DeleteObjectVersionTagging + 1;
static constexpr std::uint64_t allCount = stsAll + 1;
static const Action_t None(0);
static const Action_t s3AllValue("111111111111111111111111111111111111111111111111111111");
static const Action_t iamAllValue("11111111111110000000000000000000000000000000000000000000000000000000");
-static const Action_t stsAllValue("1000000000000000000000000000000000000000000000000000000000000000000000");
+static const Action_t stsAllValue("11000000000000000000000000000000000000000000000000000000000000000000000");
//Modify allValue if more Actions are added
-static const Action_t allValue("11111111111111111111111111111111111111111111111111111111111111111111111");
+static const Action_t allValue("111111111111111111111111111111111111111111111111111111111111111111111111");
namespace {
inline int op_to_perm(std::uint64_t op) {
Effect eval_principal(const Environment& e,
boost::optional<const rgw::auth::Identity&> ida) const;
+
+ Effect eval_conditions(const Environment& e) const;
};
std::ostream& operator <<(ostream& m, const Statement& s);
Effect eval_principal(const Environment& e,
boost::optional<const rgw::auth::Identity&> ida) const;
+ Effect eval_conditions(const Environment& e) const;
+
template <typename F>
bool has_conditional(const string& conditional, F p) const {
for (const auto&s: statements){
}
}
-rgw::IAM::Environment rgw_build_iam_environment(RGWRados* store,
- struct req_state* s)
+void rgw_build_iam_environment(RGWRados* store,
+ struct req_state* s)
{
- rgw::IAM::Environment e;
const auto& m = s->info.env->get_map();
auto t = ceph::real_clock::now();
- e.emplace("aws:CurrentTime", std::to_string(ceph::real_clock::to_time_t(t)));
- e.emplace("aws:EpochTime", ceph::to_iso_8601(t));
+ s->env.emplace("aws:CurrentTime", std::to_string(ceph::real_clock::to_time_t(t)));
+ s->env.emplace("aws:EpochTime", ceph::to_iso_8601(t));
// TODO: This is fine for now, but once we have STS we'll need to
// look and see. Also this won't work with the IdentityApplier
// model, since we need to know the actual credential.
- e.emplace("aws:PrincipalType", "User");
+ s->env.emplace("aws:PrincipalType", "User");
auto i = m.find("HTTP_REFERER");
if (i != m.end()) {
- e.emplace("aws:Referer", i->second);
+ s->env.emplace("aws:Referer", i->second);
}
if (rgw_transport_is_secure(s->cct, *s->info.env)) {
- e.emplace("aws:SecureTransport", "true");
+ s->env.emplace("aws:SecureTransport", "true");
}
const auto remote_addr_param = s->cct->_conf->rgw_remote_addr_param;
ip = &temp;
}
}
- e.emplace("aws:SourceIp", *ip);
+ s->env.emplace("aws:SourceIp", *ip);
}
i = m.find("HTTP_USER_AGENT"); {
if (i != m.end())
- e.emplace("aws:UserAgent", i->second);
+ s->env.emplace("aws:UserAgent", i->second);
}
if (s->user) {
// What to do about aws::userid? One can have multiple access
// keys so that isn't really suitable. Do we have a durable
// identifier that can persist through name changes?
- e.emplace("aws:username", s->user->user_id.id);
+ s->env.emplace("aws:username", s->user->user_id.id);
}
- return e;
}
void rgw_bucket_object_pre_exec(struct req_state *s)
return ret==-ENODATA ? -EACCES : ret;
}
- s->env = rgw_build_iam_environment(store, s);
+ rgw_build_iam_environment(store, s);
return ret;
}
extern int rgw_build_bucket_policies(RGWRados* store, struct req_state* s);
extern int rgw_build_object_policies(RGWRados *store, struct req_state *s,
bool prefetch_data);
-extern rgw::IAM::Environment rgw_build_iam_environment(RGWRados* store,
- struct req_state* s);
+extern void rgw_build_iam_environment(RGWRados* store,
+ struct req_state* s);
extern vector<rgw::IAM::Policy> get_iam_user_policy_from_attr(CephContext* cct,
RGWRados* store,
map<string, bufferlist>& attrs,
lderr(s->cct) << "Error reading IAM User Policy: " << e.what() << dendl;
}
}
- s->env = rgw_build_iam_environment(store, s);
+ rgw_build_iam_environment(store, s);
return 0;
}
return RGWHandler_REST::init(store, s, cio);
}
+int RGWHandler_REST_S3::authorize(const DoutPrefixProvider *dpp)
+{
+ if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+ return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
+ }
+ return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
+}
+
enum class AwsVersion {
UNKNOWN,
V2,
explicit RGWHandler_REST_S3(const rgw::auth::StrategyRegistry& auth_registry)
: RGWHandler_REST(),
auth_registry(auth_registry) {
- }
+ }
~RGWHandler_REST_S3() override = default;
int init(RGWRados *store,
struct req_state *s,
rgw::io::BasicClient *cio) override;
- int authorize(const DoutPrefixProvider *dpp) override {
- return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
- }
+ int authorize(const DoutPrefixProvider *dpp) override;
int postauth_init() override;
};
#include "rgw_rest.h"
#include "rgw_auth.h"
-#include "rgw_auth_s3.h"
+#include "rgw_auth_registry.h"
#include "rgw_rest_sts.h"
+#include "rgw_auth_s3.h"
+
#include "rgw_formats.h"
#include "rgw_client_io.h"
#define dout_context g_ceph_context
#define dout_subsys ceph_subsys_rgw
+namespace rgw {
+namespace auth {
+namespace sts {
+
+bool
+WebTokenEngine::is_applicable(const std::string& token) const noexcept
+{
+ return ! token.empty();
+}
+
+boost::optional<WebTokenEngine::token_t>
+WebTokenEngine::get_from_idp(const DoutPrefixProvider* dpp, const std::string& token) const
+{
+ //Access token conforming to OAuth2.0
+ if (! cct->_conf->rgw_sts_token_introspection_url.empty()) {
+ bufferlist introspect_resp;
+ RGWHTTPTransceiver introspect_req(cct, "POST", cct->_conf->rgw_sts_token_introspection_url, &introspect_resp);
+ //Headers
+ introspect_req.append_header("Content-Type", "application/x-www-form-urlencoded");
+ string base64_creds = "Basic " + rgw::to_base64(cct->_conf->rgw_sts_client_id + ":" + cct->_conf->rgw_sts_client_secret);
+ introspect_req.append_header("Authorization", base64_creds);
+ // POST data
+ string post_data = "token=" + token;
+ introspect_req.set_post_data(post_data);
+ introspect_req.set_send_length(post_data.length());
+
+ int res = introspect_req.process();
+ if (res < 0) {
+ ldpp_dout(dpp, 10) << "HTTP request res: " << res << dendl;
+ throw -EINVAL;
+ }
+ //Debug only
+ ldpp_dout(dpp, 20) << "HTTP status: " << introspect_req.get_http_status() << dendl;
+ ldpp_dout(dpp, 20) << "JSON Response is: " << introspect_resp.c_str() << dendl;
+
+ JSONParser parser;
+ WebTokenEngine::token_t token;
+ if (!parser.parse(introspect_resp.c_str(), introspect_resp.length())) {
+ ldpp_dout(dpp, 2) << "Malformed json" << dendl;
+ throw -EINVAL;
+ } else {
+ bool is_active;
+ JSONDecoder::decode_json("active", is_active, &parser);
+ if (! is_active) {
+ ldpp_dout(dpp, 0) << "Active state is false" << dendl;
+ throw -ERR_INVALID_IDENTITY_TOKEN;
+ }
+ JSONDecoder::decode_json("iss", token.iss, &parser);
+ JSONDecoder::decode_json("aud", token.aud, &parser);
+ JSONDecoder::decode_json("sub", token.sub, &parser);
+ JSONDecoder::decode_json("user_name", token.user_name, &parser);
+ }
+ return token;
+ }
+ return boost::none;
+}
+
+WebTokenEngine::result_t
+WebTokenEngine::authenticate( const DoutPrefixProvider* dpp,
+ const std::string& token,
+ const req_state* const s) const
+{
+ boost::optional<WebTokenEngine::token_t> t;
+
+ if (! is_applicable(token)) {
+ return result_t::deny();
+ }
+
+ try {
+ t = get_from_idp(dpp, token);
+ } catch(...) {
+ return result_t::deny(-EACCES);
+ }
+
+ if (t) {
+ auto apl = apl_factory->create_apl_web_identity(cct, s, *t);
+ return result_t::grant(std::move(apl));
+ }
+ return result_t::deny(-EACCES);
+}
+
+}; /* namespace sts */
+}; /* namespace auth */
+}; /* namespace rgw */
+
int RGWREST_STS::verify_permission()
{
STS::STSService _sts(s->cct, store, s->user->user_id, s->auth.identity.get());
try {
const rgw::IAM::Policy p(s->cct, s->user->user_id.tenant, bl);
//Check if the input role arn is there as one of the Principals in the policy,
- // If yes, then return 0, else -EPERM
- auto res = p.eval_principal(s->env, *s->auth.identity);
- if (res == rgw::IAM::Effect::Deny) {
+ // If yes, then return 0, else -EPERM
+ auto p_res = p.eval_principal(s->env, *s->auth.identity);
+ if (p_res == rgw::IAM::Effect::Deny) {
+ return -EPERM;
+ }
+ auto c_res = p.eval_conditions(s->env);
+ if (c_res == rgw::IAM::Effect::Deny) {
return -EPERM;
}
} catch (rgw::IAM::PolicyParseException& e) {
}
}
+int RGWSTSAssumeRoleWithWebIdentity::get_params()
+{
+ duration = s->info.args.get("DurationSeconds");
+ providerId = s->info.args.get("ProviderId");
+ policy = s->info.args.get("Policy");
+ roleArn = s->info.args.get("RoleArn");
+ roleSessionName = s->info.args.get("RoleSessionName");
+ iss = s->info.args.get("provider_id");
+ sub = s->info.args.get("sub");
+ aud = s->info.args.get("aud");
+
+ if (roleArn.empty() || roleSessionName.empty() || sub.empty() || aud.empty()) {
+ ldout(s->cct, 20) << "ERROR: one of role arn or role session name or token is empty" << dendl;
+ return -EINVAL;
+ }
+
+ if (! policy.empty()) {
+ bufferlist bl = bufferlist::static_from_string(policy);
+ try {
+ const rgw::IAM::Policy p(s->cct, s->user->user_id.tenant, bl);
+ }
+ catch (rgw::IAM::PolicyParseException& e) {
+ ldout(s->cct, 20) << "failed to parse policy: " << e.what() << "policy" << policy << dendl;
+ return -ERR_MALFORMED_DOC;
+ }
+ }
+
+ return 0;
+}
+
+void RGWSTSAssumeRoleWithWebIdentity::execute()
+{
+ if (op_ret = get_params(); op_ret < 0) {
+ return;
+ }
+
+ STS::AssumeRoleWithWebIdentityRequest req(duration, providerId, policy, roleArn,
+ roleSessionName, iss, sub, aud);
+ STS::AssumeRoleWithWebIdentityResponse response = sts.assumeRoleWithWebIdentity(req);
+ op_ret = std::move(response.assumeRoleResp.retCode);
+
+ //Dump the output
+ if (op_ret == 0) {
+ s->formatter->open_object_section("AssumeRoleWithWebIdentityResponse");
+ s->formatter->open_object_section("AssumeRoleWithWebIdentityResult");
+ encode_json("SubjectFromWebIdentityToken", response.sub , s->formatter);
+ encode_json("Audience", response.aud , s->formatter);
+ s->formatter->open_object_section("AssumedRoleUser");
+ response.assumeRoleResp.user.dump(s->formatter);
+ s->formatter->close_section();
+ s->formatter->open_object_section("Credentials");
+ response.assumeRoleResp.creds.dump(s->formatter);
+ s->formatter->close_section();
+ encode_json("Provider", response.providerId , s->formatter);
+ encode_json("PackedPolicySize", response.assumeRoleResp.packedPolicySize , s->formatter);
+ s->formatter->close_section();
+ s->formatter->close_section();
+ }
+}
+
int RGWSTSAssumeRole::get_params()
{
duration = s->info.args.get("DurationSeconds");
STS::AssumeRoleRequest req(duration, externalId, policy, roleArn,
roleSessionName, serialNumber, tokenCode);
- const auto& [ret, assumedRoleUser, creds, packedPolicySize] = sts.assumeRole(req);
- op_ret = std::move(ret);
+ STS::AssumeRoleResponse response = sts.assumeRole(req);
+ op_ret = std::move(response.retCode);
//Dump the output
if (op_ret == 0) {
s->formatter->open_object_section("AssumeRoleResponse");
s->formatter->open_object_section("AssumeRoleResult");
s->formatter->open_object_section("Credentials");
- creds.dump(s->formatter);
+ response.creds.dump(s->formatter);
s->formatter->close_section();
s->formatter->open_object_section("AssumedRoleUser");
- assumedRoleUser.dump(s->formatter);
+ response.user.dump(s->formatter);
s->formatter->close_section();
- encode_json("PackedPolicySize", packedPolicySize , s->formatter);
+ encode_json("PackedPolicySize", response.packedPolicySize , s->formatter);
s->formatter->close_section();
s->formatter->close_section();
}
}
-RGWOp *RGWHandler_REST_STS::op_post()
+int RGW_Auth_STS::authorize(const DoutPrefixProvider *dpp,
+ RGWRados *store,
+ const rgw::auth::StrategyRegistry& auth_registry,
+ struct req_state *s)
+{
+ return rgw::auth::Strategy::apply(dpp, auth_registry.get_sts(), s);
+}
+
+void RGWHandler_REST_STS::rgw_sts_parse_input()
{
const auto max_size = s->cct->_conf->rgw_max_put_param_size;
}
}
}
+}
+
+RGWOp *RGWHandler_REST_STS::op_post()
+{
+ rgw_sts_parse_input();
if (s->info.args.exists("Action")) {
string action = s->info.args.get("Action");
return new RGWSTSAssumeRole;
} else if (action == "GetSessionToken") {
return new RGWSTSGetSessionToken;
+ } else if (action == "AssumeRoleWithWebIdentity") {
+ return new RGWSTSAssumeRoleWithWebIdentity;
}
}
return RGWHandler_REST::init(store, s, cio);
}
+int RGWHandler_REST_STS::authorize(const DoutPrefixProvider* dpp)
+{
+ if (s->info.args.exists("Action") && s->info.args.get("Action") == "AssumeRoleWithWebIdentity") {
+ return RGW_Auth_STS::authorize(dpp, store, auth_registry, s);
+ }
+ return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
+}
+
int RGWHandler_REST_STS::init_from_header(struct req_state* s,
int default_formatter,
bool configurable_format)
#ifndef CEPH_RGW_REST_STS_H
#define CEPH_RGW_REST_STS_H
+#include "rgw_auth.h"
+#include "rgw_auth_filters.h"
#include "rgw_sts.h"
+#include "rgw_web_idp.h"
+
+namespace rgw {
+namespace auth {
+namespace sts {
+
+class WebTokenEngine : public rgw::auth::Engine {
+ CephContext* const cct;
+
+ using result_t = rgw::auth::Engine::result_t;
+ using token_t = rgw::web_idp::WebTokenClaims;
+
+ const rgw::auth::TokenExtractor* const extractor;
+ const rgw::auth::WebIdentityApplier::Factory* const apl_factory;
+
+ bool is_applicable(const std::string& token) const noexcept;
+
+ boost::optional<token_t>
+ get_from_idp(const DoutPrefixProvider* dpp, const std::string& token) const;
+
+ result_t authenticate(const DoutPrefixProvider* dpp,
+ const std::string& token,
+ const req_state* s) const;
+
+public:
+ WebTokenEngine(CephContext* const cct,
+ const rgw::auth::TokenExtractor* const extractor,
+ const rgw::auth::WebIdentityApplier::Factory* const apl_factory)
+ : cct(cct),
+ extractor(extractor),
+ apl_factory(apl_factory) {
+ }
+
+ const char* get_name() const noexcept override {
+ return "rgw::auth::sts::WebTokenEngine";
+ }
+
+ result_t authenticate(const DoutPrefixProvider* dpp, const req_state* const s) const override {
+ return authenticate(dpp, extractor->get_token(s), s);
+ }
+}; /* class WebTokenEngine */
+
+class DefaultStrategy : public rgw::auth::Strategy,
+ public rgw::auth::TokenExtractor,
+ public rgw::auth::WebIdentityApplier::Factory {
+ RGWRados* const store;
+
+ /* The engine. */
+ const WebTokenEngine web_token_engine;
+
+ using aplptr_t = rgw::auth::IdentityApplier::aplptr_t;
+
+ /* The method implements TokenExtractor for Web Token in req_state. */
+ std::string get_token(const req_state* const s) const override {
+ return s->info.args.get("WebIdentityToken");
+ }
+
+ aplptr_t create_apl_web_identity( CephContext* cct,
+ const req_state* s,
+ const rgw::web_idp::WebTokenClaims& token) const override {
+ auto apl = rgw::auth::add_sysreq(cct, store, s,
+ rgw::auth::WebIdentityApplier(cct, store, token));
+ return aplptr_t(new decltype(apl)(std::move(apl)));
+ }
+
+public:
+ DefaultStrategy(CephContext* const cct,
+ RGWRados* const store)
+ : store(store),
+ web_token_engine(cct,
+ static_cast<rgw::auth::TokenExtractor*>(this),
+ static_cast<rgw::auth::WebIdentityApplier::Factory*>(this)) {
+ /* When the constructor's body is being executed, all member engines
+ * should be initialized. Thus, we can safely add them. */
+ using Control = rgw::auth::Strategy::Control;
+ add_engine(Control::SUFFICIENT, web_token_engine);
+ }
+
+ const char* get_name() const noexcept override {
+ return "rgw::auth::sts::DefaultStrategy";
+ }
+};
+
+}; /* namespace sts */
+}; /* namespace auth */
+};
class RGWREST_STS : public RGWRESTOp {
protected:
void send_response() override;
};
+class RGWSTSAssumeRoleWithWebIdentity : public RGWREST_STS {
+protected:
+ string duration;
+ string providerId;
+ string policy;
+ string roleArn;
+ string roleSessionName;
+ string sub;
+ string aud;
+ string iss;
+public:
+ RGWSTSAssumeRoleWithWebIdentity() = default;
+ void execute() override;
+ int get_params();
+ const char* name() const override { return "assume_role_web_identity"; }
+ RGWOpType get_type() override { return RGW_STS_ASSUME_ROLE_WEB_IDENTITY; }
+};
+
class RGWSTSAssumeRole : public RGWREST_STS {
protected:
string duration;
RGWOpType get_type() override { return RGW_STS_GET_SESSION_TOKEN; }
};
+class RGW_Auth_STS {
+public:
+ static int authorize(const DoutPrefixProvider *dpp,
+ RGWRados *store,
+ const rgw::auth::StrategyRegistry& auth_registry,
+ struct req_state *s);
+};
+
class RGWHandler_REST_STS : public RGWHandler_REST {
const rgw::auth::StrategyRegistry& auth_registry;
RGWOp *op_post() override;
+ void rgw_sts_parse_input();
public:
+
static int init_from_header(struct req_state *s, int default_formatter, bool configurable_format);
RGWHandler_REST_STS(const rgw::auth::StrategyRegistry& auth_registry)
int init(RGWRados *store,
struct req_state *s,
rgw::io::BasicClient *cio) override;
- int authorize(const DoutPrefixProvider* dpp) override {
- return RGW_Auth_S3::authorize(dpp, store, auth_registry, s);
- }
+ int authorize(const DoutPrefixProvider* dpp) override;
int postauth_init() override { return 0; }
};
return 0;
}
-AssumeRoleRequest::AssumeRoleRequest(string& _duration, string& _externalId, string& _iamPolicy,
- string& _roleArn, string& _roleSessionName, string& _serialNumber,
- string& _tokenCode)
- : externalId(_externalId), iamPolicy(_iamPolicy),
- roleArn(_roleArn), roleSessionName(_roleSessionName),
- serialNumber(_serialNumber), tokenCode(_tokenCode)
+AssumeRoleRequestBase::AssumeRoleRequestBase( const string& duration,
+ const string& iamPolicy,
+ const string& roleArn,
+ const string& roleSessionName)
+ : iamPolicy(iamPolicy), roleArn(roleArn), roleSessionName(roleSessionName)
{
- if (_duration.empty()) {
- duration = DEFAULT_DURATION_IN_SECS;
+ if (duration.empty()) {
+ this->duration = DEFAULT_DURATION_IN_SECS;
} else {
- duration = std::stoull(_duration);
+ this->duration = std::stoull(duration);
}
}
-int AssumeRoleRequest::validate_input() const
+int AssumeRoleRequestBase::validate_input() const
{
if (duration < MIN_DURATION_IN_SECS ||
duration > MAX_DURATION_IN_SECS) {
return -EINVAL;
}
- if (! externalId.empty()) {
- if (externalId.length() < MIN_EXTERNAL_ID_LEN ||
- externalId.length() > MAX_EXTERNAL_ID_LEN) {
- return -EINVAL;
- }
-
- std::regex regex_externalId("[A-Za-z0-9_=,.@:/-]+");
- if (! std::regex_match(externalId, regex_externalId)) {
- return -EINVAL;
- }
- }
if (! iamPolicy.empty() &&
(iamPolicy.size() < MIN_POLICY_SIZE || iamPolicy.size() > MAX_POLICY_SIZE)) {
return -ERR_PACKED_POLICY_TOO_LARGE;
return -EINVAL;
}
}
+
+ return 0;
+}
+
+int AssumeRoleWithWebIdentityRequest::validate_input() const
+{
+ if (! providerId.empty()) {
+ if (providerId.length() < MIN_PROVIDER_ID_LEN ||
+ providerId.length() > MAX_PROVIDER_ID_LEN) {
+ return -EINVAL;
+ }
+ }
+ return AssumeRoleRequestBase::validate_input();
+}
+
+int AssumeRoleRequest::validate_input() const
+{
+ if (! externalId.empty()) {
+ if (externalId.length() < MIN_EXTERNAL_ID_LEN ||
+ externalId.length() > MAX_EXTERNAL_ID_LEN) {
+ return -EINVAL;
+ }
+
+ std::regex regex_externalId("[A-Za-z0-9_=,.@:/-]+");
+ if (! std::regex_match(externalId, regex_externalId)) {
+ return -EINVAL;
+ }
+ }
if (! serialNumber.empty()){
if (serialNumber.size() < MIN_SERIAL_NUMBER_SIZE || serialNumber.size() > MAX_SERIAL_NUMBER_SIZE) {
return -EINVAL;
return -EINVAL;
}
- return 0;
+ return AssumeRoleRequestBase::validate_input();
}
std::tuple<int, RGWRole> STSService::getRoleInfo(const string& arn)
return ret;
}
+AssumeRoleWithWebIdentityResponse STSService::assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest& req)
+{
+ AssumeRoleWithWebIdentityResponse response;
+ response.assumeRoleResp.packedPolicySize = 0;
+
+ if (req.getProviderId().empty()) {
+ response.providerId = req.getIss();
+ }
+ response.aud = req.getAud();
+ response.sub = req.getSub();
+
+ //Get the role info which is being assumed
+ boost::optional<rgw::IAM::ARN> r_arn = rgw::IAM::ARN::parse(req.getRoleARN());
+ if (r_arn == boost::none) {
+ response.assumeRoleResp.retCode = -EINVAL;
+ return response;
+ }
+
+ string roleId = role.get_id();
+ uint64_t roleMaxSessionDuration = role.get_max_session_duration();
+ req.setMaxDuration(roleMaxSessionDuration);
+
+ //Validate input
+ response.assumeRoleResp.retCode = req.validate_input();
+ if (response.assumeRoleResp.retCode < 0) {
+ return response;
+ }
+
+ //Calculate PackedPolicySize
+ string policy = req.getPolicy();
+ response.assumeRoleResp.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
+
+ //Generate Assumed Role User
+ response.assumeRoleResp.retCode = response.assumeRoleResp.user.generateAssumedRoleUser(cct,
+ store,
+ roleId,
+ r_arn.get(),
+ req.getRoleSessionName());
+ if (response.assumeRoleResp.retCode < 0) {
+ return response;
+ }
+
+ //Generate Credentials
+ //Role and Policy provide the authorization info, user id and applier info are not needed
+ response.assumeRoleResp.retCode = response.assumeRoleResp.creds.generateCredentials(cct, req.getDuration(),
+ req.getPolicy(), roleId,
+ user_id, nullptr);
+ if (response.assumeRoleResp.retCode < 0) {
+ return response;
+ }
+
+ response.assumeRoleResp.retCode = 0;
+ return response;
+}
+
AssumeRoleResponse STSService::assumeRole(AssumeRoleRequest& req)
{
- uint64_t packedPolicySize = 0, roleMaxSessionDuration = 0;
- AssumedRoleUser user;
- Credentials cred;
- string roleId;
+ AssumeRoleResponse response;
+ response.packedPolicySize = 0;
//Get the role info which is being assumed
- boost::optional<rgw::IAM::ARN> r_arn;
- if (r_arn = rgw::IAM::ARN::parse(req.getRoleARN()); r_arn == boost::none) {
- return make_tuple(-EINVAL, user, cred, packedPolicySize);
+ boost::optional<rgw::IAM::ARN> r_arn = rgw::IAM::ARN::parse(req.getRoleARN());
+ if (r_arn == boost::none) {
+ response.retCode = -EINVAL;
+ return response;
}
- roleId = role.get_id();
- roleMaxSessionDuration = role.get_max_session_duration();
+ string roleId = role.get_id();
+ uint64_t roleMaxSessionDuration = role.get_max_session_duration();
req.setMaxDuration(roleMaxSessionDuration);
//Validate input
- int ret = 0;
- if (ret = req.validate_input(); ret < 0) {
- return make_tuple(ret, user, cred, packedPolicySize);
+ response.retCode = req.validate_input();
+ if (response.retCode < 0) {
+ return response;
}
//Calculate PackedPolicySize
string policy = req.getPolicy();
- packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
+ response.packedPolicySize = (policy.size() / req.getMaxPolicySize()) * 100;
//Generate Assumed Role User
- if (ret = user.generateAssumedRoleUser(cct, store, roleId, r_arn.get(), req.getRoleSessionName()); ret < 0) {
- return make_tuple(ret, user, cred, packedPolicySize);
+ response.retCode = response.user.generateAssumedRoleUser(cct, store, roleId, r_arn.get(), req.getRoleSessionName());
+ if (response.retCode < 0) {
+ return response;
}
//Generate Credentials
//Role and Policy provide the authorization info, user id and applier info are not needed
- if (ret = cred.generateCredentials(cct, req.getDuration(),
- req.getPolicy(), roleId,
- user_id, nullptr); ret < 0) {
- return make_tuple(ret, user, cred, packedPolicySize);
+ response.retCode = response.creds.generateCredentials(cct, req.getDuration(),
+ req.getPolicy(), roleId,
+ user_id, nullptr);
+ if (response.retCode < 0) {
+ return response;
}
//Save ARN with the user
- string arn = user.getARN();
- if (ret = storeARN(arn); ret < 0) {
- return make_tuple(ret, user, cred, packedPolicySize);
+ string arn = response.user.getARN();
+ response.retCode = storeARN(arn);
+ if (response.retCode < 0) {
+ return response;
}
- return make_tuple(0, user, cred, packedPolicySize);
+ response.retCode = 0;
+ return response;
}
-GetSessionTokenRequest::GetSessionTokenRequest(string& duration, string& serialNumber, string& tokenCode)
+GetSessionTokenRequest::GetSessionTokenRequest(const string& duration, const string& serialNumber, const string& tokenCode)
{
if (duration.empty()) {
this->duration = DEFAULT_DURATION_IN_SECS;
#include "rgw_role.h"
#include "rgw_auth.h"
+#include "rgw_web_idp.h"
namespace STS {
-class AssumeRoleRequest {
+class AssumeRoleRequestBase {
+protected:
static constexpr uint64_t MIN_POLICY_SIZE = 1;
static constexpr uint64_t MAX_POLICY_SIZE = 2048;
static constexpr uint64_t DEFAULT_DURATION_IN_SECS = 3600;
static constexpr uint64_t MIN_DURATION_IN_SECS = 900;
- static constexpr uint64_t MIN_EXTERNAL_ID_LEN = 2;
- static constexpr uint64_t MAX_EXTERNAL_ID_LEN = 1224;
static constexpr uint64_t MIN_ROLE_ARN_SIZE = 2;
static constexpr uint64_t MAX_ROLE_ARN_SIZE = 2048;
static constexpr uint64_t MIN_ROLE_SESSION_SIZE = 2;
static constexpr uint64_t MAX_ROLE_SESSION_SIZE = 64;
- static constexpr uint64_t MIN_SERIAL_NUMBER_SIZE = 9;
- static constexpr uint64_t MAX_SERIAL_NUMBER_SIZE = 256;
- static constexpr uint64_t TOKEN_CODE_SIZE = 6;
uint64_t MAX_DURATION_IN_SECS;
uint64_t duration;
- string externalId;
string iamPolicy;
string roleArn;
string roleSessionName;
- string serialNumber;
- string tokenCode;
public:
- AssumeRoleRequest( string& _duration,
- string& _externalId,
- string& _iamPolicy,
- string& _roleArn,
- string& _roleSessionName,
- string& _serialNumber,
- string& _tokenCode);
+ AssumeRoleRequestBase(const string& duration,
+ const string& iamPolicy,
+ const string& roleArn,
+ const string& roleSessionName);
const string& getRoleARN() const { return roleArn; }
const string& getRoleSessionName() const { return roleSessionName; }
const string& getPolicy() const {return iamPolicy; }
static const uint64_t& getMaxPolicySize() { return MAX_POLICY_SIZE; }
void setMaxDuration(const uint64_t& maxDuration) { MAX_DURATION_IN_SECS = maxDuration; }
- uint64_t& getDuration() { return duration; }
+ const uint64_t& getDuration() const { return duration; }
+ int validate_input() const;
+};
+
+class AssumeRoleWithWebIdentityRequest : public AssumeRoleRequestBase {
+ static constexpr uint64_t MIN_PROVIDER_ID_LEN = 4;
+ static constexpr uint64_t MAX_PROVIDER_ID_LEN = 2048;
+ string providerId;
+ string iamPolicy;
+ string iss;
+ string sub;
+ string aud;
+public:
+ AssumeRoleWithWebIdentityRequest( const string& duration,
+ const string& providerId,
+ const string& iamPolicy,
+ const string& roleArn,
+ const string& roleSessionName,
+ const string& iss,
+ const string& sub,
+ const string& aud)
+ : AssumeRoleRequestBase(duration, iamPolicy, roleArn, roleSessionName),
+ providerId(providerId), iss(iss), sub(sub), aud(aud) {}
+ const string& getProviderId() const { return providerId; }
+ const string& getIss() const { return iss; }
+ const string& getAud() const { return aud; }
+ const string& getSub() const { return sub; }
+ int validate_input() const;
+};
+
+class AssumeRoleRequest : public AssumeRoleRequestBase {
+ static constexpr uint64_t MIN_EXTERNAL_ID_LEN = 2;
+ static constexpr uint64_t MAX_EXTERNAL_ID_LEN = 1224;
+ static constexpr uint64_t MIN_SERIAL_NUMBER_SIZE = 9;
+ static constexpr uint64_t MAX_SERIAL_NUMBER_SIZE = 256;
+ static constexpr uint64_t TOKEN_CODE_SIZE = 6;
+ string externalId;
+ string serialNumber;
+ string tokenCode;
+public:
+ AssumeRoleRequest(const string& duration,
+ const string& externalId,
+ const string& iamPolicy,
+ const string& roleArn,
+ const string& roleSessionName,
+ const string& serialNumber,
+ const string& tokenCode)
+ : AssumeRoleRequestBase(duration, iamPolicy, roleArn, roleSessionName),
+ externalId(externalId), serialNumber(serialNumber), tokenCode(tokenCode){}
int validate_input() const;
};
string tokenCode;
public:
- GetSessionTokenRequest(string& duration, string& serialNumber, string& tokenCode);
+ GetSessionTokenRequest(const string& duration, const string& serialNumber, const string& tokenCode);
const uint64_t& getDuration() const { return duration; }
static const uint64_t& getMinDuration() { return MIN_DURATION_IN_SECS; }
void dump(Formatter *f) const;
};
-//AssumedRoleUser, Credentials, PackedpolicySize
-using AssumeRoleResponse = std::tuple<int, AssumedRoleUser, Credentials, uint64_t> ;
+struct AssumeRoleResponse {
+ int retCode;
+ AssumedRoleUser user;
+ Credentials creds;
+ uint64_t packedPolicySize;
+};
+
+struct AssumeRoleWithWebIdentityResponse {
+ AssumeRoleResponse assumeRoleResp;
+ string aud;
+ string providerId;
+ string sub;
+};
+
+using AssumeRoleResponse = struct AssumeRoleResponse ;
using GetSessionTokenResponse = std::tuple<int, Credentials>;
+using AssumeRoleWithWebIdentityResponse = struct AssumeRoleWithWebIdentityResponse;
class STSService {
CephContext* cct;
std::tuple<int, RGWRole> getRoleInfo(const string& arn);
AssumeRoleResponse assumeRole(AssumeRoleRequest& req);
GetSessionTokenResponse getSessionToken(GetSessionTokenRequest& req);
+ AssumeRoleWithWebIdentityResponse assumeRoleWithWebIdentity(AssumeRoleWithWebIdentityRequest& req);
};
}
#endif /* CEPH_RGW_STS_H */
--- /dev/null
+#ifndef CEPH_RGW_WEB_IDP_H
+#define CEPH_RGW_WEB_IDP_H
+
+#include <utility>
+#include <boost/optional.hpp>
+#include <boost/utility/string_view.hpp>
+
+#include "rgw_auth.h"
+#include "rgw_common.h"
+
+namespace rgw {
+namespace web_idp {
+
+//WebToken contains some claims from the decoded token which are of interest to us.
+struct WebTokenClaims {
+ //Subject of the token
+ string sub;
+ //Intended audience for this token
+ string aud;
+ //Issuer of this token
+ string iss;
+ //Human-readable id for the resource owner
+ string user_name;
+};
+
+}; /* namespace web_idp */
+}; /* namespace rgw */
+
+#endif /* CEPH_RGW_WEB_IDP_H */
rgw_env.set("REMOTE_ADDR", "192.168.1.1");
rgw_env.set("HTTP_HOST", "1.2.3.4");
req_state rgw_req_state(cct.get(), &rgw_env, &user, 0);
- Environment iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
- auto ip = iam_env.find("aws:SourceIp");
- ASSERT_NE(ip, iam_env.end());
+ rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+ auto ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
EXPECT_EQ(ip->second, "192.168.1.1");
ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "SOME_VAR"), 0);
EXPECT_EQ(cct.get()->_conf->rgw_remote_addr_param, "SOME_VAR");
- iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
- ip = iam_env.find("aws:SourceIp");
- EXPECT_EQ(ip, iam_env.end());
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ EXPECT_EQ(ip, rgw_req_state.env.end());
rgw_env.set("SOME_VAR", "192.168.1.2");
- iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
- ip = iam_env.find("aws:SourceIp");
- ASSERT_NE(ip, iam_env.end());
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
EXPECT_EQ(ip->second, "192.168.1.2");
ASSERT_EQ(cct.get()->_conf.set_val("rgw_remote_addr_param", "HTTP_X_FORWARDED_FOR"), 0);
rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.3");
- iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
- ip = iam_env.find("aws:SourceIp");
- ASSERT_NE(ip, iam_env.end());
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
EXPECT_EQ(ip->second, "192.168.1.3");
rgw_env.set("HTTP_X_FORWARDED_FOR", "192.168.1.4, 4.3.2.1, 2001:db8:85a3:8d3:1319:8a2e:370:7348");
- iam_env = rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
- ip = iam_env.find("aws:SourceIp");
- ASSERT_NE(ip, iam_env.end());
+ rgw_req_state.env.clear();
+ rgw_build_iam_environment(&rgw_rados, &rgw_req_state);
+ ip = rgw_req_state.env.find("aws:SourceIp");
+ ASSERT_NE(ip, rgw_req_state.env.end());
EXPECT_EQ(ip->second, "192.168.1.4");
}