From: Krunal Chheda Date: Wed, 25 Feb 2026 14:53:00 +0000 (-0500) Subject: rgw/oidc: add rgwrados::oidc interface to support multisite. X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=f35001b4aa265442730f1c77d2a25094d4fbaa69;p=ceph.git rgw/oidc: add rgwrados::oidc interface to support multisite. add a new interface for oidc metadata that is used by both RadosStore and the oidc metadata handler for replication. Signed-off-by: Krunal Chheda --- diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index 9c7fe7432abd..41b349bdd9fb 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -216,6 +216,7 @@ if(WITH_RADOSGW_RADOS) driver/rados/rgw_trim_datalog.cc driver/rados/rgw_trim_mdlog.cc driver/rados/rgw_user.cc + driver/rados/oidc.cc driver/rados/role.cc driver/rados/roles.cc driver/rados/sync_fairness.cc diff --git a/src/rgw/driver/rados/oidc.cc b/src/rgw/driver/rados/oidc.cc new file mode 100644 index 000000000000..d4df60ef5e97 --- /dev/null +++ b/src/rgw/driver/rados/oidc.cc @@ -0,0 +1,467 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- +// vim: ts=8 sw=2 sts=2 expandtab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright contributors to the Ceph project + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "oidc.h" + +#include "common/errno.h" +#include "rgw_common.h" +#include "rgw_metadata.h" +#include "rgw_metadata_lister.h" +#include "rgw_oidc_provider.h" +#include "rgw_string.h" +#include "rgw_tools.h" +#include "rgw_zone.h" +#include "svc_mdlog.h" + +namespace rgwrados::oidc { + +// OIDCProviderInfo is stored in rados objects named "{tenant}oidc_url.{url}" +static constexpr std::string_view oidc_url_oid_prefix = "oidc_url."; + +// Metadata keys use '$' as separator: "{tenant}${url}" +static constexpr char metadata_key_separator = '$'; + +// Build the rados oid from tenant and url +static std::string +get_oidc_oid(std::string_view tenant, std::string_view url) +{ + return string_cat_reserve(tenant, oidc_url_oid_prefix, url); +} + +// Build the metadata key from tenant and url +std::string +get_oidc_metadata_key(std::string_view tenant, std::string_view url) +{ + return string_cat_reserve( + tenant, std::string_view(&metadata_key_separator, 1), url); +} + +// Parse metadata key "{tenant}${url}" into tenant and url +void +parse_oidc_metadata_key( + const std::string& key, + std::string& tenant, + std::string& url) +{ + auto pos = key.find(metadata_key_separator); + if (pos == std::string::npos) { + tenant.clear(); + url = key; + } else { + tenant = key.substr(0, pos); + url = key.substr(pos + 1); + } +} + +// Convert rados oid "{tenant}oidc_url.{url}" to metadata key "{tenant}${url}" +static std::string +oid_to_metadata_key(const std::string& oid) +{ + auto pos = oid.find(oidc_url_oid_prefix); + if (pos == std::string::npos) { + return oid; // shouldn't happen + } + std::string tenant = oid.substr(0, pos); + std::string url = oid.substr(pos + oidc_url_oid_prefix.size()); + return get_oidc_metadata_key(tenant, url); +} + +static rgw_raw_obj +get_oidc_obj( + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view url) +{ + return {zone.oidc_pool, get_oidc_oid(tenant, url)}; +} + + +int +read( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view url, + RGWOIDCProviderInfo& info, + ceph::real_time* pmtime, + RGWObjVersionTracker* pobjv) +{ + const rgw_raw_obj& obj = get_oidc_obj(zone, tenant, url); + + bufferlist bl; + int r = rgw_get_system_obj( + &sysobj, obj.pool, obj.oid, bl, pobjv, + pmtime, y, dpp); + if (r < 0) { + return r; + } + + try { + auto p = bl.cbegin(); + decode(info, p); + } catch (const buffer::error&) { + ldpp_dout( + dpp, 0) << "ERROR: failed to decode OIDC provider info from pool: " + << obj.pool << ": " << obj.oid << dendl; + return -EIO; + } + + return 0; +} + +int +write( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + RGWSI_MDLog* mdlog, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWOIDCProviderInfo& info, + RGWObjVersionTracker& objv, + ceph::real_time mtime, + bool exclusive) +{ + const std::string url = url_remove_prefix(info.provider_url); + const rgw_raw_obj obj = get_oidc_obj(zone, info.tenant, url); + + bufferlist bl; + encode(info, bl); + + int r = rgw_put_system_obj( + dpp, &sysobj, obj.pool, obj.oid, + bl, exclusive, &objv, mtime, y); + if (r < 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to write OIDC provider obj " << obj.oid + << " with: " << cpp_strerror(r) << dendl; + return r; + } + + + // record in the mdlog on success + if (mdlog) { + const std::string oidc_key = get_oidc_metadata_key(info.tenant, url); + return mdlog->complete_entry(dpp, y, "oidc", oidc_key, &objv); + } + return 0; +} + +int +remove( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + RGWSI_MDLog* mdlog, + librados::Rados& rados, + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view url, + RGWObjVersionTracker& objv) +{ + const std::string oidc_key = get_oidc_metadata_key(tenant, url); + const rgw_raw_obj obj = get_oidc_obj(zone, tenant, url); + + // delete OIDC provider info + int r = rgw_delete_system_obj(dpp, &sysobj, obj.pool, obj.oid, &objv, y); + if (r < 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to remove OIDC provider obj " + << obj.oid << " with: " << cpp_strerror(r) << dendl; + return r; + } + + + // record in the mdlog on success + if (mdlog) { + return mdlog->complete_entry(dpp, y, "oidc", oidc_key, &objv); + } + return 0; +} + +int +list( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + std::string_view tenant, + std::vector& providers) +{ + // Prefix format: "{tenant}oidc_url." to match original implementation + std::string prefix = string_cat_reserve(tenant, oidc_url_oid_prefix); + auto& pool = zone.oidc_pool; + + // List all objects with the tenant prefix + auto listing = sysobj.get_pool(pool); + auto op = listing.op(); + int r = op.init(dpp, "", prefix); + if (r < 0) { + return r; + } + + std::vector oids; + bool truncated = false; + do { + r = op.get_next(dpp, 1000, &oids, &truncated); + if (r == -ENOENT) { + return 0; + } + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: listing OIDC providers in pool " + << pool << " with prefix " << prefix << ": " << cpp_strerror(r) << + dendl; + return r; + } + + for (const auto& oid : oids) { + bufferlist bl; + r = rgw_get_system_obj(&sysobj, pool, oid, bl, nullptr, nullptr, y, dpp); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: failed to read OIDC provider " << oid + << ": " << cpp_strerror(r) << dendl; + return r; + } + + RGWOIDCProviderInfo info; + try { + auto iter = bl.cbegin(); + decode(info, iter); + } catch (const buffer::error& err) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode OIDC provider info from " + << oid << dendl; + return -EIO; + } + + providers.push_back(std::move(info)); + } + } while (truncated); + + return 0; +} + + +class MetadataObject : public RGWMetadataObject { + RGWOIDCProviderInfo info; + +public: + MetadataObject( + const RGWOIDCProviderInfo& info, + const obj_version& v, + real_time m) + : RGWMetadataObject(v, m), info(info) + { + } + + void + dump(Formatter* f) const override + { + info.dump(f); + } + + RGWOIDCProviderInfo& + get_oidc_info() + { + return info; + } +}; + +class MetadataLister : public RGWMetadataLister { +public: + using RGWMetadataLister::RGWMetadataLister; + + void + filter_transform( + std::vector& oids, + std::list& keys) override + { + // convert oid format "{tenant}oidc_url.{url}" to metadata key "{tenant}${url}" + // filter out any objects that don't contain "oidc_url." + for (const auto& oid : oids) { + if (oid.find(oidc_url_oid_prefix) != std::string::npos) { + keys.push_back(oid_to_metadata_key(oid)); + } + } + } +}; + +class MetadataHandler : public RGWMetadataHandler { + librados::Rados& rados; + RGWSI_SysObj& sysobj; + RGWSI_MDLog& mdlog; + const RGWZoneParams& zone; + +public: + MetadataHandler( + librados::Rados& rados, + RGWSI_SysObj& sysobj, + RGWSI_MDLog& mdlog, + const RGWZoneParams& zone) + : rados(rados), sysobj(sysobj), mdlog(mdlog), zone(zone) + { + } + + std::string + get_type() final { return "oidc"; } + + RGWMetadataObject* + get_meta_obj( + JSONObj* jo, + const obj_version& objv, + const ceph::real_time& mtime) override + { + RGWOIDCProviderInfo info; + + try { + info.decode_json(jo); + } catch (JSONDecoder::err& e) { + return nullptr; + } + + return new MetadataObject(info, objv, mtime); + } + + int + get( + std::string& entry, + RGWMetadataObject** obj, + optional_yield y, + const DoutPrefixProvider* dpp) override + { + std::string tenant; + std::string url; + parse_oidc_metadata_key(entry, tenant, url); + + RGWOIDCProviderInfo info; + ceph::real_time mtime; + RGWObjVersionTracker objv; + int ret = read(dpp, y, sysobj, zone, tenant, url, info, &mtime, &objv); + if (ret < 0) { + return ret; + } + + *obj = new MetadataObject(info, objv.read_version, mtime); + return 0; + } + + int + put( + std::string& entry, + RGWMetadataObject* obj, + RGWObjVersionTracker& objv_tracker, + optional_yield y, + const DoutPrefixProvider* dpp, + RGWMDLogSyncType type, + bool from_remote_zone) override + { + auto robj = static_cast(obj); + auto& info = robj->get_oidc_info(); + auto mtime = robj->get_mtime(); + + constexpr bool exclusive = false; + int ret = write( + dpp, y, sysobj, &mdlog, rados, zone, + info, objv_tracker, mtime, exclusive); + return ret < 0 ? ret : STATUS_APPLIED; + } + + int + remove( + std::string& entry, + RGWObjVersionTracker& objv_tracker, + optional_yield y, + const DoutPrefixProvider* dpp) override + { + std::string tenant; + std::string url; + parse_oidc_metadata_key(entry, tenant, url); + + return oidc::remove( + dpp, y, sysobj, &mdlog, rados, zone, + tenant, url, objv_tracker); + } + + int + mutate( + const std::string& entry, + const ceph::real_time& mtime, + RGWObjVersionTracker* objv_tracker, + optional_yield y, + const DoutPrefixProvider* dpp, + RGWMDLogStatus op_type, + std::function f) override + { + return -ENOTSUP; // unused + } + + int + list_keys_init( + const DoutPrefixProvider* dpp, + const std::string& marker, + void** phandle) override + { + const auto& pool = zone.oidc_pool; + auto lister = std::make_unique(sysobj.get_pool(pool)); + // Use empty prefix - we filter by "oidc_url." presence in filter_transform + // This is needed because OID format is "{tenant}oidc_url.{url}" + int ret = lister->init(dpp, marker, ""); + if (ret < 0) { + return ret; + } + *phandle = lister.release(); // release ownership + return 0; + } + + int + list_keys_next( + const DoutPrefixProvider* dpp, + void* handle, + int max, + std::list& keys, + bool* truncated) override + { + auto lister = static_cast(handle); + return lister->get_next(dpp, max, keys, truncated); + } + + void + list_keys_complete(void* handle) override + { + delete static_cast(handle); + } + + std::string + get_marker(void* handle) override + { + auto lister = static_cast(handle); + return lister->get_marker(); + } +}; + + +auto +create_metadata_handler( + librados::Rados& rados, + RGWSI_SysObj& sysobj, + RGWSI_MDLog& mdlog, + const RGWZoneParams& zone) + -> std::unique_ptr +{ + return std::make_unique(rados, sysobj, mdlog, zone); +} + +} // rgwrados::oidc + diff --git a/src/rgw/driver/rados/oidc.h b/src/rgw/driver/rados/oidc.h new file mode 100644 index 000000000000..ab1e845570c4 --- /dev/null +++ b/src/rgw/driver/rados/oidc.h @@ -0,0 +1,102 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:nil -*- +// vim: ts=8 sw=2 sts=2 expandtab ft=cpp + +/* + * Ceph - scalable distributed file system + * + * Copyright contributors to the Ceph project + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#pragma once + +#include +#include +#include +#include "include/rados/librados_fwd.hpp" +#include "common/ceph_time.h" + +class DoutPrefixProvider; +class optional_yield; +class RGWMetadataHandler; +class RGWObjVersionTracker; +struct RGWOIDCProviderInfo; +class RGWSI_MDLog; +class RGWSI_SysObj; +class RGWZoneParams; + +namespace rgwrados::oidc { + +/// Read OIDC provider info by URL. +int read( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view url, + RGWOIDCProviderInfo& info, + ceph::real_time* pmtime = nullptr, + RGWObjVersionTracker* pobjv = nullptr); + +/// Write or overwrite OIDC provider info. +int write( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + RGWSI_MDLog* mdlog, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWOIDCProviderInfo& info, + RGWObjVersionTracker& objv, + ceph::real_time mtime, + bool exclusive); + +/// Remove an OIDC provider by URL. +int remove( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + RGWSI_MDLog* mdlog, + librados::Rados& rados, + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view url, + RGWObjVersionTracker& objv); + +/// List all OIDC providers for a given tenant. +int list( + const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + std::string_view tenant, + std::vector& providers); + +/// OIDC provider metadata handler factory. +auto create_metadata_handler( + librados::Rados& rados, + RGWSI_SysObj& sysobj, + RGWSI_MDLog& mdlog, + const RGWZoneParams& zone) + -> std::unique_ptr; + +/// Construct the metadata key "{tenant}${url}" from tenant and URL. +std::string get_oidc_metadata_key( + std::string_view tenant, + std::string_view url); + +/// Parse the metadata key "{tenant}${url}" into tenant and URL. +void parse_oidc_metadata_key( + const std::string& key, + std::string& tenant, + std::string& url); + +} // rgwrados::oidc + diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index 481822bb1cc2..f6f709df13e5 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -83,6 +83,7 @@ #include "buckets.h" #include "group.h" #include "groups.h" +#include "oidc.h" #include "rgw_pubsub.h" #include "role.h" #include "roles.h" @@ -2619,29 +2620,17 @@ int RadosStore::list_roles(const DoutPrefixProvider *dpp, listing.roles, listing.next_marker); } -static constexpr std::string_view oidc_url_oid_prefix = "oidc_url."; - -static std::string oidc_provider_oid(std::string_view account, - std::string_view prefix, - std::string_view url) -{ - return string_cat_reserve(account, prefix, url); -} - int RadosStore::store_oidc_provider(const DoutPrefixProvider *dpp, optional_yield y, const RGWOIDCProviderInfo& info, bool exclusive) { - auto sysobj = svc()->sysobj; - std::string oid = oidc_provider_oid(info.tenant, oidc_url_oid_prefix, - url_remove_prefix(info.provider_url)); - - // TODO: add support for oidc metadata sync - bufferlist bl; - using ceph::encode; - encode(info, bl); - return rgw_put_system_obj(dpp, sysobj, svc()->zone->get_zone_params().oidc_pool, oid, bl, exclusive, nullptr, real_time(), y); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + RGWObjVersionTracker objv; + objv.generate_new_write_ver(dpp->get_cct()); + return rgwrados::oidc::write(dpp, y, *svc()->sysobj, svc()->mdlog, + *getRados()->get_rados_handle(), zone, + info, objv, ceph::real_clock::now(), exclusive); } int RadosStore::load_oidc_provider(const DoutPrefixProvider *dpp, @@ -2650,27 +2639,8 @@ int RadosStore::load_oidc_provider(const DoutPrefixProvider *dpp, std::string_view url, RGWOIDCProviderInfo& info) { - auto sysobj = svc()->sysobj; - auto& pool = svc()->zone->get_zone_params().oidc_pool; - std::string oid = oidc_provider_oid(account, oidc_url_oid_prefix, url); - bufferlist bl; - - int ret = rgw_get_system_obj(sysobj, pool, oid, bl, nullptr, nullptr, y, dpp); - if (ret < 0) { - return ret; - } - - try { - using ceph::decode; - auto iter = bl.cbegin(); - decode(info, iter); - } catch (buffer::error& err) { - ldpp_dout(dpp, 0) << "ERROR: failed to decode oidc provider info from pool: " << pool.name << - ": " << url << dendl; - return -EIO; - } - - return 0; + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + return rgwrados::oidc::read(dpp, y, *svc()->sysobj, zone, account, url, info); } int RadosStore::delete_oidc_provider(const DoutPrefixProvider *dpp, @@ -2678,15 +2648,11 @@ int RadosStore::delete_oidc_provider(const DoutPrefixProvider *dpp, std::string_view account, std::string_view url) { - auto& pool = svc()->zone->get_zone_params().oidc_pool; - std::string oid = oidc_provider_oid(account, oidc_url_oid_prefix, url); - int ret = rgw_delete_system_obj(dpp, svc()->sysobj, pool, oid, nullptr, y); - if (ret < 0) { - ldpp_dout(dpp, 0) << "ERROR: deleting oidc url from pool: " << pool.name << ": " - << url << ": " << cpp_strerror(-ret) << dendl; - } - - return ret; + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + RGWObjVersionTracker objv; + return rgwrados::oidc::remove(dpp, y, *svc()->sysobj, svc()->mdlog, + *getRados()->get_rados_handle(), zone, + account, url, objv); } int RadosStore::get_oidc_providers(const DoutPrefixProvider* dpp, @@ -2694,47 +2660,10 @@ int RadosStore::get_oidc_providers(const DoutPrefixProvider* dpp, std::string_view tenant, vector& providers) { - std::string prefix = string_cat_reserve(tenant, oidc_url_oid_prefix); - auto pool = svc()->zone->get_zone_params().oidc_pool; - - //Get the filtered objects - list result; - bool is_truncated; - RGWListRawObjsCtx ctx; - do { - list oids; - int r = rados->list_raw_objects(dpp, pool, prefix, 1000, ctx, oids, &is_truncated); - if (r == -ENOENT) { - return 0; - } - if (r < 0) { - ldpp_dout(dpp, 0) << "ERROR: listing filtered objects failed: OIDC pool: " - << pool.name << ": " << prefix << ": " << cpp_strerror(-r) << dendl; - return r; - } - for (const auto& iter : oids) { - bufferlist bl; - r = rgw_get_system_obj(svc()->sysobj, pool, iter, bl, nullptr, nullptr, y, dpp); - if (r < 0) { - return r; - } - - RGWOIDCProviderInfo info; - try { - using ceph::decode; - auto iter = bl.cbegin(); - decode(info, iter); - } catch (buffer::error& err) { - ldpp_dout(dpp, 0) << "ERROR: failed to decode oidc provider info from pool: " - << pool.name << ": " << iter << dendl; - return -EIO; - } - - providers.push_back(std::move(info)); - } - } while (is_truncated); - - return 0; + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + return rgwrados::oidc::list(dpp, y, *svc()->sysobj, + *getRados()->get_rados_handle(), + zone, tenant, providers); } std::unique_ptr RadosStore::get_append_writer(const DoutPrefixProvider *dpp, diff --git a/src/rgw/driver/rados/rgw_service.cc b/src/rgw/driver/rados/rgw_service.cc index b69ac11462c3..3babb98b9641 100644 --- a/src/rgw/driver/rados/rgw_service.cc +++ b/src/rgw/driver/rados/rgw_service.cc @@ -24,6 +24,7 @@ #include "account.h" #include "group.h" +#include "oidc.h" #include "rgw_bucket.h" #include "rgw_cr_rados.h" #include "rgw_datalog.h" @@ -350,6 +351,8 @@ int RGWCtlDef::init(RGWServices& svc, rgw::sal::Driver* driver, *svc.sysobj, *svc.cls, *svc.mdlog, svc.zone->get_zone_params()); meta.role = rgwrados::role::create_metadata_handler( rados, *svc.sysobj, *svc.mdlog, svc.zone->get_zone_params()); + meta.oidc = rgwrados::oidc::create_metadata_handler( + rados, *svc.sysobj, *svc.mdlog, svc.zone->get_zone_params()); meta.account = rgwrados::account::create_metadata_handler( *svc.sysobj, svc.zone->get_zone_params()); meta.group = rgwrados::group::create_metadata_handler( @@ -424,6 +427,13 @@ int RGWCtl::init(RGWServices *_svc, rgw::sal::Driver* driver, return r; } + r = _ctl.meta.oidc->attach(meta.mgr); + if (r < 0) { + ldout(cct, 0) << "ERROR: failed to start init meta.oidc ctl (" + << cpp_strerror(-r) << ")" << dendl; + return r; + } + r = _ctl.meta.account->attach(meta.mgr); if (r < 0) { ldout(cct, 0) << "ERROR: failed to start init meta.account ctl (" << cpp_strerror(-r) << dendl; diff --git a/src/rgw/driver/rados/rgw_service.h b/src/rgw/driver/rados/rgw_service.h index cdc2392da186..5ded0d919ab4 100644 --- a/src/rgw/driver/rados/rgw_service.h +++ b/src/rgw/driver/rados/rgw_service.h @@ -179,6 +179,7 @@ struct RGWCtlDef { std::unique_ptr user; std::unique_ptr otp; std::unique_ptr role; + std::unique_ptr oidc; std::unique_ptr topic; std::unique_ptr account; std::unique_ptr group;