From aec4815c21dccd3be5766c985488fe4f68a41079 Mon Sep 17 00:00:00 2001 From: Pritha Srivastava Date: Sat, 28 Mar 2020 08:17:54 +0530 Subject: [PATCH] rgw: adds REST APIs for OpenID connect providers manipulation. This commit implements: 1. CreateOpenIDConnectProvider 2. DeleteOpenIDConnectProvider 3. ListOpenIDConnectProviders 4. GetOpenIDConnectProviders Signed-off-by: Pritha Srivastava --- src/rgw/CMakeLists.txt | 4 +- src/rgw/rgw_common.cc | 7 +- src/rgw/rgw_common.h | 6 + src/rgw/rgw_iam_policy.h | 6 +- src/rgw/rgw_oidc_provider.cc | 310 ++++++++++++++++++++++++++++++ src/rgw/rgw_oidc_provider.h | 124 ++++++++++++ src/rgw/rgw_rest_iam.cc | 9 + src/rgw/rgw_rest_oidc_provider.cc | 237 +++++++++++++++++++++++ src/rgw/rgw_rest_oidc_provider.h | 70 +++++++ src/rgw/rgw_rest_s3.cc | 6 +- src/rgw/rgw_zone.cc | 2 + src/rgw/rgw_zone.h | 11 +- 12 files changed, 786 insertions(+), 6 deletions(-) create mode 100644 src/rgw/rgw_oidc_provider.cc create mode 100644 src/rgw/rgw_oidc_provider.h create mode 100644 src/rgw/rgw_rest_oidc_provider.cc create mode 100644 src/rgw/rgw_rest_oidc_provider.h diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 02b82a0a65db4..03389dd7113d0 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -147,10 +147,12 @@ set(librgw_common_srcs rgw_sts.cc rgw_rest_sts.cc rgw_perf_counters.cc + rgw_rest_oidc_provider.cc rgw_rest_iam.cc rgw_object_lock.cc rgw_kms.cc - rgw_url.cc) + rgw_url.cc + rgw_oidc_provider) if(WITH_RADOSGW_AMQP_ENDPOINT) list(APPEND librgw_common_srcs rgw_amqp.cc) diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 3978d8952380f..8e482e2144a47 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -154,8 +154,12 @@ rgw_http_errors rgw_http_sts_errors({ }); rgw_http_errors rgw_http_iam_errors({ + { EINVAL, {400, "InvalidInput" }}, + { ENOENT, {404, "NoSuchEntity"}}, { ERR_ROLE_EXISTS, {409, "EntityAlreadyExists"}}, { ERR_DELETE_CONFLICT, {409, "DeleteConflict"}}, + { EEXIST, {409, "EntityAlreadyExists"}}, + { ERR_INTERNAL_ERROR, {500, "ServiceFailure" }}, }); using namespace ceph::crypto; @@ -1842,7 +1846,8 @@ bool RGWUserCaps::is_valid_cap_type(const string& tp) "datalog", "roles", "user-policy", - "amz-cache"}; + "amz-cache", + "oidc-provider"}; for (unsigned int i = 0; i < sizeof(cap_type) / sizeof(char *); ++i) { if (tp.compare(cap_type[i]) == 0) { diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index b58e062a426cd..d0cbcd1eb82d3 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -544,6 +544,12 @@ enum RGWOpType { RGW_OP_PUT_BUCKET_PUBLIC_ACCESS_BLOCK, RGW_OP_GET_BUCKET_PUBLIC_ACCESS_BLOCK, RGW_OP_DELETE_BUCKET_PUBLIC_ACCESS_BLOCK, + + /*OIDC provider specific*/ + RGW_OP_CREATE_OIDC_PROVIDER, + RGW_OP_DELETE_OIDC_PROVIDER, + RGW_OP_GET_OIDC_PROVIDER, + RGW_OP_LIST_OIDC_PROVIDERS, }; class RGWAccessControlPolicy; diff --git a/src/rgw/rgw_iam_policy.h b/src/rgw/rgw_iam_policy.h index d66fd48dc9ee8..106af5b60cf80 100644 --- a/src/rgw/rgw_iam_policy.h +++ b/src/rgw/rgw_iam_policy.h @@ -122,7 +122,11 @@ static constexpr std::uint64_t iamPutRolePolicy = s3All + 10; static constexpr std::uint64_t iamGetRolePolicy = s3All + 11; static constexpr std::uint64_t iamListRolePolicies = s3All + 12; static constexpr std::uint64_t iamDeleteRolePolicy = s3All + 13; -static constexpr std::uint64_t iamAll = s3All + 14; +static constexpr std::uint64_t iamCreateOIDCProvider = s3All + 14; +static constexpr std::uint64_t iamDeleteOIDCProvider = s3All + 15; +static constexpr std::uint64_t iamGetOIDCProvider = s3All + 16; +static constexpr std::uint64_t iamListOIDCProviders = s3All + 17; +static constexpr std::uint64_t iamAll = s3All + 18; static constexpr std::uint64_t stsAssumeRole = iamAll + 1; static constexpr std::uint64_t stsAssumeRoleWithWebIdentity = iamAll + 2; diff --git a/src/rgw/rgw_oidc_provider.cc b/src/rgw/rgw_oidc_provider.cc new file mode 100644 index 0000000000000..ee28d2cd162d7 --- /dev/null +++ b/src/rgw/rgw_oidc_provider.cc @@ -0,0 +1,310 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include +#include +#include + +#include "common/errno.h" +#include "common/Formatter.h" +#include "common/ceph_json.h" +#include "common/ceph_time.h" +#include "rgw_rados.h" +#include "rgw_zone.h" + +#include "include/types.h" +#include "rgw_string.h" + +#include "rgw_common.h" +#include "rgw_tools.h" +#include "rgw_oidc_provider.h" + +#include "services/svc_zone.h" +#include "services/svc_sys_obj.h" + +#define dout_subsys ceph_subsys_rgw + +const string RGWOIDCProvider::oidc_url_oid_prefix = "oidc_url."; +const string RGWOIDCProvider::oidc_arn_prefix = "arn:aws:iam::"; + +int RGWOIDCProvider::store_url(const string& url, bool exclusive) +{ + using ceph::encode; + string oid = tenant + get_url_oid_prefix() + url; + + auto svc = ctl->svc; + + bufferlist bl; + encode(*this, bl); + auto obj_ctx = svc->sysobj->init_obj_ctx(); + return rgw_put_system_obj(obj_ctx, svc->zone->get_zone_params().oidc_pool, oid, + bl, exclusive, NULL, real_time(), NULL); +} + +int RGWOIDCProvider::get_tenant_url_from_arn(string& tenant, string& url) +{ + auto provider_arn = rgw::ARN::parse(arn); + if (!provider_arn) { + return -EINVAL; + } + url = provider_arn->resource; + tenant = provider_arn->account; + auto pos = url.find("oidc-provider/"); + if (pos != std::string::npos) { + url.erase(pos, 14); + } + return 0; +} + +int RGWOIDCProvider::create(bool exclusive) +{ + int ret; + + if (! validate_input()) { + return -EINVAL; + } + + string idp_url = provider_url; + 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 { + pos = idp_url.find("www."); + if (pos != std::string::npos) { + idp_url.erase(pos, 4); + } + } + } else { + idp_url.erase(pos, 7); + } + + /* check to see the name is not used */ + ret = read_url(idp_url, tenant); + if (exclusive && ret == 0) { + ldout(cct, 0) << "ERROR: url " << provider_url << " already in use" + << id << dendl; + return -EEXIST; + } else if ( ret < 0 && ret != -ENOENT) { + ldout(cct, 0) << "failed reading provider url " << provider_url << ": " + << cpp_strerror(-ret) << dendl; + return ret; + } + + //arn + arn = oidc_arn_prefix + tenant + ":oidc-provider/" + idp_url; + + // Creation time + real_clock::time_point t = real_clock::now(); + + struct timeval tv; + real_clock::to_timeval(t, tv); + + char buf[30]; + struct tm result; + gmtime_r(&tv.tv_sec, &result); + strftime(buf,30,"%Y-%m-%dT%H:%M:%S", &result); + sprintf(buf + strlen(buf),".%dZ",(int)tv.tv_usec/1000); + creation_date.assign(buf, strlen(buf)); + + auto svc = ctl->svc; + + auto& pool = svc->zone->get_zone_params().oidc_pool; + ret = store_url(idp_url, exclusive); + if (ret < 0) { + ldout(cct, 0) << "ERROR: storing role info in pool: " << pool.name << ": " + << provider_url << ": " << cpp_strerror(-ret) << dendl; + return ret; + } + + return 0; +} + +int RGWOIDCProvider::delete_obj() +{ + auto svc = ctl->svc; + auto& pool = svc->zone->get_zone_params().oidc_pool; + + string url, tenant; + auto ret = get_tenant_url_from_arn(tenant, url); + if (ret < 0) { + ldout(cct, 0) << "ERROR: failed to parse arn" << dendl; + return -EINVAL; + } + + if (this->tenant != tenant) { + ldout(cct, 0) << "ERROR: tenant in arn doesn't match that of user " << this->tenant << ", " + << tenant << ": " << dendl; + return -EINVAL; + } + + // Delete url + string oid = tenant + get_url_oid_prefix() + url; + ret = rgw_delete_system_obj(svc->sysobj, pool, oid, NULL); + if (ret < 0) { + ldout(cct, 0) << "ERROR: deleting oidc url from pool: " << pool.name << ": " + << provider_url << ": " << cpp_strerror(-ret) << dendl; + } + + return ret; +} + +int RGWOIDCProvider::get() +{ + string url, tenant; + auto ret = get_tenant_url_from_arn(tenant, url); + if (ret < 0) { + ldout(cct, 0) << "ERROR: failed to parse arn" << dendl; + return -EINVAL; + } + + if (this->tenant != tenant) { + ldout(cct, 0) << "ERROR: tenant in arn doesn't match that of user " << this->tenant << ", " + << tenant << ": " << dendl; + return -EINVAL; + } + + ret = read_url(url, tenant); + if (ret < 0) { + return ret; + } + + return 0; +} + +void RGWOIDCProvider::dump(Formatter *f) const +{ + encode_json("OpenIDConnectProviderArn", arn, f); +} + +void RGWOIDCProvider::dump_all(Formatter *f) const +{ + f->open_object_section("ClientIDList"); + for (auto it : client_ids) { + encode_json("member", it, f); + } + f->close_section(); + encode_json("CreateDate", creation_date, f); + f->open_object_section("ThumbprintList"); + for (auto it : thumbprints) { + encode_json("member", it, f); + } + f->close_section(); + encode_json("Url", provider_url, f); +} + +void RGWOIDCProvider::decode_json(JSONObj *obj) +{ + JSONDecoder::decode_json("OpenIDConnectProviderArn", arn, obj); +} + +int RGWOIDCProvider::read_url(const string& url, const string& tenant) +{ + auto svc = ctl->svc; + auto& pool = svc->zone->get_zone_params().oidc_pool; + string oid = tenant + get_url_oid_prefix() + url; + bufferlist bl; + auto obj_ctx = svc->sysobj->init_obj_ctx(); + + int ret = rgw_get_system_obj(obj_ctx, pool, oid, bl, NULL, NULL, null_yield); + if (ret < 0) { + return ret; + } + + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(*this, iter); + } catch (buffer::error& err) { + ldout(cct, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << + ": " << url << dendl; + return -EIO; + } + + return 0; +} + +bool RGWOIDCProvider::validate_input() +{ + if (provider_url.length() > MAX_OIDC_URL_LEN) { + ldout(cct, 0) << "ERROR: Invalid length of url " << dendl; + return false; + } + if (client_ids.size() > MAX_OIDC_NUM_CLIENT_IDS) { + ldout(cct, 0) << "ERROR: Invalid number of client ids " << dendl; + return false; + } + + for (auto& it : client_ids) { + if (it.length() > MAX_OIDC_CLIENT_ID_LEN) { + return false; + } + } + + if (thumbprints.size() > MAX_OIDC_NUM_THUMBPRINTS) { + ldout(cct, 0) << "ERROR: Invalid number of thumbprints " << thumbprints.size() << dendl; + return false; + } + + for (auto& it : thumbprints) { + if (it.length() > MAX_OIDC_THUMBPRINT_LEN) { + return false; + } + } + + return true; +} + +int RGWOIDCProvider::get_providers(RGWRados *store, + const string& tenant, + vector& providers) +{ + auto ctl = store->pctl; + auto svc = ctl->svc; + auto pool = store->svc.zone->get_zone_params().oidc_pool; + string prefix = tenant + oidc_url_oid_prefix; + + //Get the filtered objects + list result; + bool is_truncated; + RGWListRawObjsCtx ctx; + do { + list oids; + int r = store->list_raw_objects(pool, prefix, 1000, ctx, oids, &is_truncated); + if (r < 0) { + ldout(ctl->cct, 0) << "ERROR: listing filtered objects failed: " << pool.name << ": " + << prefix << ": " << cpp_strerror(-r) << dendl; + return r; + } + for (const auto& iter : oids) { + RGWOIDCProvider provider(ctl->cct, store->pctl); + bufferlist bl; + auto obj_ctx = svc->sysobj->init_obj_ctx(); + + int ret = rgw_get_system_obj(obj_ctx, pool, iter, bl, NULL, NULL, null_yield); + if (ret < 0) { + return ret; + } + + try { + using ceph::decode; + auto iter = bl.cbegin(); + decode(provider, iter); + } catch (buffer::error& err) { + ldout(ctl->cct, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << + ": " << iter << dendl; + return -EIO; + } + + providers.push_back(std::move(provider)); + } + } while (is_truncated); + + return 0; +} + +const string& RGWOIDCProvider::get_url_oid_prefix() +{ + return oidc_url_oid_prefix; +} diff --git a/src/rgw/rgw_oidc_provider.h b/src/rgw/rgw_oidc_provider.h new file mode 100644 index 0000000000000..fc14d10ac0f85 --- /dev/null +++ b/src/rgw/rgw_oidc_provider.h @@ -0,0 +1,124 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#ifndef CEPH_RGW_OIDC_PROVIDER_H +#define CEPH_RGW_OIDC_PROVIDER_H + +#include + +#include "common/ceph_context.h" + +class RGWCtl; + +class RGWOIDCProvider +{ + using string = std::string; + static const string oidc_url_oid_prefix; + static const string oidc_arn_prefix; + static constexpr int MAX_OIDC_NUM_CLIENT_IDS = 100; + static constexpr int MAX_OIDC_CLIENT_ID_LEN = 255; + static constexpr int MAX_OIDC_NUM_THUMBPRINTS = 5; + static constexpr int MAX_OIDC_THUMBPRINT_LEN = 40; + static constexpr int MAX_OIDC_URL_LEN = 255; + + CephContext *cct; + RGWCtl *ctl; + string id; + string provider_url; + string arn; + string creation_date; + string tenant; + vector client_ids; + vector thumbprints; + + int get_tenant_url_from_arn(string& tenant, string& url); + int store_url(const string& url, bool exclusive); + int read_url(const string& url, const string& tenant); + bool validate_input(); + +public: + RGWOIDCProvider(CephContext *cct, + RGWCtl *ctl, + string provider_url, + string tenant, + vector client_ids, + vector thumbprints) + : cct(cct), + ctl(ctl), + provider_url(std::move(provider_url)), + tenant(std::move(tenant)), + client_ids(std::move(client_ids)), + thumbprints(std::move(thumbprints)) { + } + + RGWOIDCProvider(CephContext *cct, + RGWCtl *ctl, + string arn, + string tenant) + : cct(cct), + ctl(ctl), + arn(std::move(arn)), + tenant(std::move(tenant)) { + } + + RGWOIDCProvider(CephContext *cct, + RGWCtl *ctl, + string tenant) + : cct(cct), + ctl(ctl), + tenant(std::move(tenant)) {} + + RGWOIDCProvider(CephContext *cct, + RGWCtl *ctl) + : cct(cct), + ctl(ctl) {} + + RGWOIDCProvider() {} + + ~RGWOIDCProvider() = default; + + void encode(bufferlist& bl) const { + ENCODE_START(3, 1, bl); + encode(id, bl); + encode(provider_url, bl); + encode(arn, bl); + encode(creation_date, bl); + encode(tenant, bl); + encode(client_ids, bl); + encode(thumbprints, bl); + ENCODE_FINISH(bl); + } + + void decode(bufferlist::const_iterator& bl) { + DECODE_START(2, bl); + decode(id, bl); + decode(provider_url, bl); + decode(arn, bl); + decode(creation_date, bl); + decode(tenant, bl); + decode(client_ids, bl); + decode(thumbprints, bl); + DECODE_FINISH(bl); + } + + const string& get_provider_url() const { return provider_url; } + const string& get_arn() const { return arn; } + const string& get_create_date() const { return creation_date; } + const vector& get_client_ids() const { return client_ids;} + const vector& get_thumbprints() const { return thumbprints; } + + int create(bool exclusive); + int delete_obj(); + int get(); + void dump(Formatter *f) const; + void dump_all(Formatter *f) const; + void decode_json(JSONObj *obj); + + static const string& get_url_oid_prefix(); + static int get_providers(RGWRados *store, + const string& tenant, + vector& providers); +}; +WRITE_CLASS_ENCODER(RGWOIDCProvider) +#endif /* CEPH_RGW_OIDC_PROVIDER_H */ + diff --git a/src/rgw/rgw_rest_iam.cc b/src/rgw/rgw_rest_iam.cc index 554f10e8b3394..4f56d6334a0d4 100644 --- a/src/rgw/rgw_rest_iam.cc +++ b/src/rgw/rgw_rest_iam.cc @@ -11,6 +11,7 @@ #include "rgw_rest_role.h" #include "rgw_rest_user_policy.h" +#include "rgw_rest_oidc_provider.h" #define dout_context g_ceph_context #define dout_subsys ceph_subsys_rgw @@ -68,6 +69,14 @@ RGWOp *RGWHandler_REST_IAM::op_post() return new RGWListUserPolicies; if (action.compare("DeleteUserPolicy") == 0) return new RGWDeleteUserPolicy; + if (action.compare("CreateOpenIDConnectProvider") == 0) + return new RGWCreateOIDCProvider; + if (action.compare("ListOpenIDConnectProviders") == 0) + return new RGWListOIDCProviders; + if (action.compare("GetOpenIDConnectProvider") == 0) + return new RGWGetOIDCProvider; + if (action.compare("DeleteOpenIDConnectProvider") == 0) + return new RGWDeleteOIDCProvider; } return nullptr; diff --git a/src/rgw/rgw_rest_oidc_provider.cc b/src/rgw/rgw_rest_oidc_provider.cc new file mode 100644 index 0000000000000..e2f6ddc00d6b0 --- /dev/null +++ b/src/rgw/rgw_rest_oidc_provider.cc @@ -0,0 +1,237 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#include + +#include "common/errno.h" +#include "common/Formatter.h" +#include "common/ceph_json.h" + +#include "include/types.h" +#include "rgw_string.h" + +#include "rgw_common.h" +#include "rgw_op.h" +#include "rgw_rest.h" +#include "rgw_role.h" +#include "rgw_rest_oidc_provider.h" +#include "rgw_oidc_provider.h" + +#define dout_subsys ceph_subsys_rgw + +int RGWRestOIDCProvider::verify_permission() +{ + if (s->auth.identity->is_anonymous()) { + return -EACCES; + } + + provider_arn = s->info.args.get("OpenIDConnectProviderArn"); + if (provider_arn.empty()) { + ldout(s->cct, 20) << "ERROR: Provider ARN is empty"<< dendl; + return -EINVAL; + } + + auto ret = check_caps(s->user->get_caps()); + if (ret == 0) { + return ret; + } + + uint64_t op = get_op(); + auto rgw_arn = rgw::ARN::parse(provider_arn, true); + if (rgw_arn) { + if (!verify_user_permission(this, s, *rgw_arn, op)) { + return -EACCES; + } + } else { + return -EACCES; + } + + return 0; +} + +void RGWRestOIDCProvider::send_response() +{ + if (op_ret) { + set_req_state_err(s, op_ret); + } + dump_errno(s); + end_header(s, this); +} + +int RGWRestOIDCProviderRead::check_caps(const RGWUserCaps& caps) +{ + return caps.check_cap("oidc-provider", RGW_CAP_READ); +} + +int RGWRestOIDCProviderWrite::check_caps(const RGWUserCaps& caps) +{ + return caps.check_cap("oidc-provider", RGW_CAP_WRITE); +} + +int RGWCreateOIDCProvider::verify_permission() +{ + if (s->auth.identity->is_anonymous()) { + return -EACCES; + } + + auto ret = check_caps(s->user->get_caps()); + if (ret == 0) { + return ret; + } + + string idp_url = provider_url; + 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 { + pos = idp_url.find("www."); + if (pos != std::string::npos) { + idp_url.erase(pos, 4); + } + } + } else { + idp_url.erase(pos, 7); + } + if (!verify_user_permission(this, + s, + rgw::ARN(idp_url, + "oidc-provider", + s->user->get_tenant(), true), + get_op())) { + return -EACCES; + } + return 0; +} + +int RGWCreateOIDCProvider::get_params() +{ + provider_url = s->info.args.get("Url"); + + auto val_map = s->info.args.get_params(); + for (auto& it : val_map) { + if (it.first.find("ClientIDList.member.") != string::npos) { + client_ids.emplace_back(it.second); + } + if (it.first.find("ThumbprintList.member.") != string::npos) { + thumbprints.emplace_back(it.second); + } + } + + if (provider_url.empty() || thumbprints.empty()) { + ldout(s->cct, 20) << "ERROR: one of url or thumbprints is empty" << dendl; + return -EINVAL; + } + + return 0; +} + +void RGWCreateOIDCProvider::execute() +{ + op_ret = get_params(); + if (op_ret < 0) { + return; + } + + RGWOIDCProvider provider(s->cct, store->getRados()->pctl, provider_url, + s->user->get_tenant(), client_ids, thumbprints); + op_ret = provider.create(true); + + if (op_ret == 0) { + s->formatter->open_object_section("CreateOpenIDConnectProviderResponse"); + s->formatter->open_object_section("CreateOpenIDConnectProviderResult"); + provider.dump(s->formatter); + s->formatter->close_section(); + s->formatter->open_object_section("ResponseMetadata"); + s->formatter->dump_string("RequestId", s->trans_id); + s->formatter->close_section(); + s->formatter->close_section(); + } + +} + +void RGWDeleteOIDCProvider::execute() +{ + RGWOIDCProvider provider(s->cct, store->getRados()->pctl, provider_arn, s->user->get_tenant()); + op_ret = provider.delete_obj(); + + if (op_ret < 0 && op_ret != -ENOENT && op_ret != -EINVAL) { + op_ret = ERR_INTERNAL_ERROR; + } + + if (op_ret == 0) { + s->formatter->open_object_section("DeleteOpenIDConnectProviderResponse"); + s->formatter->open_object_section("ResponseMetadata"); + s->formatter->dump_string("RequestId", s->trans_id); + s->formatter->close_section(); + s->formatter->close_section(); + } +} + +void RGWGetOIDCProvider::execute() +{ + RGWOIDCProvider provider(s->cct, store->getRados()->pctl, provider_arn, s->user->get_tenant()); + op_ret = provider.get(); + + if (op_ret < 0 && op_ret != -ENOENT && op_ret != -EINVAL) { + op_ret = ERR_INTERNAL_ERROR; + } + + if (op_ret == 0) { + s->formatter->open_object_section("GetOpenIDConnectProviderResponse"); + s->formatter->open_object_section("ResponseMetadata"); + s->formatter->dump_string("RequestId", s->trans_id); + s->formatter->close_section(); + s->formatter->open_object_section("GetOpenIDConnectProviderResult"); + provider.dump_all(s->formatter); + s->formatter->close_section(); + s->formatter->close_section(); + } +} + +int RGWListOIDCProviders::verify_permission() +{ + if (s->auth.identity->is_anonymous()) { + return -EACCES; + } + + if (int ret = check_caps(s->user->get_caps()); ret == 0) { + return ret; + } + + if (!verify_user_permission(this, + s, + rgw::ARN(), + get_op())) { + return -EACCES; + } + + return 0; +} + +void RGWListOIDCProviders::execute() +{ + vector result; + op_ret = RGWOIDCProvider::get_providers(store->getRados(), s->user->get_tenant(), result); + + if (op_ret == 0) { + s->formatter->open_array_section("ListOpenIDConnectProvidersResponse"); + s->formatter->open_object_section("ResponseMetadata"); + s->formatter->dump_string("RequestId", s->trans_id); + s->formatter->close_section(); + s->formatter->open_object_section("ListOpenIDConnectProvidersResult"); + s->formatter->open_array_section("OpenIDConnectProviderList"); + for (const auto& it : result) { + s->formatter->open_object_section("Arn"); + auto& arn = it.get_arn(); + ldout(s->cct, 0) << "ARN: " << arn << dendl; + s->formatter->dump_string("Arn", arn); + s->formatter->close_section(); + } + s->formatter->close_section(); + s->formatter->close_section(); + s->formatter->close_section(); + } +} + diff --git a/src/rgw/rgw_rest_oidc_provider.h b/src/rgw/rgw_rest_oidc_provider.h new file mode 100644 index 0000000000000..bcebf95480ca1 --- /dev/null +++ b/src/rgw/rgw_rest_oidc_provider.h @@ -0,0 +1,70 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab ft=cpp + +#pragma once + +#include "rgw_oidc_provider.h" + +class RGWRestOIDCProvider : public RGWRESTOp { +protected: + vector client_ids; + vector thumbprints; + string provider_url; //'iss' field in JWT + string provider_arn; +public: + int verify_permission() override; + void send_response() override; + virtual uint64_t get_op() = 0; +}; + +class RGWRestOIDCProviderRead : public RGWRestOIDCProvider { +public: + RGWRestOIDCProviderRead() = default; + int check_caps(const RGWUserCaps& caps) override; +}; + +class RGWRestOIDCProviderWrite : public RGWRestOIDCProvider { +public: + RGWRestOIDCProviderWrite() = default; + int check_caps(const RGWUserCaps& caps) override; +}; + +class RGWCreateOIDCProvider : public RGWRestOIDCProviderWrite { +public: + RGWCreateOIDCProvider() = default; + int verify_permission() override; + void execute() override; + int get_params(); + const char* name() const override { return "create_oidc_provider"; } + RGWOpType get_type() override { return RGW_OP_CREATE_OIDC_PROVIDER; } + uint64_t get_op() { return rgw::IAM::iamCreateOIDCProvider; } +}; + +class RGWDeleteOIDCProvider : public RGWRestOIDCProviderWrite { +public: + RGWDeleteOIDCProvider() = default; + void execute() override; + const char* name() const override { return "delete_oidc_provider"; } + RGWOpType get_type() override { return RGW_OP_DELETE_OIDC_PROVIDER; } + uint64_t get_op() { return rgw::IAM::iamDeleteOIDCProvider; } +}; + +class RGWGetOIDCProvider : public RGWRestOIDCProviderRead { +public: + RGWGetOIDCProvider() = default; + void execute() override; + const char* name() const override { return "get_oidc_provider"; } + RGWOpType get_type() override { return RGW_OP_GET_OIDC_PROVIDER; } + uint64_t get_op() { return rgw::IAM::iamGetOIDCProvider; } +}; + +class RGWListOIDCProviders : public RGWRestOIDCProviderRead { +public: + RGWListOIDCProviders() = default; + int verify_permission() override; + void execute() override; + int get_params(); + const char* name() const override { return "list_oidc_providers"; } + RGWOpType get_type() override { return RGW_OP_LIST_OIDC_PROVIDERS; } + uint64_t get_op() { return rgw::IAM::iamListOIDCProviders; } +}; diff --git a/src/rgw/rgw_rest_s3.cc b/src/rgw/rgw_rest_s3.cc index 089fc7b7c02d4..781df84a401bb 100644 --- a/src/rgw/rgw_rest_s3.cc +++ b/src/rgw/rgw_rest_s3.cc @@ -5218,7 +5218,11 @@ AWSGeneralAbstractor::get_auth_data_v4(const req_state* const s, s->op_type == RGW_OP_PUT_USER_POLICY || s->op_type == RGW_OP_GET_USER_POLICY || s->op_type == RGW_OP_LIST_USER_POLICIES || - s->op_type == RGW_OP_DELETE_USER_POLICY) { + s->op_type == RGW_OP_DELETE_USER_POLICY || + s->op_type == RGW_OP_CREATE_OIDC_PROVIDER || + s->op_type == RGW_OP_DELETE_OIDC_PROVIDER || + s->op_type == RGW_OP_GET_OIDC_PROVIDER || + s->op_type == RGW_OP_LIST_OIDC_PROVIDERS) { is_non_s3_op = true; } diff --git a/src/rgw/rgw_zone.cc b/src/rgw/rgw_zone.cc index 23a2b68327a07..34c00014ad31e 100644 --- a/src/rgw/rgw_zone.cc +++ b/src/rgw/rgw_zone.cc @@ -1557,6 +1557,7 @@ int get_zones_pool_set(CephContext* cct, } pool_names.insert(iter.second.data_extra_pool); } + pool_names.insert(zone.oidc_pool); } } return 0; @@ -1621,6 +1622,7 @@ int RGWZoneParams::fix_pool_names() roles_pool = fix_zone_pool_dup(pools, name, ".rgw.meta:roles", roles_pool); reshard_pool = fix_zone_pool_dup(pools, name, ".rgw.log:reshard", reshard_pool); otp_pool = fix_zone_pool_dup(pools, name, ".rgw.otp", otp_pool); + oidc_pool = fix_zone_pool_dup(pools, name, ".rgw.meta:oidc", oidc_pool); for(auto& iter : placement_pools) { iter.second.index_pool = fix_zone_pool_dup(pools, name, "." + default_bucket_index_pool_suffix, diff --git a/src/rgw/rgw_zone.h b/src/rgw/rgw_zone.h index b29575af7951c..aa0baa205ade8 100644 --- a/src/rgw/rgw_zone.h +++ b/src/rgw/rgw_zone.h @@ -372,6 +372,7 @@ struct RGWZoneParams : RGWSystemMetaObj { rgw_pool roles_pool; rgw_pool reshard_pool; rgw_pool otp_pool; + rgw_pool oidc_pool; RGWAccessKey system_key; @@ -405,7 +406,7 @@ struct RGWZoneParams : RGWSystemMetaObj { const string& get_compression_type(const rgw_placement_rule& placement_rule) const; void encode(bufferlist& bl) const override { - ENCODE_START(12, 1, bl); + ENCODE_START(13, 1, bl); encode(domain_root, bl); encode(control_pool, bl); encode(gc_pool, bl); @@ -429,11 +430,12 @@ struct RGWZoneParams : RGWSystemMetaObj { encode(reshard_pool, bl); encode(otp_pool, bl); encode(tier_config, bl); + encode(oidc_pool, bl); ENCODE_FINISH(bl); } void decode(bufferlist::const_iterator& bl) override { - DECODE_START(12, bl); + DECODE_START(13, bl); decode(domain_root, bl); decode(control_pool, bl); decode(gc_pool, bl); @@ -492,6 +494,11 @@ struct RGWZoneParams : RGWSystemMetaObj { tier_config.set(kv.first, kv.second); } } + if (struct_v >= 13) { + ::decode(oidc_pool, bl); + } else { + oidc_pool = name + ".rgw.oidc"; + } DECODE_FINISH(bl); } void dump(Formatter *f) const; -- 2.39.5