cout << " subscription ack ack (remove) an events in a pubsub subscription\n";
cout << "options:\n";
cout << " --tenant=<tenant> tenant name\n";
+ cout << " --user_ns=<namespace> namespace of user (oidc in case of users authenticated with oidc provider)\n";
cout << " --uid=<id> user id\n";
cout << " --new-uid=<id> new user id\n";
cout << " --subuser=<name> subuser name\n";
rgw_user user_id;
string tenant;
+ string user_ns;
rgw_user new_user_id;
std::string access_key, secret_key, user_email, display_name;
std::string bucket_name, pool_name, object;
} else if (ceph_argparse_witharg(args, i, &val, "--tenant", (char*)NULL)) {
tenant = val;
opt_tenant = val;
+ } else if (ceph_argparse_witharg(args, i, &val, "--user_ns", (char*)NULL)) {
+ user_ns = val;
} else if (ceph_argparse_witharg(args, i, &val, "--access-key", (char*)NULL)) {
access_key = val;
} else if (ceph_argparse_witharg(args, i, &val, "--subuser", (char*)NULL)) {
}
user_id.tenant = tenant;
}
+ if (user_ns.empty()) {
+ user_ns = user_id.ns;
+ } else {
+ user_id.ns = user_ns;
+ }
if (!new_user_id.empty() && !tenant.empty()) {
new_user_id.tenant = tenant;
return idp_url;
}
+void rgw::auth::WebIdentityApplier::create_account(const DoutPrefixProvider* dpp,
+ const rgw_user& acct_user,
+ const string& display_name,
+ RGWUserInfo& user_info) const /* out */
+{
+ user_info.user_id = acct_user;
+ user_info.display_name = display_name;
+ user_info.type = TYPE_WEB;
+
+ user_info.max_buckets =
+ cct->_conf.get_val<int64_t>("rgw_user_max_buckets");
+ rgw_apply_default_bucket_quota(user_info.bucket_quota, cct->_conf);
+ rgw_apply_default_user_quota(user_info.user_quota, cct->_conf);
+
+ int ret = ctl->user->store_info(user_info, null_yield,
+ RGWUserCtl::PutParams().set_exclusive(true));
+ if (ret < 0) {
+ ldpp_dout(dpp, 0) << "ERROR: failed to store new user info: user="
+ << user_info.user_id << " ret=" << ret << dendl;
+ throw ret;
+ }
+}
+
+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.tenant = role_tenant;
+ federated_user.ns = "oidc";
+
+ if (ctl->user->get_info_by_uid(federated_user, &user_info, null_yield) >= 0) {
+ /* Succeeded. */
+ return;
+ }
+
+ 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);
+}
+
void rgw::auth::WebIdentityApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const
{
s->info.args.append("sub", token_claims.sub);
}
} else {
string id = p.get_id();
- if (user_id.id == id) {
+ string tenant = p.get_tenant();
+ string oidc_id;
+ if (user_id.ns.empty()) {
+ oidc_id = user_id.id;
+ } else {
+ oidc_id = user_id.ns + "$" + user_id.id;
+ }
+ if (oidc_id == id && user_id.tenant == tenant) {
return true;
}
}
{
/* Load the user id */
user_info.user_id = this->user_id;
-
- user_info.user_id.tenant = role.tenant;
}
void rgw::auth::RoleApplier::modify_request_state(const DoutPrefixProvider *dpp, req_state* s) const
/* Subuser of Account */
virtual string get_subuser() const = 0;
+
+ virtual string get_role_tenant() const { return ""; }
};
inline std::ostream& operator<<(std::ostream& out,
CephContext* const cct;
RGWCtl* const ctl;
string role_session;
+ string role_tenant;
rgw::web_idp::WebTokenClaims token_claims;
string get_idp_url() const;
+ void create_account(const DoutPrefixProvider* dpp,
+ const rgw_user& acct_user,
+ const string& display_name,
+ RGWUserInfo& user_info) const; /* out */
public:
WebIdentityApplier( CephContext* const cct,
RGWCtl* const ctl,
const string& role_session,
+ const string& role_tenant,
const rgw::web_idp::WebTokenClaims& token_claims)
: cct(cct),
ctl(ctl),
role_session(role_session),
+ role_tenant(role_tenant),
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 {
}
bool is_owner_of(const rgw_user& uid) const override {
+ if (uid.id == token_claims.sub && uid.tenant == role_tenant && uid.ns == "oidc") {
+ return true;
+ }
return false;
}
bool is_identity(const idset_t& ids) const override;
+ void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override;
+
uint32_t get_identity_type() const override {
return TYPE_WEB;
}
virtual aplptr_t create_apl_web_identity( CephContext* cct,
const req_state* s,
const string& role_session,
+ const string& role_tenant,
const rgw::web_idp::WebTokenClaims& token) const = 0;
};
};
return false;
}
bool is_owner_of(const rgw_user& uid) const override {
- return false;
+ return (this->user_id.id == uid.id && this->user_id.tenant == uid.tenant && this->user_id.ns == this->user_id.ns);
}
bool is_identity(const idset_t& ids) const override;
uint32_t get_perm_mask() const override {
string get_acct_name() const override { return {}; }
string get_subuser() const override { return {}; }
void modify_request_state(const DoutPrefixProvider* dpp, req_state* s) const override;
+ string get_role_tenant() const override { return role.tenant; }
struct Factory {
virtual ~Factory() {}
get_decoratee().to_str(out);
}
+ string get_role_tenant() const override { /* in/out */
+ return get_decoratee().get_role_tenant();
+ }
+
void load_acct_info(const DoutPrefixProvider* dpp, RGWUserInfo& user_info) const override { /* out */
return get_decoratee().load_acct_info(dpp, user_info);
}
struct rgw_user {
std::string tenant;
std::string id;
+ std::string ns;
rgw_user() {}
explicit rgw_user(const std::string& s) {
from_str(s);
}
- rgw_user(const std::string& tenant, const std::string& id)
+ rgw_user(const std::string& tenant, const std::string& id, const std::string& ns="")
: tenant(tenant),
- id(id) {
+ id(id),
+ ns(ns) {
}
- rgw_user(std::string&& tenant, std::string&& id)
+ rgw_user(std::string&& tenant, std::string&& id, std::string&& ns="")
: tenant(std::move(tenant)),
- id(std::move(id)) {
+ id(std::move(id)),
+ ns(std::move(ns)) {
}
void encode(ceph::buffer::list& bl) const {
- ENCODE_START(1, 1, bl);
+ ENCODE_START(2, 1, bl);
encode(tenant, bl);
encode(id, bl);
+ encode(ns, bl);
ENCODE_FINISH(bl);
}
void decode(ceph::buffer::list::const_iterator& bl) {
- DECODE_START(1, bl);
+ DECODE_START(2, bl);
decode(tenant, bl);
decode(id, bl);
+ if (struct_v >= 2) {
+ decode(ns, bl);
+ }
DECODE_FINISH(bl);
}
void to_str(std::string& str) const {
if (!tenant.empty()) {
- str = tenant + '$' + id;
+ if (!ns.empty()) {
+ str = tenant + '$' + ns + '$' + id;
+ } else {
+ str = tenant + '$' + id;
+ }
+ } else if (!ns.empty()) {
+ str = '$' + ns + '$' + id;
} else {
str = id;
}
void clear() {
tenant.clear();
id.clear();
+ ns.clear();
}
bool empty() const {
size_t pos = str.find('$');
if (pos != std::string::npos) {
tenant = str.substr(0, pos);
- id = str.substr(pos + 1);
+ string_view sv = str;
+ string_view ns_id = sv.substr(pos + 1);
+ size_t ns_pos = ns_id.find('$');
+ if (ns_pos != std::string::npos) {
+ ns = string(ns_id.substr(0, ns_pos));
+ id = string(ns_id.substr(ns_pos + 1));
+ } else {
+ ns.clear();
+ id = string(ns_id);
+ }
} else {
tenant.clear();
+ ns.clear();
id = str;
}
}
int r = tenant.compare(u.tenant);
if (r != 0)
return r;
-
+ r = ns.compare(u.ns);
+ if (r != 0) {
+ return r;
+ }
return id.compare(u.id);
}
int compare(const std::string& str) const {
} else if (tenant > rhs.tenant) {
return false;
}
+ if (ns < rhs.ns) {
+ return true;
+ } else if (ns > rhs.ns) {
+ return false;
+ }
return (id < rhs.id);
}
void dump(ceph::Formatter *f) const;
}
void RGWBucketInfo::encode(bufferlist& bl) const {
- ENCODE_START(22, 4, bl);
+ ENCODE_START(23, 4, bl);
encode(bucket, bl);
encode(owner.id, bl);
encode(flags, bl);
encode(*sync_policy, bl);
}
encode(layout, bl);
+ encode(owner.ns, bl);
ENCODE_FINISH(bl);
}
void RGWBucketInfo::decode(bufferlist::const_iterator& bl) {
- DECODE_START_LEGACY_COMPAT_LEN_32(22, 4, 4, bl);
+ DECODE_START_LEGACY_COMPAT_LEN_32(23, 4, 4, bl);
decode(bucket, bl);
if (struct_v >= 2) {
string s;
if (struct_v >= 22) {
decode(layout, bl);
}
-
+ if (struct_v >= 23) {
+ decode(owner.ns, bl);
+ }
DECODE_FINISH(bl);
}
rgw::Partition partition = rgw::Partition::aws;
rgw::Service service = rgw::Service::s3;
- if (!verify_user_permission(this, s, ARN(partition, service, "", s->user->get_tenant(), "*"), rgw::IAM::s3ListAllMyBuckets)) {
+ string tenant;
+ if (s->auth.identity->get_identity_type() == TYPE_ROLE) {
+ tenant = s->auth.identity->get_role_tenant();
+ } else {
+ tenant = s->user->get_tenant();
+ }
+
+ if (!verify_user_permission(this, s, ARN(partition, service, "", tenant, "*"), rgw::IAM::s3ListAllMyBuckets)) {
return -EACCES;
}
}
if (s->user->get_tenant() != s->bucket_tenant) {
- ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant"
- << " (user_id.tenant=" << s->user->get_tenant()
- << " requested=" << s->bucket_tenant << ")"
- << dendl;
- return -EACCES;
+ //AssumeRole is meant for cross account access
+ if (s->auth.identity->get_identity_type() != TYPE_ROLE) {
+ ldpp_dout(this, 10) << "user cannot create a bucket in a different tenant"
+ << " (user_id.tenant=" << s->user->get_tenant()
+ << " requested=" << s->bucket_tenant << ")"
+ << dendl;
+ return -EACCES;
+ }
}
+
if (s->user->get_max_buckets() < 0) {
return -EPERM;
}
rgw_parse_url_bucket(t->url_bucket, s->user->get_tenant(),
s->bucket_tenant, s->bucket_name);
+ if (s->auth.identity->get_identity_type() == TYPE_ROLE) {
+ s->bucket_tenant = s->auth.identity->get_role_tenant();
+ }
+
dout(10) << "s->object=" << s->object
<< " s->bucket=" << rgw_make_bucket_entry_name(s->bucket_tenant, s->bucket_name) << dendl;
return ! token.empty();
}
-boost::optional<RGWOIDCProvider>
-WebTokenEngine::get_provider(const string& role_arn, const string& iss) const
+std::string
+WebTokenEngine::get_role_tenant(const string& role_arn) const
{
string tenant;
auto r_arn = rgw::ARN::parse(role_arn);
if (r_arn) {
tenant = r_arn->account;
}
+ return tenant;
+}
+
+boost::optional<RGWOIDCProvider>
+WebTokenEngine::get_provider(const string& role_arn, const string& iss) const
+{
+ string tenant = get_role_tenant(role_arn);
+
string idp_url = iss;
auto pos = idp_url.find("http://");
if (pos == std::string::npos) {
if (role_session.empty()) {
return result_t::deny(-EACCES);
}
- auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, *t);
+ string role_arn = s->info.args.get("RoleArn");
+ string role_tenant = get_role_tenant(role_arn);
+ auto apl = apl_factory->create_apl_web_identity(cct, s, role_session, role_tenant, *t);
return result_t::grant(std::move(apl));
}
return result_t::deny(-EACCES);
boost::optional<RGWOIDCProvider> get_provider(const string& role_arn, const string& iss) const;
+ std::string get_role_tenant(const string& role_arn) const;
+
boost::optional<WebTokenEngine::token_t>
get_from_jwt(const DoutPrefixProvider* dpp, const std::string& token, const req_state* const s) const;
aplptr_t create_apl_web_identity( CephContext* cct,
const req_state* s,
const string& role_session,
+ const string& role_tenant,
const rgw::web_idp::WebTokenClaims& token) const override {
auto apl = rgw::auth::add_sysreq(cct, ctl, s,
- rgw::auth::WebIdentityApplier(cct, ctl, role_session, token));
+ rgw::auth::WebIdentityApplier(cct, ctl, role_session, role_tenant, token));
return aplptr_t(new decltype(apl)(std::move(apl)));
}
if (user)
token.user = *user;
else {
- rgw_user u({}, {});
+ rgw_user u({}, {}, {});
token.user = u;
}
subscription ack ack (remove) an events in a pubsub subscription
options:
--tenant=<tenant> tenant name
+ --user_ns=<namespace> namespace of user (oidc in case of users authenticated with oidc provider)
--uid=<id> user id
--new-uid=<id> new user id
--subuser=<name> subuser name