]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: adds REST APIs for OpenID connect providers manipulation.
authorPritha Srivastava <prsrivas@redhat.com>
Sat, 28 Mar 2020 02:47:54 +0000 (08:17 +0530)
committerPritha Srivastava <prsrivas@redhat.com>
Fri, 5 Jun 2020 16:01:58 +0000 (21:31 +0530)
This commit implements:
1. CreateOpenIDConnectProvider
2. DeleteOpenIDConnectProvider
3. ListOpenIDConnectProviders
4. GetOpenIDConnectProviders

Signed-off-by: Pritha Srivastava <prsrivas@redhat.com>
12 files changed:
src/rgw/CMakeLists.txt
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_iam_policy.h
src/rgw/rgw_oidc_provider.cc [new file with mode: 0644]
src/rgw/rgw_oidc_provider.h [new file with mode: 0644]
src/rgw/rgw_rest_iam.cc
src/rgw/rgw_rest_oidc_provider.cc [new file with mode: 0644]
src/rgw/rgw_rest_oidc_provider.h [new file with mode: 0644]
src/rgw/rgw_rest_s3.cc
src/rgw/rgw_zone.cc
src/rgw/rgw_zone.h

index 02b82a0a65db4e9f9359f85999c6ca808adc7d99..03389dd7113d0a9c61e1898c27fec7b2f6266813 100644 (file)
@@ -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)
index 3978d8952380f66a7769650a28660c57ae8cc8a2..8e482e2144a47a6ca5041f026646c9b217445a0d 100644 (file)
@@ -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) {
index b58e062a426cdcfab56fc649cfa95daf806b0aef..d0cbcd1eb82d35f264306b2416e9a4821f96e4aa 100644 (file)
@@ -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;
index d66fd48dc9ee8a5f7a27457cf052de62efd84803..106af5b60cf807bed1f5caae7e99b4a96316057f 100644 (file)
@@ -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 (file)
index 0000000..ee28d2c
--- /dev/null
@@ -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 <errno.h>
+#include <ctime>
+#include <regex>
+
+#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<RGWOIDCProvider>& 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<string> result;
+  bool is_truncated;
+  RGWListRawObjsCtx ctx;
+  do {
+    list<string> 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 (file)
index 0000000..fc14d10
--- /dev/null
@@ -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 <string>
+
+#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<string> client_ids;
+  vector<string> 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<string> client_ids,
+                    vector<string> 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<string>& get_client_ids() const { return client_ids;}
+  const vector<string>& 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<RGWOIDCProvider>& providers);
+};
+WRITE_CLASS_ENCODER(RGWOIDCProvider)
+#endif /* CEPH_RGW_OIDC_PROVIDER_H */
+
index 554f10e8b3394973bf1ee21b9cfa4976f255f6ea..4f56d6334a0d460f3792cb629aa9ea5e27a7331b 100644 (file)
@@ -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 (file)
index 0000000..e2f6ddc
--- /dev/null
@@ -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 <errno.h>
+
+#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<RGWOIDCProvider> 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 (file)
index 0000000..bcebf95
--- /dev/null
@@ -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<string> client_ids;
+  vector<string> 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; }
+};
index 089fc7b7c02d436a669bb0b89e397b3993ceabd4..781df84a401bb3b108813f84a8768d268a4ae38c 100644 (file)
@@ -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;
   }
 
index 23a2b68327a0732561ba97f0b85d77b7429846b7..34c00014ad31ee92a8069bc5127660ab8a3a73e0 100644 (file)
@@ -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,
index b29575af7951c19432606d9571ba6ad68d0de323..aa0baa205ade8ca47ae617dd303a15014b0faa03 100644 (file)
@@ -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;