throw std::bad_cast();
return val.get<double>();
}
+ /**
+ * Get the contained object as an object
+ * \return content as object
+ * \throws std::bad_cast Content was not an object
+ */
+ const picojson::object& as_object() const {
+ if (!val.is<picojson::object>())
+ throw std::bad_cast();
+ return val.get<picojson::object>();
+ }
};
/**
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 << ")";
+ out << "rgw::auth::WebIdentityApplier(sub =" << sub
+ << ", user_name=" << user_name
+ << ", provider_id =" << iss << ")";
}
string rgw::auth::WebIdentityApplier::get_idp_url() const
{
- string idp_url = token_claims.iss;
+ string idp_url = this->iss;
idp_url = url_remove_prefix(idp_url);
return idp_url;
}
void rgw::auth::WebIdentityApplier::load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const {
rgw_user federated_user;
- federated_user.id = token_claims.sub;
+ federated_user.id = this->sub;
federated_user.tenant = role_tenant;
federated_user.ns = "oidc";
}
ldpp_dout(dpp, 0) << "NOTICE: couldn't map oidc federated user " << federated_user << dendl;
- create_account(dpp, federated_user, token_claims.user_name, user_info);
+ create_account(dpp, federated_user, this->user_name, user_info);
}
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);
- s->info.args.append("client_id", token_claims.client_id);
+ s->info.args.append("sub", this->sub);
+ s->info.args.append("aud", this->aud);
+ s->info.args.append("provider_id", this->iss);
+ s->info.args.append("client_id", this->client_id);
+ string condition;
string idp_url = get_idp_url();
- string condition = idp_url + ":app_id";
-
- s->env.emplace(condition, token_claims.aud);
-
- condition.clear();
- condition = idp_url + ":sub";
- s->env.emplace(condition, token_claims.sub);
+ for (auto& claim : token_claims) {
+ if (claim.first == "aud") {
+ condition.clear();
+ condition = idp_url + ":app_id";
+ s->env.emplace(condition, claim.second);
+ }
+ condition.clear();
+ condition = idp_url + ":" + claim.first;
+ s->env.emplace(condition, claim.second);
+ }
}
bool rgw::auth::WebIdentityApplier::is_identity(const idset_t& ids) const
class StrategyRegistry;
class WebIdentityApplier : public IdentityApplier {
+ std::string sub;
+ std::string iss;
+ std::string aud;
+ std::string client_id;
+ std::string user_name;
protected:
CephContext* const cct;
rgw::sal::Store* store;
std::string role_session;
std::string role_tenant;
- rgw::web_idp::WebTokenClaims token_claims;
+ std::unordered_multimap<std::string, std::string> token_claims;
std::string get_idp_url() const;
rgw::sal::Store* store,
const std::string& role_session,
const std::string& role_tenant,
- const rgw::web_idp::WebTokenClaims& token_claims)
- : cct(cct),
+ const std::unordered_multimap<std::string, std::string>& token_claims)
+ : cct(cct),
store(store),
role_session(role_session),
role_tenant(role_tenant),
token_claims(token_claims) {
+ const auto& sub = token_claims.find("sub");
+ if(sub != token_claims.end()) {
+ this->sub = sub->second;
+ }
+
+ const auto& iss = token_claims.find("iss");
+ if(iss != token_claims.end()) {
+ this->iss = iss->second;
+ }
+
+ const auto& aud = token_claims.find("aud");
+ if(aud != token_claims.end()) {
+ this->aud = aud->second;
+ }
+
+ const auto& client_id = token_claims.find("client_id");
+ if(client_id != token_claims.end()) {
+ this->client_id = client_id->second;
+ } else {
+ const auto& azp = token_claims.find("azp");
+ if (azp != token_claims.end()) {
+ this->client_id = azp->second;
+ }
+ }
+
+ const auto& user_name = token_claims.find("username");
+ if(user_name != token_claims.end()) {
+ this->user_name = user_name->second;
+ } else {
+ const auto& given_username = token_claims.find("given_username");
+ if (given_username != token_claims.end()) {
+ this->user_name = given_username->second;
+ }
+ }
}
void modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const override;
}
bool is_owner_of(const rgw_user& uid) const override {
- if (uid.id == token_claims.sub && uid.tenant == role_tenant && uid.ns == "oidc") {
+ if (uid.id == this->sub && uid.tenant == role_tenant && uid.ns == "oidc") {
return true;
}
return false;
}
std::string get_acct_name() const override {
- return token_claims.user_name;
+ return this->user_name;
}
std::string get_subuser() const override {
const req_state* s,
const std::string& role_session,
const std::string& role_tenant,
- const rgw::web_idp::WebTokenClaims& token) const = 0;
+ const std::unordered_multimap<std::string, std::string>& token) const = 0;
};
};
}
const auto& s = i->second;
+ const auto& itr = env.equal_range(key);
+
switch (op) {
// String!
case TokenID::StringEquals:
- return orrible(std::equal_to<std::string>(), s, vals);
+ return orrible(std::equal_to<std::string>(), itr, vals);
case TokenID::StringNotEquals:
return orrible(std::not_fn(std::equal_to<std::string>()),
- s, vals);
+ itr, vals);
case TokenID::StringEqualsIgnoreCase:
- return orrible(ci_equal_to(), s, vals);
+ return orrible(ci_equal_to(), itr, vals);
case TokenID::StringNotEqualsIgnoreCase:
- return orrible(std::not_fn(ci_equal_to()), s, vals);
+ return orrible(std::not_fn(ci_equal_to()), itr, vals);
case TokenID::StringLike:
- return orrible(string_like(), s, vals);
+ return orrible(string_like(), itr, vals);
case TokenID::StringNotLike:
- return orrible(std::not_fn(string_like()), s, vals);
+ return orrible(std::not_fn(string_like()), itr, vals);
// Numeric
case TokenID::NumericEquals:
Other
};
-using Environment = boost::container::flat_map<std::string, std::string>;
+using Environment = std::unordered_multimap<std::string, std::string>;
using Address = std::bitset<128>;
struct MaskedIP {
}
};
+ using unordered_multimap_it_pair = std::pair <std::unordered_multimap<std::string,std::string>::const_iterator, std::unordered_multimap<std::string,std::string>::const_iterator>;
template<typename F>
- static bool orrible(F&& f, const std::string& c,
+ static bool orrible(F&& f, const unordered_multimap_it_pair& it,
const std::vector<std::string>& v) {
- for (const auto& d : v) {
- if (std::forward<F>(f)(c, d)) {
- return true;
+ for (auto itr = it.first; itr != it.second; itr++) {
+ for (const auto& d : v) {
+ if (std::forward<F>(f)(itr->second, d)) {
+ return true;
}
+ }
}
return false;
}
for (const auto& c: acl_header_conditionals){
auto hdr = s->info.env->get(c.first);
if(hdr) {
- e[c.second] = hdr;
+ e.emplace(c.second, hdr);
}
}
}
return false;
}
+void
+WebTokenEngine::recurse_and_insert(const string& key, const jwt::claim& c, std::unordered_multimap<string, string>& token) const
+{
+ string s_val;
+ jwt::claim::type t = c.get_type();
+ switch(t) {
+ case jwt::claim::type::null:
+ break;
+ case jwt::claim::type::boolean:
+ case jwt::claim::type::number:
+ case jwt::claim::type::int64:
+ {
+ s_val = c.to_json().serialize();
+ token.emplace(key, s_val);
+ break;
+ }
+ case jwt::claim::type::string:
+ {
+ s_val = c.to_json().to_str();
+ token.emplace(key, s_val);
+ break;
+ }
+ case jwt::claim::type::array:
+ {
+ const picojson::array& arr = c.as_array();
+ for (auto& a : arr) {
+ recurse_and_insert(key, jwt::claim(a), token);
+ }
+ break;
+ }
+ case jwt::claim::type::object:
+ {
+ const picojson::object& obj = c.as_object();
+ for (auto& m : obj) {
+ recurse_and_insert(m.first, jwt::claim(m.second), token);
+ }
+ break;
+ }
+ }
+ return;
+}
+
+//Extract all token claims so that they can be later used in the Condition element of Role's trust policy
+std::unordered_multimap<string,string>
+WebTokenEngine::get_token_claims(const jwt::decoded_jwt& decoded) const
+{
+ std::unordered_multimap<string, string> token;
+ const auto& claims = decoded.get_payload_claims();
+
+ for (auto& c : claims) {
+ recurse_and_insert(c.first, c.second, token);
+ }
+ return token;
+}
+
//Offline validation of incoming Web Token which is a signed JWT (JSON Web Token)
boost::optional<WebTokenEngine::token_t>
WebTokenEngine::get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s,
auto& payload = decoded.get_payload();
ldpp_dout(dpp, 20) << " payload = " << payload << dendl;
+
+ t = get_token_claims(decoded);
+
+ string iss;
if (decoded.has_issuer()) {
- t.iss = decoded.get_issuer();
+ iss = decoded.get_issuer();
}
+
+ set<string> aud;
if (decoded.has_audience()) {
- auto aud = decoded.get_audience();
- t.aud = *(aud.begin());
- }
- if (decoded.has_subject()) {
- t.sub = decoded.get_subject();
+ aud = decoded.get_audience();
}
+
+ string client_id;
if (decoded.has_payload_claim("client_id")) {
- t.client_id = decoded.get_payload_claim("client_id").as_string();
+ client_id = decoded.get_payload_claim("client_id").as_string();
}
- if (t.client_id.empty() && decoded.has_payload_claim("clientId")) {
- t.client_id = decoded.get_payload_claim("clientId").as_string();
+ if (client_id.empty() && decoded.has_payload_claim("clientId")) {
+ client_id = decoded.get_payload_claim("clientId").as_string();
+ }
+ string azp;
+ if (decoded.has_payload_claim("azp")) {
+ azp = decoded.get_payload_claim("azp").as_string();
}
string role_arn = s->info.args.get("RoleArn");
- auto provider = get_provider(dpp, role_arn, t.iss);
+ auto provider = get_provider(dpp, role_arn, iss);
if (! provider) {
- ldpp_dout(dpp, 0) << "Couldn't get oidc provider info using input iss" << t.iss << dendl;
+ ldpp_dout(dpp, 0) << "Couldn't get oidc provider info using input iss" << iss << dendl;
throw -EACCES;
}
vector<string> client_ids = provider->get_client_ids();
vector<string> thumbprints = provider->get_thumbprints();
if (! client_ids.empty()) {
- if (! is_client_id_valid(client_ids, t.client_id) && ! is_client_id_valid(client_ids, t.aud)) {
+ bool found = false;
+ for (auto& it : aud) {
+ if (is_client_id_valid(client_ids, it)) {
+ found = true;
+ break;
+ }
+ }
+ if (! found && ! is_client_id_valid(client_ids, client_id) && ! is_client_id_valid(client_ids, azp)) {
ldpp_dout(dpp, 0) << "Client id in token doesn't match with that registered with oidc provider" << dendl;
throw -EACCES;
}
if (decoded.has_algorithm()) {
auto& algorithm = decoded.get_algorithm();
try {
- validate_signature(dpp, decoded, algorithm, t.iss, thumbprints, y);
+ validate_signature(dpp, decoded, algorithm, iss, thumbprints, y);
} catch (...) {
throw -EACCES;
}
rgw::sal::Store* store;
using result_t = rgw::auth::Engine::result_t;
- using token_t = rgw::web_idp::WebTokenClaims;
+ using token_t = std::unordered_multimap<string,string>;
const rgw::auth::TokenExtractor* const extractor;
const rgw::auth::WebIdentityApplier::Factory* const apl_factory;
const std::string& token,
const req_state* s, optional_yield y) const;
+ void recurse_and_insert(const string& key, const jwt::claim& c, std::unordered_multimap<string, string>& token) const;
+ std::unordered_multimap<string,string> get_token_claims(const jwt::decoded_jwt& decoded) const;
+
public:
WebTokenEngine(CephContext* const cct,
rgw::sal::Store* store,
const req_state* s,
const std::string& role_session,
const std::string& role_tenant,
- const rgw::web_idp::WebTokenClaims& token) const override {
+ const std::unordered_multimap<string, string>& token) const override {
auto apl = rgw::auth::add_sysreq(cct, store, s,
rgw::auth::WebIdentityApplier(cct, store, role_session, role_tenant, token));
return aplptr_t(new decltype(apl)(std::move(apl)));
bufferlist::static_from_string(ip_address_full_example));
Environment e;
Environment allowedIP, blocklistedIP, allowedIPv6, blocklistedIPv6;
- allowedIP["aws:SourceIp"] = "192.168.1.2";
- allowedIPv6["aws:SourceIp"] = "::1";
- blocklistedIP["aws:SourceIp"] = "192.168.1.1";
- blocklistedIPv6["aws:SourceIp"] = "2001:0db8:85a3:0000:0000:8a2e:0370:7334";
+ allowedIP.emplace("aws:SourceIp","192.168.1.2");
+ allowedIPv6.emplace("aws:SourceIp", "::1");
+ blocklistedIP.emplace("aws:SourceIp", "192.168.1.1");
+ blocklistedIPv6.emplace("aws:SourceIp", "2001:0db8:85a3:0000:0000:8a2e:0370:7334");
auto trueacct = FakeIdentity(
Principal::tenant("ACCOUNT-ID-WITHOUT-HYPHENS"));
)";
DEFINE_REQ_STATE;
- s.env[""] = "world";
- s.env[""] = "bar";
- s.env["goodbye"] = "cruel world";
- s.env["ka"] = "boom";
+ s.env.emplace("", "bar");
+ s.env.emplace("goodbye", "cruel world");
+ s.env.emplace("ka", "boom");
const auto rc = lua::request::execute(nullptr, nullptr, nullptr, &s, "put_obj", script);
ASSERT_EQ(rc, 0);