If the default provider is required when also using custom providers,
it must be explicitly loaded in the configuration file or code (see https://github.com/openssl/openssl/blob/master/README-PROVIDERS.md).
+* RGW: When the `rgw_copious_policy_logging` variable is set to true
+ and rgw logging is set to 20, a detailed trace of policy
+ evaluation is logged.
+
* DASHBOARD: Removed the older landing page which was deprecated in Quincy.
Admins can no longer enable the older, deprecated landing page layout by
adjusting FEATURE_TOGGLE_DASHBOARD.
default: 11
services:
- rgw
+
- name: rgw_usage_log_key_transition
type: bool
level: advanced
see_also:
- rgw_enable_usage_log
with_legacy: true
+
+- name: rgw_copious_policy_logging
+ type: bool
+ level: advanced
+ desc: Log the details of each policy evaluation in extreme verbosity
+ default: false
+ services:
+ - rgw
+ with_legacy: false
const auto source_account = to_string(s->bucket_owner.id);
s->env.emplace("aws:SourceArn", source_bucket_arn);
s->env.emplace("aws:SourceAccount", source_account);
- if (policy.eval(s->env, ident, rgw::IAM::s3PutObject, target_resource_arn) != rgw::IAM::Effect::Allow) {
+ if (policy.eval(dpp, s->env, ident, rgw::IAM::s3PutObject, target_resource_arn) != rgw::IAM::Effect::Allow) {
ldpp_dout(dpp, 1) << "ERROR: logging bucket: '" << target_bucket_id <<
"' must have a bucket policy that allows logging service principal to put objects in the following resource ARN: '" <<
target_resource_arn.to_string() << "' from source bucket ARN: '" << source_bucket_arn <<
const uint64_t op,
const ARN& resource,
boost::optional<rgw::IAM::PolicyPrincipal&> princ_type=boost::none) {
- if (!policy)
+ if (!policy) {
return Effect::Pass;
- else
- return policy->eval(env, id, op, resource, princ_type);
+ } else {
+ return policy->eval(dpp, env, id, op, resource, princ_type);
+ }
}
Effect eval_identity_or_session_policies(const DoutPrefixProvider* dpp,
// If RestrictPublicBuckets is enabled and the bucket policy allows public access,
// deny the request if the requester is not in the bucket owner account
const bool restrict_public_buckets = s->bucket_access_conf && s->bucket_access_conf->restrict_public_buckets();
- if (restrict_public_buckets && bucket_policy && rgw::IAM::is_public(*bucket_policy) && !s->identity->is_owner_of(s->bucket_info.owner)) {
+ if (restrict_public_buckets && bucket_policy &&
+ rgw::IAM::is_public(dpp, *bucket_policy) &&
+ !s->identity->is_owner_of(s->bucket_info.owner)) {
ldpp_dout(dpp, 10) << __func__ << ": public policies are blocked by the RestrictPublicBuckets block public access setting" << dendl;
return false;
}
// If RestrictPublicBuckets is enabled and the bucket policy allows public access,
// deny the request if the requester is not in the bucket owner account
const bool restrict_public_buckets = ps->bucket_access_conf && ps->bucket_access_conf->restrict_public_buckets();
- if (restrict_public_buckets && bucket_policy && rgw::IAM::is_public(*bucket_policy) && !ps->identity->is_owner_of(ps->bucket_info.owner)) {
+ if (restrict_public_buckets && bucket_policy &&
+ rgw::IAM::is_public(dpp, *bucket_policy) &&
+ !ps->identity->is_owner_of(ps->bucket_info.owner)) {
ldpp_dout(dpp, 10) << __func__ << ": public policies are blocked by the RestrictPublicBuckets block public access setting" << dendl;
return false;
}
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*-
// vim: ts=8 sw=2 sts=2 expandtab ft=cpp
-
#include <cstring>
#include <iostream>
#include <regex>
#include <sstream>
-#include <stack>
#include <utility>
#include <arpa/inet.h>
#include "rapidjson/reader.h"
-#include "include/expected.hpp"
-
#include "rgw_auth.h"
#include "rgw_iam_policy.h"
-namespace {
-constexpr int dout_subsys = ceph_subsys_rgw;
-}
+inline constexpr int dout_subsys = ceph_subsys_rgw;
using std::dec;
using std::hex;
using rgw::auth::Principal;
+namespace std {
+inline std::ostream&
+operator<<(
+ std::ostream& out,
+ const pair<
+ unordered_multimap<string, string>::const_iterator,
+ unordered_multimap<string, string>::const_iterator> it)
+{
+ out << "[";
+ for (auto itr = it.first; itr != it.second; itr++) {
+ if (itr != it.first) {
+ out << ", ";
+ }
+ out << *itr;
+ }
+ return out << "]";
+}
+} // namespace std
+
namespace rgw {
namespace IAM {
#include "rgw_iam_policy_keywords.frag.cc"
{ "organizations:ListTargetsForPolicy", organizationsListTargetsForPolicy},
};
+namespace {
+const char* condop_string(const TokenID t) {
+ switch (t) {
+ case TokenID::StringEquals:
+ return "StringEquals";
+
+ case TokenID::StringNotEquals:
+ return "StringNotEquals";
+
+ case TokenID::StringEqualsIgnoreCase:
+ return "StringEqualsIgnoreCase";
+
+ case TokenID::StringNotEqualsIgnoreCase:
+ return "StringNotEqualsIgnoreCase";
+
+ case TokenID::StringLike:
+ return "StringLike";
+
+ case TokenID::StringNotLike:
+ return "StringNotLike";
+
+ // Numeric!
+ case TokenID::NumericEquals:
+ return "NumericEquals";
+
+ case TokenID::NumericNotEquals:
+ return "NumericNotEquals";
+
+ case TokenID::NumericLessThan:
+ return "NumericLessThan";
+
+ case TokenID::NumericLessThanEquals:
+ return "NumericLessThanEquals";
+
+ case TokenID::NumericGreaterThan:
+ return "NumericGreaterThan";
+
+ case TokenID::NumericGreaterThanEquals:
+ return "NumericGreaterThanEquals";
+
+ case TokenID::DateEquals:
+ return "DateEquals";
+
+ case TokenID::DateNotEquals:
+ return "DateNotEquals";
+
+ case TokenID::DateLessThan:
+ return "DateLessThan";
+
+ case TokenID::DateLessThanEquals:
+ return "DateLessThanEquals";
+
+ case TokenID::DateGreaterThan:
+ return "DateGreaterThan";
+
+ case TokenID::DateGreaterThanEquals:
+ return "DateGreaterThanEquals";
+
+ case TokenID::Bool:
+ return "Bool";
+
+ case TokenID::BinaryEquals:
+ return "BinaryEquals";
+
+ case TokenID::IpAddress:
+ return "case TokenID::IpAddress";
+
+ case TokenID::NotIpAddress:
+ return "NotIpAddress";
+
+ case TokenID::ArnEquals:
+ return "ArnEquals";
+
+ case TokenID::ArnNotEquals:
+ return "ArnNotEquals";
+
+ case TokenID::ArnLike:
+ return "ArnLike";
+
+ case TokenID::ArnNotLike:
+ return "ArnNotLike";
+
+ case TokenID::Null:
+ return "Null";
+
+ default:
+ return "InvalidConditionOperator";
+ }
+}
+}
+
+template <typename T>
+inline std::ostream&
+operator<<(std::ostream& out, const boost::optional<T>& t)
+{
+ if (!t)
+ out << "--";
+ else
+ out << ' ' << *t;
+ return out;
+}
+
+inline std::ostream&
+operator<<(std::ostream& out, const Effect& e)
+{
+ switch (e) {
+ case Effect::Allow:
+ return out << "Allow";
+ case Effect::Pass:
+ return out << "Pass";
+ case Effect::Deny:
+ return out << "Deny";
+ }
+ return out << "Unknown Effect";
+}
+
+void
+maybeout::lendl_() const
+{
+ *this << lendl;
+}
+
+void
+maybeout::bracket::close()
+{
+ if (m && !closer.empty()) {
+ *m.m << closer;
+ m.lendl_();
+ closer = std::string_view{};
+ }
+}
+
+
struct PolicyParser;
const Keyword top[1]{{"<Top>", TokenKind::pseudo, TokenID::Top, 0, false,
return match_policy(pattern, input, MATCH_POLICY_ARN);
}
-bool Condition::eval(const Environment& env) const {
+bool Condition::eval(const Environment& env, const maybeout& eval_log) const {
+ eval_log << "Evaluating condition " << *this << lendl;
std::vector<std::string> runtime_vals;
auto i = env.find(key);
if (op == TokenID::Null) {
- return i == env.end() ? true : false;
+ auto ret = i == env.end() ? true : false;
+ eval_log << "Null check. key `" << key
+ << (ret ?
+ "`. Is not present, returning true." :
+ "`. Is present, returning false.") << lendl;
+ return ret;
}
if (i == env.end()) {
if (op == TokenID::ForAllValuesStringEquals ||
op == TokenID::ForAllValuesStringEqualsIgnoreCase ||
op == TokenID::ForAllValuesStringLike) {
+ eval_log
+ << "Evaluating " << condop_string(op) << " When key "
+ << " is not present. Vacuously true." << lendl;
return true;
} else {
+ eval_log << "Evaluating " << condop_string(op) << " When key `" << key
+ << "` is not present. Returning "
+ << (ifexists ? "true" : "false") << lendl;
return ifexists;
}
}
const auto& itr = env.equal_range(key);
+ eval_log << "Evaluating " << condop_string(op) << "for " << itr << " against "
+ << (isruntime ? runtime_vals : vals) << lendl;
switch (op) {
// String!
case TokenID::ForAnyValueStringEquals:
case TokenID::StringEquals:
- return multimap_any(std::equal_to<std::string>(), itr, isruntime? runtime_vals : vals);
+ return multimap_any(std::equal_to<std::string>(), itr,
+ isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::StringNotEquals:
- return multimap_none(std::equal_to<std::string>(),
- itr, isruntime? runtime_vals : vals);
+ return multimap_none(std::equal_to<std::string>(), itr,
+ isruntime ? runtime_vals : vals, eval_log);
case TokenID::ForAnyValueStringEqualsIgnoreCase:
case TokenID::StringEqualsIgnoreCase:
- return multimap_any(ci_equal_to(), itr, isruntime? runtime_vals : vals);
+ return multimap_any(ci_equal_to(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::StringNotEqualsIgnoreCase:
- return multimap_none(ci_equal_to(), itr, isruntime? runtime_vals : vals);
+ return multimap_none(ci_equal_to(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::ForAnyValueStringLike:
case TokenID::StringLike:
- return multimap_any(string_like(), itr, isruntime? runtime_vals : vals);
+ return multimap_any(string_like(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::StringNotLike:
- return multimap_none(string_like(), itr, isruntime? runtime_vals : vals);
+ return multimap_none(string_like(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::ForAllValuesStringEquals:
- return multimap_all(std::equal_to<std::string>(), itr, isruntime? runtime_vals : vals);
+ return multimap_all(std::equal_to<std::string>(), itr,
+ isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::ForAllValuesStringLike:
- return multimap_all(string_like(), itr, isruntime? runtime_vals : vals);
+ return multimap_all(string_like(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::ForAllValuesStringEqualsIgnoreCase:
- return multimap_all(ci_equal_to(), itr, isruntime? runtime_vals : vals);
+ return multimap_all(ci_equal_to(), itr, isruntime ? runtime_vals : vals,
+ eval_log);
// Numeric
case TokenID::NumericEquals:
- return typed_any(std::equal_to<double>(), as_number, s, vals);
+ return typed_any(std::equal_to<double>(), as_number, s, vals, eval_log);
case TokenID::NumericNotEquals:
- return typed_none(std::equal_to<double>(),
- as_number, s, vals);
+ return typed_none(std::equal_to<double>(), as_number, s, vals, eval_log);
case TokenID::NumericLessThan:
- return typed_any(std::less<double>(), as_number, s, vals);
+ return typed_any(std::less<double>(), as_number, s, vals, eval_log);
case TokenID::NumericLessThanEquals:
- return typed_any(std::less_equal<double>(), as_number, s, vals);
+ return typed_any(std::less_equal<double>(), as_number, s, vals, eval_log);
case TokenID::NumericGreaterThan:
- return typed_any(std::greater<double>(), as_number, s, vals);
+ return typed_any(std::greater<double>(), as_number, s, vals, eval_log);
case TokenID::NumericGreaterThanEquals:
- return typed_any(std::greater_equal<double>(), as_number, s,
- vals);
+ return typed_any(std::greater_equal<double>(), as_number, s, vals,
+ eval_log);
// Date!
case TokenID::DateEquals:
- return typed_any(std::equal_to<ceph::real_time>(), as_date, s, vals);
+ return typed_any(std::equal_to<ceph::real_time>(), as_date, s, vals,
+ eval_log);
case TokenID::DateNotEquals:
return typed_none(std::equal_to<ceph::real_time>(),
- as_date, s, vals);
+ as_date, s, vals, eval_log);
case TokenID::DateLessThan:
- return typed_any(std::less<ceph::real_time>(), as_date, s, vals);
+ return typed_any(std::less<ceph::real_time>(), as_date, s, vals, eval_log);
case TokenID::DateLessThanEquals:
- return typed_any(std::less_equal<ceph::real_time>(), as_date, s, vals);
+ return typed_any(std::less_equal<ceph::real_time>(), as_date, s, vals,
+ eval_log);
case TokenID::DateGreaterThan:
- return typed_any(std::greater<ceph::real_time>(), as_date, s, vals);
+ return typed_any(std::greater<ceph::real_time>(), as_date, s, vals,
+ eval_log);
case TokenID::DateGreaterThanEquals:
return typed_any(std::greater_equal<ceph::real_time>(), as_date, s,
- vals);
+ vals, eval_log);
// Bool!
case TokenID::Bool:
- return typed_any(std::equal_to<bool>(), as_bool, s, vals);
+ return typed_any(std::equal_to<bool>(), as_bool, s, vals, eval_log);
// Binary!
case TokenID::BinaryEquals:
return typed_any(std::equal_to<ceph::bufferlist>(), as_binary, s,
- vals);
+ vals, eval_log);
// IP Address!
case TokenID::IpAddress:
- return typed_any(std::equal_to<MaskedIP>(), as_network, s, vals);
+ return typed_any(std::equal_to<MaskedIP>(), as_network, s, vals, eval_log);
case TokenID::NotIpAddress:
return typed_none(std::equal_to<MaskedIP>(),
- as_network, s, vals);
+ as_network, s, vals, eval_log);
// Amazon Resource Names!
// The ArnEquals and ArnLike condition operators behave identically.
case TokenID::ArnEquals:
case TokenID::ArnLike:
- return multimap_any(arn_like, itr, isruntime? runtime_vals : vals);
+ return multimap_any(arn_like, itr, isruntime ? runtime_vals : vals,
+ eval_log);
case TokenID::ArnNotEquals:
case TokenID::ArnNotLike:
- return multimap_none(arn_like, itr, isruntime? runtime_vals : vals);
+ return multimap_none(arn_like, itr, isruntime ? runtime_vals : vals,
+ eval_log);
default:
+ eval_log << "Unknown operation: Returning false" << lendl;
return false;
}
}
return m;
}
-namespace {
-const char* condop_string(const TokenID t) {
- switch (t) {
- case TokenID::StringEquals:
- return "StringEquals";
-
- case TokenID::StringNotEquals:
- return "StringNotEquals";
-
- case TokenID::StringEqualsIgnoreCase:
- return "StringEqualsIgnoreCase";
-
- case TokenID::StringNotEqualsIgnoreCase:
- return "StringNotEqualsIgnoreCase";
-
- case TokenID::StringLike:
- return "StringLike";
-
- case TokenID::StringNotLike:
- return "StringNotLike";
-
- // Numeric!
- case TokenID::NumericEquals:
- return "NumericEquals";
-
- case TokenID::NumericNotEquals:
- return "NumericNotEquals";
-
- case TokenID::NumericLessThan:
- return "NumericLessThan";
-
- case TokenID::NumericLessThanEquals:
- return "NumericLessThanEquals";
-
- case TokenID::NumericGreaterThan:
- return "NumericGreaterThan";
-
- case TokenID::NumericGreaterThanEquals:
- return "NumericGreaterThanEquals";
-
- case TokenID::DateEquals:
- return "DateEquals";
-
- case TokenID::DateNotEquals:
- return "DateNotEquals";
-
- case TokenID::DateLessThan:
- return "DateLessThan";
-
- case TokenID::DateLessThanEquals:
- return "DateLessThanEquals";
-
- case TokenID::DateGreaterThan:
- return "DateGreaterThan";
-
- case TokenID::DateGreaterThanEquals:
- return "DateGreaterThanEquals";
-
- case TokenID::Bool:
- return "Bool";
-
- case TokenID::BinaryEquals:
- return "BinaryEquals";
-
- case TokenID::IpAddress:
- return "case TokenID::IpAddress";
-
- case TokenID::NotIpAddress:
- return "NotIpAddress";
-
- case TokenID::ArnEquals:
- return "ArnEquals";
-
- case TokenID::ArnNotEquals:
- return "ArnNotEquals";
-
- case TokenID::ArnLike:
- return "ArnLike";
-
- case TokenID::ArnNotLike:
- return "ArnNotLike";
-
- case TokenID::Null:
- return "Null";
-
- default:
- return "InvalidConditionOperator";
- }
-}
-
template<typename Iterator>
ostream& print_array(ostream& m, Iterator begin, Iterator end) {
if (begin == end) {
return m;
}
-}
-
ostream& operator <<(ostream& m, const Condition& c) {
m << condop_string(c.op);
if (c.ifexists) {
return m << " }";
}
-Effect Statement::eval(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida,
- uint64_t act, boost::optional<const ARN&> res, boost::optional<PolicyPrincipal&> princ_type) const {
-
- if (eval_principal(e, ida, princ_type) == Effect::Deny) {
+Effect
+Statement::eval(
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ uint64_t act,
+ boost::optional<const ARN&> res,
+ const maybeout& eval_log,
+ boost::optional<PolicyPrincipal&> princ_type) const {
+
+ if (eval_principal(e, ida, eval_log, princ_type) == Effect::Deny) {
+ eval_log << "Passing." << lendl;
return Effect::Pass;
}
if (res && resource.empty() && notresource.empty()) {
+ eval_log << "A resource was specified but both Resource and NotResource "
+ "were empty. Passing." << lendl;
return Effect::Pass;
}
if (!res && (!resource.empty() || !notresource.empty())) {
+ eval_log << "No resource was specified yet neither "
+ "Resource nor NotResource were empty. Passing." << lendl;
return Effect::Pass;
}
if (!resource.empty() && res) {
- if (!std::any_of(resource.begin(), resource.end(),
- [&res](const ARN& pattern) {
- return pattern.match(*res);
- })) {
+ eval_log << "Checking Resource for " << res << lendl;
+ if (!std::any_of(
+ resource.begin(), resource.end(),
+ [&res, &eval_log](const ARN& pattern) {
+ if (pattern.match(*res)) {
+ eval_log << "Matched " << pattern << lendl;
+ return true;
+ }
+ return false;
+ })) {
+ eval_log << "No match in Resource. Passing." << lendl;
return Effect::Pass;
}
} else if (!notresource.empty() && res) {
- if (std::any_of(notresource.begin(), notresource.end(),
- [&res](const ARN& pattern) {
- return pattern.match(*res);
+ eval_log << "Checking NotResource for " << res << lendl;
+ if (std::any_of(
+ notresource.begin(), notresource.end(),
+ [&res, &eval_log](const ARN& pattern) {
+ if (pattern.match(*res)) {
+ eval_log << "Matched " << pattern << lendl;
+ return true;
+ }
+ return false;
})) {
+ eval_log << "Match found in NotResource. Passing." << lendl;
return Effect::Pass;
}
}
- if (!(action[act] == 1) || (notaction[act] == 1)) {
+ if (!(action[act] == 1)) {
+ eval_log << action_bit_string(action_t(act))
+ << "not found in Action. Passing." << lendl;
+ return Effect::Pass;
+ }
+
+ if (notaction[act] == 1) {
+ eval_log << action_bit_string(action_t(act))
+ << "found in NotAction. Passing." << lendl;
return Effect::Pass;
}
- if (std::all_of(conditions.begin(),
- conditions.end(),
- [&e](const Condition& c) { return c.eval(e);})) {
- return effect;
+ {
+ eval_log << "Evaluating conditions:" << lendl;
+ auto b = eval_log.open("{", "}");
+ auto indent = eval_log.indent();
+ if (std::all_of(
+ conditions.begin(), conditions.end(),
+ [&e, &indent](const Condition& c) {
+ indent << "Evaluating condition: " << c << lendl;
+ return c.eval(e, indent);
+ })) {
+ b.close();
+ eval_log << "Returning " << effect;
+ return effect;
+ }
}
+ eval_log << "Passing." << lendl;
return Effect::Pass;
}
});
}
-Effect Statement::eval_principal(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type) const {
+Effect
+Statement::eval_principal(
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ const maybeout& eval_log,
+ boost::optional<PolicyPrincipal&> princ_type) const {
if (princ_type) {
*princ_type = PolicyPrincipal::Other;
}
+ eval_log << "Evaluating identity: " << ida << lendl;
if (ida) {
if (princ.empty() && noprinc.empty()) {
+ eval_log << "Principal empty and NotPrincipal empty: Denying." << lendl;
return Effect::Deny;
}
- if (ida->get_identity_type() != TYPE_ROLE && !princ.empty() && !is_identity(*ida, princ)) {
+ if (ida->get_identity_type() != TYPE_ROLE &&
+ !princ.empty() && !is_identity(*ida, princ)) {
+ eval_log << "Identity is not role, Principal is not empty, and "
+ "the identity is not in Principal." << lendl;
return Effect::Deny;
}
+ eval_log << "Checking type of Principal match." << lendl;
if (ida->get_identity_type() == TYPE_ROLE && !princ.empty()) {
bool princ_matched = false;
- for (auto p : princ) { // Check each principal to determine the type of the one that has matched
+ // Check each principal to determine the type of the one that has matched
+ for (auto p : princ) {
if (ida->is_identity(p)) {
if (p.is_assumed_role() || p.is_user()) {
- if (princ_type) *princ_type = PolicyPrincipal::Session;
+ if (princ_type) {
+ eval_log << "Setting principal type to Session" << lendl;
+ *princ_type = PolicyPrincipal::Session;
+ }
} else {
- if (princ_type) *princ_type = PolicyPrincipal::Role;
+ if (princ_type) {
+ eval_log << "Setting principal type to Session" << lendl;
+ *princ_type = PolicyPrincipal::Role;
+ }
}
princ_matched = true;
}
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;
- }
+Effect Statement::eval_conditions(const Environment& e,
+ const maybeout& eval_log) const {
+ if (std::all_of(
+ conditions.begin(), conditions.end(),
+ [&e, &eval_log](const Condition& c) {
+ eval_log << "Evaluating condition: " << c << lendl;
+ return c.eval(e, eval_log);
+ })) {
+ eval_log << "Returning Allow." << lendl;
+ return Effect::Allow;
+ }
+
+ eval_log << "Returning Deny." << lendl;
return Effect::Deny;
}
}
Effect Policy::eval(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida,
- std::uint64_t action, boost::optional<const ARN&> resource,
- boost::optional<PolicyPrincipal&> princ_type) const {
+ boost::optional<const rgw::auth::Identity&> ida,
+ std::uint64_t action,
+ boost::optional<const ARN&> resource,
+ maybeout eval_log,
+ boost::optional<PolicyPrincipal&> princ_type) const
+{
auto allowed = false;
- for (auto& s : statements) {
- auto g = s.eval(e, ida, action, resource, princ_type);
- if (g == Effect::Deny) {
- return g;
- } else if (g == Effect::Allow) {
- allowed = true;
+ eval_log
+ << "Evaluating policy: ```" << lendl << text << lendl << "'''" << lendl
+ << "Environment: " << e << lendl
+ << "Principal: " << ida << lendl
+ << "Action: " << action_bit_string(action_t(action)) << lendl
+ << "Resource: " << resource << lendl;
+
+ {
+ for (auto& s : statements) {
+ eval_log << "Evaluating statement: " << s << lendl;
+ auto b = eval_log.open("{", "}");
+ auto indent = eval_log.indent();
+ auto g = s.eval(e, ida, action, resource, indent, princ_type);
+ b.close();
+ if (g == Effect::Deny) {
+ eval_log << "Denying.\n";
+ return g;
+ } else if (g == Effect::Allow) {
+ allowed = true;
+ }
}
}
+ eval_log << (allowed ? "Allowing.\n" : "Passing\n");
return allowed ? Effect::Allow : Effect::Pass;
}
-Effect Policy::eval_principal(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type) const {
+Effect
+Policy::eval_principal(
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ maybeout eval_log,
+ boost::optional<PolicyPrincipal&> princ_type) const {
auto allowed = false;
for (auto& s : statements) {
- auto g = s.eval_principal(e, ida, princ_type);
+ eval_log << "Evaluating identity " << ida << " in statement " << s << lendl;
+ auto g = s.eval_principal(e, ida, eval_log.indent(), princ_type);
if (g == Effect::Deny) {
+ eval_log << "Denying." << lendl;
return g;
} else if (g == Effect::Allow) {
allowed = true;
}
}
+ eval_log << (allowed ? "Allowing." : "Denying.") << lendl;
return allowed ? Effect::Allow : Effect::Deny;
}
-Effect Policy::eval_conditions(const Environment& e) const {
+Effect Policy::eval_conditions(const Environment& e,
+ maybeout eval_log) const {
auto allowed = false;
for (auto& s : statements) {
- auto g = s.eval_conditions(e);
+ eval_log << "Evaluating conditions in statement:" << lendl;
+ auto b = eval_log.open("{", "}");
+ auto indent = eval_log.indent();
+ auto g = s.eval_conditions(e, indent);
if (g == Effect::Deny) {
+ eval_log << "Denying." << lendl;
return g;
} else if (g == Effect::Allow) {
allowed = true;
}
}
+ eval_log << (allowed ? "Allowing." : "Denying.") << lendl;
return allowed ? Effect::Allow : Effect::Deny;
}
struct IsPublicStatement
{
- bool operator() (const Statement &s) const {
+ const maybeout& eval_log;
+
+ IsPublicStatement(const maybeout& eval_log) :
+ eval_log(eval_log)
+ {}
+
+ bool operator()(const Statement& s) const {
if (s.effect == Effect::Allow) {
for (const auto& p : s.princ) {
if (p.is_wildcard()) {
- return s.eval_conditions(iam_all_env) == Effect::Allow;
+ eval_log << "Evaluating conditions in statement:" << lendl;
+ auto b = eval_log.open("{", "}");
+ auto indent = eval_log.indent();
+ return s.eval_conditions(iam_all_env, indent) == Effect::Allow;
}
}
}
};
-bool is_public(const Policy& p)
+bool is_public(const Policy& p, maybeout eval_log)
{
- return std::any_of(p.statements.begin(), p.statements.end(), IsPublicStatement());
+ return std::any_of(p.statements.begin(), p.statements.end(),
+ IsPublicStatement(eval_log));
}
} // namespace IAM
allCount
};
+struct maybeout {
+ mutable std::ostream* m;
+ std::string prefix;
+
+ maybeout(std::ostream* m,
+ std::string&& prefix_ = "") :
+ m(m)
+ {
+ if (m) {
+ prefix = std::move(prefix_);
+ lendl_();
+ }
+ }
+
+ ~maybeout()
+ {
+ if (m) {
+ m->flush();
+ }
+ }
+ maybeout(const maybeout&) = default;
+ maybeout& operator=(const maybeout&) = default;
+ maybeout(maybeout&&) = delete;
+ maybeout& operator=(maybeout&&) = delete;
+
+ maybeout&
+ operator=(std::nullptr_t)
+ {
+ m = nullptr;
+ prefix.clear();
+ return *this;
+ }
+
+ maybeout
+ indent(std::string&& prefix2 = " ",
+ std::string_view closer_ = "") const
+ {
+ if (m) {
+ return maybeout(m, (prefix + std::move(prefix2)));
+ } else {
+ return nullptr;
+ }
+ }
+
+ void lendl_() const;
+
+ struct bracket {
+ const maybeout& m;
+ std::string_view closer;
+
+ bracket(const maybeout& m, std::string_view closer) :
+ m(m), closer(closer)
+ {}
+
+ void close();
+
+ ~bracket() { close(); }
+ };
+
+ bracket
+ open(std::string_view opener, std::string_view closer_) const
+ {
+ if (m) {
+ *m << opener;
+ lendl_();
+ return { *this, closer_ };
+ }
+ return { *this, "" };
+ }
+ operator bool() const { return !!m; }
+
+ const maybeout&
+ operator<<(const maybeout& (*func)(const maybeout&)) const
+ {
+ return func(*this);
+ }
+};
+
+const maybeout&
+operator<<(const maybeout& m, const auto& datum)
+{
+ if (m) {
+ *(m.m) << datum;
+ }
+ return m;
+}
+
+inline const maybeout& lendl(const maybeout& m)
+{
+ if (m.m) {
+ m.m->put(m.m->widen('\n'));
+ *m.m << m.prefix;
+ }
+ return m;
+}
+
+
+
using Action_t = std::bitset<allCount>;
using NotAction_t = Action_t;
Condition() = default;
Condition(TokenID op, const char* s, std::size_t len, bool ifexists)
: op(op), key(s, len), ifexists(ifexists) {}
-
- bool eval(const Environment& e) const;
+ bool eval(const Environment& e, const maybeout& eval_log) const;
static boost::optional<double> as_number(const std::string& s) {
std::size_t p = 0;
}
};
- 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>;
+ 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 multimap_all(F&& f, const unordered_multimap_it_pair& it,
- const std::vector<std::string>& v) {
+ static bool multimap_all(F&& f,
+ const unordered_multimap_it_pair& it,
+ const std::vector<std::string>& v,
+ const maybeout& eval_log) {
for (auto itr = it.first; itr != it.second; itr++) {
bool matched = false;
+ std::string failmatch;
for (const auto& d : v) {
if (f(itr->second, d)) {
- matched = true;
+ matched = true;
+ } else if (eval_log && failmatch.empty()) {
+ failmatch = fmt::format("({}, {})", itr->second, d);
+ }
+ }
+ if (!matched) {
+ if (failmatch.empty()) {
+ eval_log << "Values matched against were empty." << lendl;
+ } else {
+ eval_log << "Predicate false for " << failmatch << lendl;
+ }
+ return false;
}
- }
- if (!matched)
- return false;
}
return true;
}
- template<typename F>
- static bool multimap_any(F&& f, const unordered_multimap_it_pair& it,
- const std::vector<std::string>& v) {
+ template <typename F>
+ static bool
+ multimap_any(F&& f,
+ const unordered_multimap_it_pair& it,
+ const std::vector<std::string>& v,
+ const maybeout eval_log) {
for (auto itr = it.first; itr != it.second; itr++) {
for (const auto& d : v) {
if (f(itr->second, d)) {
- return true;
+ eval_log << "Predicate true for (" << itr->second
+ << ", " << d << ")" << lendl;
+ return true;
+ }
}
- }
}
return false;
}
template<typename F>
static bool multimap_none(F&& f, const unordered_multimap_it_pair& it,
- const std::vector<std::string>& v) {
+ const std::vector<std::string>& v,
+ const maybeout eval_log) {
for (auto itr = it.first; itr != it.second; itr++) {
for (const auto& d : v) {
if (f(itr->second, d)) {
+ eval_log << "Predicate true for (" << itr->second << ", "
+ << d << ")" << lendl;
return false;
}
}
return true;
}
- template<typename F, typename X>
+ template <typename F, typename X>
static bool typed_any(F&& f, X& x, const std::string& c,
- const std::vector<std::string>& v) {
+ const std::vector<std::string>& v,
+ const maybeout& eval_log) {
auto xc = std::forward<X>(x)(c);
if (!xc) {
+ eval_log << "Failed to convert `" << c << "`. Returning false."
+ << lendl;
return false;
}
for (const auto& d : v) {
auto xd = x(d);
if (!xd) {
+ eval_log << "Failed to convert `" << d << "`. Skipping."
+ << lendl;
continue;
}
if (f(*xc, *xd)) {
+ eval_log << "Predicate true for (" << *xc << ", " << *xd << ")"
+ << lendl;
return true;
}
}
template<typename F, typename X>
static bool typed_none(F&& f, X& x, const std::string& c,
- const std::vector<std::string>& v) {
+ const std::vector<std::string>& v,
+ const maybeout& eval_log) {
auto xc = std::forward<X>(x)(c);
if (!xc) {
+ eval_log << "Failed to convert `" << c << "`. Returning false."
+ << lendl;
return false;
}
for (const auto& d : v) {
auto xd = x(d);
if (!xd) {
+ eval_log << "Failed to convert `" << d << "`. Skipping."
+ << lendl;
continue;
}
if (f(*xc, *xd)) {
+ eval_log << "Predicate true for (" << *xc << ", " << *xd << ")"
+ << lendl;
return false;
}
}
std::vector<Condition> conditions;
Effect eval(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida,
- std::uint64_t action, boost::optional<const ARN&> resource, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
-
- Effect eval_principal(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
-
- Effect eval_conditions(const Environment& e) const;
+ boost::optional<const rgw::auth::Identity&> ida,
+ std::uint64_t action,
+ boost::optional<const ARN&> resource,
+ const maybeout& eval_log,
+ boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
+
+ Effect eval_principal(
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ const maybeout& eval_log,
+ boost::optional<PolicyPrincipal&> princ_type = boost::none) const;
+
+ Effect
+ eval_conditions(const Environment& e,
+ const maybeout& eval_log) const;
};
std::ostream& operator <<(std::ostream& m, const Statement& s);
};
struct Policy {
+ bool copious_logging;
std::string text;
Version version = Version::v2008_10_17;
boost::optional<std::string> id = boost::none;
bool reject_invalid_principals);
Effect eval(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida,
- std::uint64_t action, boost::optional<const ARN&> resource, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
+ boost::optional<const rgw::auth::Identity&> ida,
+ std::uint64_t action,
+ boost::optional<const ARN&> resource,
+ maybeout eval_log,
+ boost::optional<PolicyPrincipal&> princ_type =boost::none) const;
+
+ Effect
+ eval(const DoutPrefixProvider* dpp,
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ std::uint64_t action,
+ boost::optional<const ARN&> resource,
+ boost::optional<PolicyPrincipal&> princ_type = boost::none) const {
+ std::ostringstream ostr;
+ bool copious_logging = false;
+ if (dpp) {
+ copious_logging =
+ (dpp->get_cct()->_conf.get_val<bool>("rgw_copious_policy_logging") &&
+ dpp->get_cct()->_conf->subsys.should_gather(ceph_subsys_rgw, 20));
+ }
- Effect eval_principal(const Environment& e,
- boost::optional<const rgw::auth::Identity&> ida, boost::optional<PolicyPrincipal&> princ_type=boost::none) const;
+ auto eff = eval(e, ida, action, resource,
+ {copious_logging ? &ostr : nullptr}, princ_type);
+ if (dpp) {
+ ldpp_dout(dpp, 20) << ostr.str() << dendl;
+ }
+ return eff;
+ }
- Effect eval_conditions(const Environment& e) const;
+ Effect eval_principal(
+ const Environment& e,
+ boost::optional<const rgw::auth::Identity&> ida,
+ maybeout eval_log,
+ boost::optional<PolicyPrincipal&> princ_type = boost::none) const;
+
+ Effect eval_conditions(const Environment& e,
+ maybeout eval_log) const;
template <typename F>
bool has_conditional(const std::string& conditional, F p) const {
};
std::ostream& operator <<(std::ostream& m, const Policy& p);
-bool is_public(const Policy& p);
+bool is_public(const Policy& p, maybeout eval_log);
+
+inline bool is_public(const DoutPrefixProvider* dpp, const Policy& p)
+{
+ std::ostringstream ostr;
+ bool copious_logging = false;
+ if (dpp) {
+ copious_logging =
+ (dpp->get_cct()->_conf.get_val<bool>("rgw_copious_policy_logging") &&
+ dpp->get_cct()->_conf->subsys.should_gather(ceph_subsys_rgw, 20));
+ }
+ auto b = is_public(p, {copious_logging ? &ostr : nullptr});
+ if (dpp) {
+ ldpp_dout(dpp, 20) << ostr.str() << dendl;
+ }
+ return b;
+}
}
}
rgw::sal::Attrs attrs(s->bucket_attrs);
if (s->bucket_access_conf &&
s->bucket_access_conf->block_public_policy() &&
- rgw::IAM::is_public(p)) {
+ rgw::IAM::is_public(this, p)) {
op_ret = -EACCES;
return;
}
void RGWGetBucketPolicyStatus::execute(optional_yield y)
{
- isPublic = (s->iam_policy && rgw::IAM::is_public(*s->iam_policy)) || s->bucket_acl.is_public(this);
+ isPublic = (s->iam_policy && rgw::IAM::is_public(this, *s->iam_policy)) ||
+ s->bucket_acl.is_public(this);
}
int RGWPutBucketPublicAccessBlock::verify_permission(optional_yield y)
const rgw::IAM::Policy p(s->cct, policy_tenant, policy, false);
if (!s->principal_tags.empty()) {
- auto res = p.eval(s->env, *s->auth.identity, rgw::IAM::stsTagSession, boost::none);
+ auto res = p.eval(this, s->env, *s->auth.identity, rgw::IAM::stsTagSession, boost::none);
if (res != rgw::IAM::Effect::Allow) {
ldout(s->cct, 0) << "evaluating policy for stsTagSession returned deny/pass" << dendl;
return -EPERM;
op = rgw::IAM::stsAssumeRole;
}
- auto res = p.eval(s->env, *s->auth.identity, op, boost::none);
+ auto res = p.eval(this, s->env, *s->auth.identity, op, boost::none);
if (res != rgw::IAM::Effect::Allow) {
ldout(s->cct, 0) << "evaluating policy for op: " << op << " returned deny/pass" << dendl;
return -EPERM;
ARN arn1(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(p.eval(e, none, s3ListBucket, arn1),
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(p.eval(e, none, s3PutBucketAcl, arn2),
+ EXPECT_EQ(p.eval(e, none, s3PutBucketAcl, arn2, nullptr),
Effect::Pass);
ARN arn3(Partition::aws, Service::s3,
"", arbitrary_tenant, "erroneous_bucket");
- EXPECT_EQ(p.eval(e, none, s3ListBucket, arn3),
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn3, nullptr),
Effect::Pass);
}
for (auto i = 0ULL; i < s3All; ++i) {
ARN arn1(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket");
- EXPECT_EQ(p.eval(e, trueacct, i, arn1),
+ EXPECT_EQ(p.eval(e, trueacct, i, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket/myobject");
- EXPECT_EQ(p.eval(e, trueacct, i, arn2),
+ EXPECT_EQ(p.eval(e, trueacct, i, arn2, nullptr),
Effect::Allow);
ARN arn3(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket");
- EXPECT_EQ(p.eval(e, notacct, i, arn3),
+ EXPECT_EQ(p.eval(e, notacct, i, arn3, nullptr),
Effect::Pass);
ARN arn4(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket/myobject");
- EXPECT_EQ(p.eval(e, notacct, i, arn4),
+ EXPECT_EQ(p.eval(e, notacct, i, arn4, nullptr),
Effect::Pass);
ARN arn5(Partition::aws, Service::s3,
"", arbitrary_tenant, "notyourbucket");
- EXPECT_EQ(p.eval(e, trueacct, i, arn5),
+ EXPECT_EQ(p.eval(e, trueacct, i, arn5, nullptr),
Effect::Pass);
ARN arn6(Partition::aws, Service::s3,
"", arbitrary_tenant, "notyourbucket/notyourobject");
- EXPECT_EQ(p.eval(e, trueacct, i, arn6),
+ EXPECT_EQ(p.eval(e, trueacct, i, arn6, nullptr),
Effect::Pass);
}
ARN arn1(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket");
- EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn1),
+ EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket");
- EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn2),
+ EXPECT_EQ(p.eval(em, none, s3PutBucketPolicy, arn2, nullptr),
Effect::Allow);
}
ARN arn3(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data");
- EXPECT_EQ(p.eval(em, none, op, arn3),
+ EXPECT_EQ(p.eval(em, none, op, arn3, nullptr),
Effect::Pass);
ARN arn4(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data");
- EXPECT_EQ(p.eval(tr, none, op, arn4),
+ EXPECT_EQ(p.eval(tr, none, op, arn4, nullptr),
s3allow[op] ? Effect::Allow : Effect::Pass);
ARN arn5(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data");
- EXPECT_EQ(p.eval(fa, none, op, arn5),
+ EXPECT_EQ(p.eval(fa, none, op, arn5, nullptr),
Effect::Pass);
ARN arn6(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data/moo");
- EXPECT_EQ(p.eval(em, none, op, arn6),
+ EXPECT_EQ(p.eval(em, none, op, arn6, nullptr),
Effect::Pass);
ARN arn7(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data/moo");
- EXPECT_EQ(p.eval(tr, none, op, arn7),
+ EXPECT_EQ(p.eval(tr, none, op, arn7, nullptr),
s3allow[op] ? Effect::Allow : Effect::Pass);
ARN arn8(Partition::aws, Service::s3,
"", arbitrary_tenant, "confidential-data/moo");
- EXPECT_EQ(p.eval(fa, none, op, arn8),
+ EXPECT_EQ(p.eval(fa, none, op, arn8, nullptr),
Effect::Pass);
ARN arn9(Partition::aws, Service::s3,
"", arbitrary_tenant, "really-confidential-data");
- EXPECT_EQ(p.eval(em, none, op, arn9),
+ EXPECT_EQ(p.eval(em, none, op, arn9, nullptr),
Effect::Pass);
ARN arn10(Partition::aws, Service::s3,
"", arbitrary_tenant, "really-confidential-data");
- EXPECT_EQ(p.eval(tr, none, op, arn10),
+ EXPECT_EQ(p.eval(tr, none, op, arn10, nullptr),
Effect::Pass);
ARN arn11(Partition::aws, Service::s3,
"", arbitrary_tenant, "really-confidential-data");
- EXPECT_EQ(p.eval(fa, none, op, arn11),
+ EXPECT_EQ(p.eval(fa, none, op, arn11, nullptr),
Effect::Pass);
ARN arn12(Partition::aws, Service::s3,
"", arbitrary_tenant,
"really-confidential-data/moo");
- EXPECT_EQ(p.eval(em, none, op, arn12), Effect::Pass);
+ EXPECT_EQ(p.eval(em, none, op, arn12, nullptr), Effect::Pass);
ARN arn13(Partition::aws, Service::s3,
"", arbitrary_tenant,
"really-confidential-data/moo");
- EXPECT_EQ(p.eval(tr, none, op, arn13), Effect::Pass);
+ EXPECT_EQ(p.eval(tr, none, op, arn13, nullptr), Effect::Pass);
ARN arn14(Partition::aws, Service::s3,
"", arbitrary_tenant,
"really-confidential-data/moo");
- EXPECT_EQ(p.eval(fa, none, op, arn14), Effect::Pass);
+ EXPECT_EQ(p.eval(fa, none, op, arn14, nullptr), Effect::Pass);
}
}
ARN arn1(Partition::aws, Service::iam,
"", arbitrary_tenant, "role/example_role");
- EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::iam,
"", arbitrary_tenant, "role/example_role");
- EXPECT_EQ(p.eval(e, none, iamDeleteRole, arn2),
+ EXPECT_EQ(p.eval(e, none, iamDeleteRole, arn2, nullptr),
Effect::Pass);
}
ARN arn1(Partition::aws, Service::iam,
"", arbitrary_tenant, "role/example_role");
- EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::iam,
"", arbitrary_tenant, "role/example_role");
- EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2),
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2, nullptr),
Effect::Pass);
ARN arn3(Partition::aws, Service::iam,
"", "", "role/example_role");
- EXPECT_EQ(p.eval(e, none, iamCreateRole, arn3),
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn3, nullptr),
Effect::Pass);
}
ARN arn1(Partition::aws, Service::iam,
"", arbitrary_tenant, "user/A");
- EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1),
+ EXPECT_EQ(p.eval(e, none, iamCreateRole, arn1, nullptr),
Effect::Allow);
ARN arn2(Partition::aws, Service::iam,
"", arbitrary_tenant, "user/A");
- EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2),
+ EXPECT_EQ(p.eval(e, none, s3ListBucket, arn2, nullptr),
Effect::Allow);
}
ARN arn1(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket/*");
- EXPECT_EQ(p.eval(e, subacct, s3ListBucket, arn1),
+ EXPECT_EQ(p.eval(e, subacct, s3ListBucket, arn1, nullptr),
Effect::Allow);
-
+
ARN arn2(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket/*");
- EXPECT_EQ(p.eval(e, parentacct, s3ListBucket, arn2),
+ EXPECT_EQ(p.eval(e, parentacct, s3ListBucket, arn2, nullptr),
Effect::Pass);
ARN arn3(Partition::aws, Service::s3,
"", arbitrary_tenant, "mybucket/*");
- EXPECT_EQ(p.eval(e, sub2acct, s3ListBucket, arn3),
+ EXPECT_EQ(p.eval(e, sub2acct, s3ListBucket, arn3, nullptr),
Effect::Pass);
}
// Without an IP address in the environment then evaluation will always pass
ARN arn1(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(allowp.eval(e, trueacct, s3ListBucket, arn1),
+ EXPECT_EQ(allowp.eval(e, trueacct, s3ListBucket, arn1, nullptr),
Effect::Pass);
ARN arn2(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(fullp.eval(e, trueacct, s3ListBucket, arn2),
+ EXPECT_EQ(fullp.eval(e, trueacct, s3ListBucket, arn2, nullptr),
Effect::Pass);
ARN arn3(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(allowp.eval(allowedIP, trueacct, s3ListBucket, arn3),
+ EXPECT_EQ(allowp.eval(allowedIP, trueacct, s3ListBucket, arn3, nullptr),
Effect::Allow);
ARN arn4(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(allowp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn4),
+ EXPECT_EQ(allowp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn4, nullptr),
Effect::Pass);
ARN arn5(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn5),
+ EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn5, nullptr),
Effect::Deny);
ARN arn6(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn6),
+ EXPECT_EQ(denyp.eval(allowedIP, trueacct, s3ListBucket, arn6, nullptr),
Effect::Deny);
ARN arn7(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn7),
+ EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn7, nullptr),
Effect::Pass);
ARN arn8(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn8),
+ EXPECT_EQ(denyp.eval(blocklistedIP, trueacct, s3ListBucket, arn8, nullptr),
Effect::Pass);
ARN arn9(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn9),
+ EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn9, nullptr),
Effect::Pass);
ARN arn10(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn10),
+ EXPECT_EQ(denyp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn10, nullptr),
Effect::Pass);
ARN arn11(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn11),
+ EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn11, nullptr),
Effect::Deny);
ARN arn12(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn12),
+ EXPECT_EQ(denyp.eval(allowedIPv6, trueacct, s3ListBucket, arn12, nullptr),
Effect::Deny);
ARN arn13(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn13),
+ EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn13, nullptr),
Effect::Allow);
ARN arn14(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn14),
+ EXPECT_EQ(fullp.eval(allowedIP, trueacct, s3ListBucket, arn14, nullptr),
Effect::Allow);
ARN arn15(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn15),
+ EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn15, nullptr),
Effect::Pass);
ARN arn16(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn16),
+ EXPECT_EQ(fullp.eval(blocklistedIP, trueacct, s3ListBucket, arn16, nullptr),
Effect::Pass);
ARN arn17(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn17),
+ EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn17, nullptr),
Effect::Allow);
ARN arn18(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn18),
+ EXPECT_EQ(fullp.eval(allowedIPv6, trueacct, s3ListBucket, arn18, nullptr),
Effect::Allow);
ARN arn19(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket");
- EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn19),
+ EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn19, nullptr),
Effect::Pass);
ARN arn20(Partition::aws, Service::s3,
"", arbitrary_tenant, "example_bucket/myobject");
- EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn20),
+ EXPECT_EQ(fullp.eval(blocklistedIPv6, trueacct, s3ListBucket, arn20, nullptr),
Effect::Pass);
}
Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false};
ArnLike.vals.push_back("arn:aws:s3:::bucket");
- EXPECT_FALSE(ArnLike.eval({}));
- EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}));
- EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}));
- EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}));
+ EXPECT_FALSE(ArnLike.eval({}, nullptr));
+ EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}, nullptr));
+ EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}, nullptr));
+ EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}, nullptr));
}
{
Condition ArnLike{TokenID::ArnLike, key.data(), key.size(), false};
ArnLike.vals.push_back("arn:aws:s3:::b*");
- EXPECT_FALSE(ArnLike.eval({}));
- EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::b"}}));
- EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}));
- EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}));
- EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}));
+ EXPECT_FALSE(ArnLike.eval({}, nullptr));
+ EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::b"}}, nullptr));
+ EXPECT_TRUE(ArnLike.eval({{key, "arn:aws:s3:::bucket"}}, nullptr));
+ EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::BUCKET"}}, nullptr));
+ EXPECT_FALSE(ArnLike.eval({{key, "arn:aws:s3:::user"}}, nullptr));
}
}
TEST_F(ConditionTest, StringNotEqualsLogic)
{
std::string key = "aws:UserName";
-
+
// Test case: value matches one of multiple condition values
// Should return false because value equals at least one condition value
{
stringNotEquals.vals.push_back("charlie");
// Input "bob" matches second condition value, should return false
- EXPECT_FALSE(stringNotEquals.eval({{key, "bob"}}));
- // Input "alice" matches first condition value, should return false
- EXPECT_FALSE(stringNotEquals.eval({{key, "alice"}}));
+ EXPECT_FALSE(stringNotEquals.eval({{key, "bob"}}, nullptr));
+ // Input "alice" matches first condition value, should return false
+ EXPECT_FALSE(stringNotEquals.eval({{key, "alice"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition values
// Should return true because value differs from all condition values
{
stringNotEquals.vals.push_back("charlie");
// Input "david" doesn't match any condition value, should return true
- EXPECT_TRUE(stringNotEquals.eval({{key, "david"}}));
+ EXPECT_TRUE(stringNotEquals.eval({{key, "david"}}, nullptr));
}
}
numericNotEquals.vals.push_back("30");
// Input "20" matches second condition value, should return false
- EXPECT_FALSE(numericNotEquals.eval({{key, "20"}}));
+ EXPECT_FALSE(numericNotEquals.eval({{key, "20"}}, nullptr));
// Input "10" matches first condition value, should return false
- EXPECT_FALSE(numericNotEquals.eval({{key, "10"}}));
+ EXPECT_FALSE(numericNotEquals.eval({{key, "10"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition values
// Should return true because value differs from all condition values
{
numericNotEquals.vals.push_back("30");
// Input "40" doesn't match any condition value, should return true
- EXPECT_TRUE(numericNotEquals.eval({{key, "40"}}));
+ EXPECT_TRUE(numericNotEquals.eval({{key, "40"}}, nullptr));
}
}
TEST_F(ConditionTest, DateNotEqualsLogic)
{
std::string key = "aws:CurrentTime";
-
+
// Test case: value matches one of multiple condition values
// Should return false because value equals at least one condition value
{
dateNotEquals.vals.push_back("2023-12-01T00:00:00Z");
// Input matches second condition value, should return false
- EXPECT_FALSE(dateNotEquals.eval({{key, "2023-06-01T00:00:00Z"}}));
+ EXPECT_FALSE(dateNotEquals.eval({{key, "2023-06-01T00:00:00Z"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition values
// Should return true because value differs from all condition values
{
dateNotEquals.vals.push_back("2023-12-01T00:00:00Z");
// Input doesn't match any condition value, should return true
- EXPECT_TRUE(dateNotEquals.eval({{key, "2024-01-01T00:00:00Z"}}));
+ EXPECT_TRUE(dateNotEquals.eval({{key, "2024-01-01T00:00:00Z"}}, nullptr));
}
}
TEST_F(ConditionTest, NotIpAddressLogic)
{
std::string key = "aws:SourceIp";
-
+
// Test case: value matches one of multiple condition values
// Should return false because value equals at least one condition value
{
notIpAddress.vals.push_back("172.16.0.1");
// Input matches second condition value, should return false
- EXPECT_FALSE(notIpAddress.eval({{key, "10.0.0.1"}}));
+ EXPECT_FALSE(notIpAddress.eval({{key, "10.0.0.1"}}, nullptr));
// Input matches first condition value, should return false
- EXPECT_FALSE(notIpAddress.eval({{key, "192.168.1.1"}}));
+ EXPECT_FALSE(notIpAddress.eval({{key, "192.168.1.1"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition values
// Should return true because value differs from all condition values
{
notIpAddress.vals.push_back("172.16.0.1");
// Input doesn't match any condition value, should return true
- EXPECT_TRUE(notIpAddress.eval({{key, "8.8.8.8"}}));
+ EXPECT_TRUE(notIpAddress.eval({{key, "8.8.8.8"}}, nullptr));
}
}
TEST_F(ConditionTest, ArnNotEqualsLogic)
{
std::string key = "aws:SourceArn";
-
+
// Test case: value matches one of multiple condition values
// Should return false because value equals at least one condition value
{
arnNotEquals.vals.push_back("arn:aws:s3:::bucket3");
// Input matches second condition value, should return false
- EXPECT_FALSE(arnNotEquals.eval({{key, "arn:aws:s3:::bucket2"}}));
+ EXPECT_FALSE(arnNotEquals.eval({{key, "arn:aws:s3:::bucket2"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition values
// Should return true because value differs from all condition values
{
arnNotEquals.vals.push_back("arn:aws:s3:::bucket3");
// Input doesn't match any condition value, should return true
- EXPECT_TRUE(arnNotEquals.eval({{key, "arn:aws:s3:::other-bucket"}}));
+ EXPECT_TRUE(arnNotEquals.eval({{key, "arn:aws:s3:::other-bucket"}},
+ nullptr));
}
}
TEST_F(ConditionTest, StringNotLikeLogic)
{
std::string key = "s3:prefix";
-
+
// Test case: value matches one of multiple condition patterns
// Should return false because value matches at least one condition pattern
{
stringNotLike.vals.push_back("temp/*");
// Input matches second condition pattern, should return false
- EXPECT_FALSE(stringNotLike.eval({{key, "admin/config.txt"}}));
+ EXPECT_FALSE(stringNotLike.eval({{key, "admin/config.txt"}}, nullptr));
// Input matches first condition pattern, should return false
- EXPECT_FALSE(stringNotLike.eval({{key, "user/profile.jpg"}}));
+ EXPECT_FALSE(stringNotLike.eval({{key, "user/profile.jpg"}}, nullptr));
}
-
+
// Test case: value doesn't match any condition patterns
// Should return true because value differs from all condition patterns
{
stringNotLike.vals.push_back("temp/*");
// Input doesn't match any condition pattern, should return true
- EXPECT_TRUE(stringNotLike.eval({{key, "public/document.pdf"}}));
+ EXPECT_TRUE(stringNotLike.eval({{key, "public/document.pdf"}}, nullptr));
}
}