From: Casey Bodley Date: Sun, 11 Feb 2024 17:15:41 +0000 (-0500) Subject: rgw/sal: add backend interfaces for group metadata X-Git-Tag: testing/wip-yuriw-testing-20240416.150233~10^2~72 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=2cfc12dfa6cd2ffc7cd61feb9688f82ba18d6f79;p=ceph-ci.git rgw/sal: add backend interfaces for group metadata Signed-off-by: Casey Bodley (cherry picked from commit 3683a4097fc42f514c5310fc2078d7ef8e0561eb) --- diff --git a/src/rgw/CMakeLists.txt b/src/rgw/CMakeLists.txt index ca7bf2b104f..5592c29757b 100644 --- a/src/rgw/CMakeLists.txt +++ b/src/rgw/CMakeLists.txt @@ -155,6 +155,8 @@ set(librgw_common_srcs driver/rados/account.cc driver/rados/buckets.cc driver/rados/cls_fifo_legacy.cc + driver/rados/group.cc + driver/rados/groups.cc driver/rados/rgw_bucket.cc driver/rados/rgw_bucket_sync.cc driver/rados/rgw_cr_rados.cc diff --git a/src/rgw/driver/rados/account.cc b/src/rgw/driver/rados/account.cc index 79bc376b163..02a26993227 100644 --- a/src/rgw/driver/rados/account.cc +++ b/src/rgw/driver/rados/account.cc @@ -34,6 +34,7 @@ namespace rgwrados::account { static constexpr std::string_view buckets_oid_prefix = "buckets."; static constexpr std::string_view users_oid_prefix = "users."; +static constexpr std::string_view groups_oid_prefix = "groups."; static constexpr std::string_view roles_oid_prefix = "roles."; static const std::string account_oid_prefix = "account."; static constexpr std::string_view name_oid_prefix = "name."; @@ -55,6 +56,14 @@ rgw_raw_obj get_users_obj(const RGWZoneParams& zone, return {zone.account_pool, get_users_key(account_id)}; } +static std::string get_groups_key(std::string_view account_id) { + return string_cat_reserve(groups_oid_prefix, account_id); +} +rgw_raw_obj get_groups_obj(const RGWZoneParams& zone, + std::string_view account_id) { + return {zone.account_pool, get_groups_key(account_id)}; +} + static std::string get_roles_key(std::string_view account_id) { return string_cat_reserve(roles_oid_prefix, account_id); } diff --git a/src/rgw/driver/rados/account.h b/src/rgw/driver/rados/account.h index f9d4f534a7f..635f0968200 100644 --- a/src/rgw/driver/rados/account.h +++ b/src/rgw/driver/rados/account.h @@ -51,6 +51,11 @@ rgw_raw_obj get_buckets_obj(const RGWZoneParams& zone, rgw_raw_obj get_users_obj(const RGWZoneParams& zone, std::string_view account_id); +/// Return the rados object that tracks the given account's groups. This +/// can be used with the cls_user interface in namespace rgwrados::groups. +rgw_raw_obj get_groups_obj(const RGWZoneParams& zone, + std::string_view account_id); + /// Return the rados object that tracks the given account's roles. This /// can be used with the cls_user interface in namespace rgwrados::roles. rgw_raw_obj get_roles_obj(const RGWZoneParams& zone, diff --git a/src/rgw/driver/rados/group.cc b/src/rgw/driver/rados/group.cc new file mode 100644 index 00000000000..7cdd9487156 --- /dev/null +++ b/src/rgw/driver/rados/group.cc @@ -0,0 +1,522 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab 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 "group.h" + +#include +#include "common/errno.h" +#include "account.h" +#include "groups.h" +#include "rgw_common.h" +#include "rgw_metadata.h" +#include "rgw_metadata_lister.h" +#include "rgw_obj_types.h" +#include "rgw_string.h" +#include "rgw_tools.h" +#include "rgw_user.h" +#include "rgw_zone.h" +#include "services/svc_sys_obj.h" + +namespace rgwrados::group { + +static constexpr std::string_view info_oid_prefix = "info."; +static constexpr std::string_view name_oid_prefix = "name."; +static constexpr std::string_view users_oid_prefix = "users."; + +// metadata keys/objects +std::string get_users_key(std::string_view group_id) { + return string_cat_reserve(users_oid_prefix, group_id); +} +rgw_raw_obj get_users_obj(const RGWZoneParams& zone, + std::string_view group_id) { + return {zone.group_pool, get_users_key(group_id)}; +} + +static std::string get_group_key(std::string_view group_id) { + return string_cat_reserve(info_oid_prefix, group_id); +} +static rgw_raw_obj get_group_obj(const RGWZoneParams& zone, + std::string_view group_id) { + return {zone.group_pool, get_group_key(group_id)}; +} + +static std::string get_name_key(std::string_view account, + std::string_view name) { + // names are case-insensitive, so store them in lower case + std::string lower_name{name}; + boost::algorithm::to_lower(lower_name); + return string_cat_reserve(name_oid_prefix, account, "$", lower_name); +} +static rgw_raw_obj get_name_obj(const RGWZoneParams& zone, + std::string_view account, + std::string_view name) { + return {zone.group_pool, get_name_key(account, name)}; +} + + +struct NameObj { + rgw_raw_obj obj; + RGWUID data; + RGWObjVersionTracker objv; +}; + +static int read_name(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + NameObj& name) +{ + bufferlist bl; + int r = rgw_get_system_obj(&sysobj, name.obj.pool, name.obj.oid, + bl, &name.objv, nullptr, y, dpp); + if (r < 0) { + ldpp_dout(dpp, 20) << "failed to read " << name.obj.oid + << " with: " << cpp_strerror(r) << dendl; + return r; + } + + try { + auto p = bl.cbegin(); + decode(name.data, p); + } catch (const buffer::error& e) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode group name: " + << e.what() << dendl; + return -EIO; + } + return 0; +} + +static int write_name(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + NameObj& name) +{ + bufferlist bl; + encode(name.data, bl); + + constexpr bool exclusive = true; + return rgw_put_system_obj(dpp, &sysobj, name.obj.pool, + name.obj.oid, bl, exclusive, + &name.objv, ceph::real_time{}, y); +} + + +int read(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view id, + RGWGroupInfo& info, + std::map& attrs, + ceph::real_time& mtime, + RGWObjVersionTracker& objv) +{ + const rgw_raw_obj obj = get_group_obj(zone, id); + + bufferlist bl; + int r = rgw_get_system_obj(&sysobj, obj.pool, obj.oid, bl, + &objv, &mtime, y, dpp, &attrs); + if (r < 0) { + ldpp_dout(dpp, 20) << "group lookup with id=" << id + << " failed: " << cpp_strerror(r) << dendl; + return r; + } + + try { + auto p = bl.cbegin(); + decode(info, p); + } catch (const buffer::error& e) { + ldpp_dout(dpp, 0) << "ERROR: failed to decode group info: " + << e.what() << dendl; + return -EIO; + } + if (info.id != id) { + ldpp_dout(dpp, 0) << "ERROR: read group id mismatch " + << info.id << " != " << id << dendl; + return -EIO; + } + return 0; +} + +int read_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view tenant, + std::string_view name, + RGWGroupInfo& info, + std::map& attrs, + RGWObjVersionTracker& objv) +{ + auto nameobj = NameObj{.obj = get_name_obj(zone, tenant, name)}; + int r = read_name(dpp, y, sysobj, nameobj); + if (r < 0) { + return r; + } + ceph::real_time mtime; // ignored + return read(dpp, y, sysobj, zone, nameobj.data.id, info, attrs, mtime, objv); +} + + +int write(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWGroupInfo& info, + const RGWGroupInfo* old_info, + const std::map& attrs, + ceph::real_time mtime, + bool exclusive, + RGWObjVersionTracker& objv) +{ + const rgw_raw_obj obj = get_group_obj(zone, info.id); + + const bool same_name = old_info + && old_info->account_id == info.account_id + && old_info->name == info.name; + + std::optional remove_name; + if (old_info) { + if (old_info->id != info.id) { + ldpp_dout(dpp, 1) << "ERROR: can't modify group id" << dendl; + return -EINVAL; + } + if (!same_name && !old_info->name.empty()) { + // read old group name object + NameObj nameobj; + nameobj.obj = get_name_obj(zone, old_info->account_id, old_info->name); + int r = read_name(dpp, y, sysobj, nameobj); + if (r == -ENOENT) { + // leave remove_name empty + } else if (r < 0) { + return r; + } else if (nameobj.data.id == info.id) { + remove_name = std::move(nameobj); + } + } + } // old_info + + if (!same_name && !info.name.empty()) { + // read new account name object + NameObj nameobj; + nameobj.obj = get_name_obj(zone, info.account_id, info.name); + int r = read_name(dpp, y, sysobj, nameobj); + if (r == -ENOENT) { + // write the new name object below + } else if (r == 0) { + ldpp_dout(dpp, 1) << "ERROR: group name obj " << nameobj.obj + << " already taken for group id " << nameobj.data.id << dendl; + return -EEXIST; + } else if (r < 0) { + return r; + } + } + + // encode/write the group info + { + bufferlist bl; + encode(info, bl); + + const rgw_raw_obj obj = get_group_obj(zone, info.id); + int r = rgw_put_system_obj(dpp, &sysobj, obj.pool, obj.oid, bl, + exclusive, &objv, mtime, y, &attrs); + if (r < 0) { + ldpp_dout(dpp, 1) << "ERROR: failed to write group obj " << obj + << " with: " << cpp_strerror(r) << dendl; + return r; + } + } + + if (remove_name) { + // remove the old name object, ignoring errors + auto& nameobj = *remove_name; + int r = rgw_delete_system_obj(dpp, &sysobj, nameobj.obj.pool, + nameobj.obj.oid, &nameobj.objv, y); + if (r < 0) { + ldpp_dout(dpp, 20) << "WARNING: failed to remove old name obj " + << nameobj.obj.oid << ": " << cpp_strerror(r) << dendl; + } // not fatal + // unlink the old name from its account + const auto& users = account::get_groups_obj(zone, old_info->account_id); + r = groups::remove(dpp, y, rados, users, old_info->name); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: could not unlink from account " + << old_info->account_id << ": " << cpp_strerror(r) << dendl; + } // not fatal + } + if (!same_name && !info.name.empty()) { + // write the new name object + NameObj nameobj; + nameobj.obj = get_name_obj(zone, info.account_id, info.name); + nameobj.data.id = info.id; + nameobj.objv.generate_new_write_ver(dpp->get_cct()); + + int r = write_name(dpp, y, sysobj, nameobj); + if (r < 0) { + ldpp_dout(dpp, 20) << "WARNING: failed to write name obj " + << nameobj.obj << " with: " << cpp_strerror(r) << dendl; + } // not fatal + // link the new name to its account + const auto& users = account::get_groups_obj(zone, info.account_id); + r = groups::add(dpp, y, rados, users, info, false, + std::numeric_limits::max()); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: could not link to account " + << info.account_id << ": " << cpp_strerror(r) << dendl; + } // not fatal + } + + return 0; +} + +int remove(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) +{ + const rgw_raw_obj obj = get_group_obj(zone, info.id); + 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 account obj " + << obj << " with: " << cpp_strerror(r) << dendl; + return r; + } + + { + // remove the name object + const rgw_raw_obj obj = get_name_obj(zone, info.account_id, info.name); + r = rgw_delete_system_obj(dpp, &sysobj, obj.pool, obj.oid, nullptr, y); + if (r < 0) { + ldpp_dout(dpp, 20) << "WARNING: failed to remove name obj " + << obj << " with: " << cpp_strerror(r) << dendl; + } // not fatal + } + { + // remove the users object + const rgw_raw_obj obj = get_users_obj(zone, info.id); + r = rgw_delete_system_obj(dpp, &sysobj, obj.pool, obj.oid, nullptr, y); + if (r < 0) { + ldpp_dout(dpp, 20) << "WARNING: failed to remove users obj " + << obj << " with: " << cpp_strerror(r) << dendl; + } // not fatal + } + { + // unlink the name from its account + const auto& users = account::get_groups_obj(zone, info.account_id); + r = groups::remove(dpp, y, rados, users, info.name); + if (r < 0) { + ldpp_dout(dpp, 0) << "ERROR: could not unlink from account " + << info.account_id << ": " << cpp_strerror(r) << dendl; + } // not fatal + } + + return 0; +} + + +// metadata abstraction + +struct CompleteInfo { + RGWGroupInfo info; + std::map attrs; + bool has_attrs = false; + + void dump(Formatter* f) const { + info.dump(f); + encode_json("attrs", attrs, f); + } + + void decode_json(JSONObj* obj) { + decode_json_obj(info, obj); + has_attrs = JSONDecoder::decode_json("attrs", attrs, obj); + } +}; + +class MetadataObject : public RGWMetadataObject { + CompleteInfo aci; + public: + MetadataObject(CompleteInfo& aci, const obj_version& v, ceph::real_time m) + : RGWMetadataObject(v, m), aci(std::move(aci)) {} + + void dump(Formatter *f) const override { + aci.dump(f); + } + + CompleteInfo& get() { return aci; } +}; + +class MetadataLister : public RGWMetadataLister { + public: + using RGWMetadataLister::RGWMetadataLister; + + void filter_transform(std::vector& oids, + std::list& keys) override + { + // remove the oid prefix from keys + constexpr auto trim = [] (const std::string& oid) { + return oid.substr(info_oid_prefix.size()); + }; + std::transform(oids.begin(), oids.end(), + std::back_inserter(keys), + trim); + } +}; + +class MetadataHandler : public RGWMetadataHandler { + RGWSI_SysObj& sysobj; + librados::Rados& rados; + const RGWZoneParams& zone; + public: + MetadataHandler(RGWSI_SysObj& sysobj, librados::Rados& rados, + const RGWZoneParams& zone) + : sysobj(sysobj), rados(rados), zone(zone) {} + + std::string get_type() override { return "group"; } + + RGWMetadataObject* get_meta_obj(JSONObj* obj, + const obj_version& objv, + const ceph::real_time& mtime) override + { + CompleteInfo aci; + try { + decode_json_obj(aci, obj); + } catch (const JSONDecoder::err&) { + return nullptr; + } + return new MetadataObject(aci, objv, mtime); + } + + int get(std::string& entry, RGWMetadataObject** obj, + optional_yield y, const DoutPrefixProvider* dpp) override + { + const std::string& group_id = entry; + CompleteInfo aci; + RGWObjVersionTracker objv; + ceph::real_time mtime; + + int r = read(dpp, y, sysobj, zone, group_id, + aci.info, aci.attrs, mtime, objv); + if (r < 0) { + return r; + } + + *obj = new MetadataObject(aci, objv.read_version, mtime); + return 0; + } + + int put(std::string& entry, RGWMetadataObject* obj, + RGWObjVersionTracker& objv, optional_yield y, + const DoutPrefixProvider* dpp, + RGWMDLogSyncType type, bool from_remote_zone) override + { + const std::string& group_id = entry; + auto group_obj = static_cast(obj); + const auto& new_info = group_obj->get().info; + + // account id must match metadata key + if (new_info.id != group_id) { + return -EINVAL; + } + + // read existing metadata + RGWGroupInfo old_info; + std::map old_attrs; + ceph::real_time old_mtime; + int r = read(dpp, y, sysobj, zone, group_id, + old_info, old_attrs, old_mtime, objv); + if (r < 0 && r != -ENOENT) { + return r; + } + const RGWGroupInfo* pold_info = (r == -ENOENT ? nullptr : &old_info); + + // write/overwrite metadata + constexpr bool exclusive = false; + return write(dpp, y, sysobj, rados, zone, new_info, pold_info, + group_obj->get().attrs, obj->get_mtime(), + exclusive, objv); + } + + int remove(std::string& entry, RGWObjVersionTracker& objv, + optional_yield y, const DoutPrefixProvider* dpp) override + { + const std::string& group_id = entry; + + // read existing metadata + RGWGroupInfo info; + std::map attrs; + ceph::real_time mtime; + int r = read(dpp, y, sysobj, zone, group_id, + info, attrs, mtime, objv); + if (r < 0) { + return r; + } + + return group::remove(dpp, y, sysobj, rados, zone, info, objv); + } + + int mutate(const std::string& entry, + const ceph::real_time& mtime, + RGWObjVersionTracker* objv, + 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 + { + auto lister = std::make_unique( + sysobj.get_pool(zone.group_pool)); + int r = lister->init(dpp, marker, std::string{info_oid_prefix}); + if (r < 0) { + return r; + } + *phandle = lister.release(); + 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(RGWSI_SysObj& sysobj, librados::Rados& rados, + const RGWZoneParams& zone) + -> std::unique_ptr +{ + return std::make_unique(sysobj, rados, zone); +} + +} // namespace rgwrados::group diff --git a/src/rgw/driver/rados/group.h b/src/rgw/driver/rados/group.h new file mode 100644 index 00000000000..b96d1cc0259 --- /dev/null +++ b/src/rgw/driver/rados/group.h @@ -0,0 +1,90 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab 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/buffer_fwd.h" +#include "include/rados/librados_fwd.hpp" +#include "common/async/yield_context.h" +#include "common/ceph_time.h" + +class DoutPrefixProvider; +struct rgw_raw_obj; +class RGWGroupInfo; +class RGWMetadataHandler; +class RGWObjVersionTracker; +class RGWSI_SysObj; +class RGWZoneParams; + +namespace rgwrados::group { + +/// Group metadata handler factory +auto create_metadata_handler(RGWSI_SysObj& sysobj, librados::Rados& rados, + const RGWZoneParams& zone) + -> std::unique_ptr; + +/// Return the rados object that tracks the given group's users +rgw_raw_obj get_users_obj(const RGWZoneParams& zone, + std::string_view group_id); + + +/// Read group info by id +int read(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view id, + RGWGroupInfo& info, + std::map& attrs, + ceph::real_time& mtime, + RGWObjVersionTracker& objv); + +/// Read group info by name +int read_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + const RGWZoneParams& zone, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, + std::map& attrs, + RGWObjVersionTracker& objv); + +/// Write group info and update name index +int write(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWGroupInfo& info, + const RGWGroupInfo* old_info, + const std::map& attrs, + ceph::real_time mtime, + bool exclusive, + RGWObjVersionTracker& objv); + +/// Remove group info and name index +int remove(const DoutPrefixProvider* dpp, + optional_yield y, + RGWSI_SysObj& sysobj, + librados::Rados& rados, + const RGWZoneParams& zone, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv); + +} // namespace rgwrados::group diff --git a/src/rgw/driver/rados/groups.cc b/src/rgw/driver/rados/groups.cc new file mode 100644 index 00000000000..21f66e7b7d5 --- /dev/null +++ b/src/rgw/driver/rados/groups.cc @@ -0,0 +1,135 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab 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 "groups.h" + +#include "include/rados/librados.hpp" +#include "common/ceph_json.h" +#include "common/dout.h" +#include "cls/user/cls_user_client.h" +#include "rgw_sal.h" + +namespace rgwrados::groups { + +int add(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + const RGWGroupInfo& group, + bool exclusive, uint32_t limit) +{ + resource_metadata meta; + meta.group_id = group.id; + + cls_user_account_resource resource; + resource.name = group.name; + resource.path = group.path; + encode(meta, resource.metadata); + + rgw_rados_ref ref; + int r = rgw_get_rados_ref(dpp, &rados, obj, &ref); + if (r < 0) { + return r; + } + + librados::ObjectWriteOperation op; + ::cls_user_account_resource_add(op, resource, exclusive, limit); + return ref.operate(dpp, &op, y); +} + +int remove(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + std::string_view name) +{ + rgw_rados_ref ref; + int r = rgw_get_rados_ref(dpp, &rados, obj, &ref); + if (r < 0) { + return r; + } + + librados::ObjectWriteOperation op; + ::cls_user_account_resource_rm(op, name); + return ref.operate(dpp, &op, y); +} + +int list(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + std::string_view marker, + std::string_view path_prefix, + uint32_t max_items, + std::vector& ids, + std::string& next_marker) +{ + rgw_rados_ref ref; + int r = rgw_get_rados_ref(dpp, &rados, obj, &ref); + if (r < 0) { + return r; + } + + librados::ObjectReadOperation op; + std::vector entries; + bool truncated = false; + int ret = 0; + ::cls_user_account_resource_list(op, marker, path_prefix, max_items, + entries, &truncated, &next_marker, &ret); + + r = ref.operate(dpp, &op, nullptr, y); + if (r == -ENOENT) { + next_marker.clear(); + return 0; + } + if (r < 0) { + return r; + } + if (ret < 0) { + return ret; + } + + for (auto& resource : entries) { + resource_metadata meta; + try { + auto p = resource.metadata.cbegin(); + decode(meta, p); + } catch (const buffer::error&) { + return -EIO; + } + ids.push_back(std::move(meta.group_id)); + } + + if (!truncated) { + next_marker.clear(); + } + return 0; +} + + +void resource_metadata::dump(ceph::Formatter* f) const +{ + encode_json("group_id", group_id, f); +} + +void resource_metadata::generate_test_instances(std::list& o) +{ + o.push_back(new resource_metadata); + auto m = new resource_metadata; + m->group_id = "id"; + o.push_back(m); +} + +} // namespace rgwrados::groups diff --git a/src/rgw/driver/rados/groups.h b/src/rgw/driver/rados/groups.h new file mode 100644 index 00000000000..50ebcad2d62 --- /dev/null +++ b/src/rgw/driver/rados/groups.h @@ -0,0 +1,79 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab 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/rados/librados_fwd.hpp" +#include "include/encoding.h" +#include "rgw_sal_fwd.h" + +namespace ceph { class Formatter; } +class DoutPrefixProvider; +class optional_yield; +struct rgw_raw_obj; +struct RGWGroupInfo; + + +namespace rgwrados::groups { + +/// Add the given group to the list. +int add(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + const RGWGroupInfo& info, + bool exclusive, uint32_t limit); + +/// Remove the given group from the list. +int remove(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + std::string_view name); + +/// Return a paginated listing of group ids. +int list(const DoutPrefixProvider* dpp, + optional_yield y, + librados::Rados& rados, + const rgw_raw_obj& obj, + std::string_view marker, + std::string_view path_prefix, + uint32_t max_items, + std::vector& ids, + std::string& next_marker); + +// group-specific metadata for cls_user_account_resource +struct resource_metadata { + std::string group_id; + + void encode(bufferlist& bl) const { + ENCODE_START(1, 1, bl); + encode(group_id, bl); + ENCODE_FINISH(bl); + } + void decode(bufferlist::const_iterator& bl) { + DECODE_START(1, bl); + decode(group_id, bl); + DECODE_FINISH(bl); + } + + void dump(ceph::Formatter* f) const; + static void generate_test_instances(std::list& o); +}; +WRITE_CLASS_ENCODER(resource_metadata); + +} // namespace rgwrados::groups diff --git a/src/rgw/driver/rados/rgw_sal_rados.cc b/src/rgw/driver/rados/rgw_sal_rados.cc index f2ea4078b53..e26c48bac52 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.cc +++ b/src/rgw/driver/rados/rgw_sal_rados.cc @@ -71,6 +71,8 @@ #include "account.h" #include "buckets.h" +#include "group.h" +#include "groups.h" #include "roles.h" #include "users.h" #include "rgw_pubsub.h" @@ -279,6 +281,39 @@ int RadosUser::verify_mfa(const std::string& mfa_str, bool* verified, return 0; } +int RadosUser::list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) +{ + RGWSI_SysObj& sysobj = *store->svc()->sysobj; + const RGWZoneParams& zone = store->svc()->zone->get_zone_params(); + + const auto& ids = info.group_ids; + for (auto id = ids.lower_bound(marker); id != ids.end(); ++id) { + if (listing.groups.size() >= max_items) { + listing.next_marker = *id; + return 0; + } + + RGWGroupInfo info; + Attrs attrs_ignored; + ceph::real_time mtime_ignored; + RGWObjVersionTracker objv_ignored; + int r = rgwrados::group::read(dpp, y, sysobj, zone, *id, info, + attrs_ignored, mtime_ignored, objv_ignored); + if (r == -ENOENT) { + continue; + } + if (r < 0) { + return r; + } + listing.groups.push_back(std::move(info)); + } + + listing.next_marker.clear(); + return 0; +} + RadosBucket::~RadosBucket() {} int RadosBucket::remove(const DoutPrefixProvider* dpp, @@ -1329,6 +1364,151 @@ int RadosStore::list_account_users(const DoutPrefixProvider* dpp, return 0; } +int RadosStore::load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) +{ + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + ceph::real_time mtime_ignored; + return rgwrados::group::read(dpp, y, *svc()->sysobj, zone, id, + info, attrs, mtime_ignored, objv); +} + +int RadosStore::load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) +{ + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + return rgwrados::group::read_by_name(dpp, y, *svc()->sysobj, zone, account_id, + name, info, attrs, objv); +} + +int RadosStore::store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) +{ + librados::Rados& rados = *getRados()->get_rados_handle(); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + ceph::real_time mtime = ceph::real_clock::now(); + int r = rgwrados::group::write(dpp, y, *svc()->sysobj, rados, zone, info, + old_info, attrs, mtime, exclusive, objv); + if (r < 0) { + return r; + } + + return write_mdlog_entry(dpp, y, *svc()->mdlog, "group", info.id, objv); +} + +int RadosStore::remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) +{ + librados::Rados& rados = *getRados()->get_rados_handle(); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + int r = rgwrados::group::remove(dpp, y, *svc()->sysobj, rados, zone, info, objv); + if (r < 0) { + return r; + } + + return write_mdlog_entry(dpp, y, *svc()->mdlog, "group", info.id, objv); +} + +int RadosStore::list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) +{ + // fetch the list of user ids from cls_user + librados::Rados& rados = *getRados()->get_rados_handle(); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + const rgw_raw_obj& obj = rgwrados::group::get_users_obj(zone, id); + const std::string path_prefix; // empty + std::vector ids; + int r = rgwrados::users::list(dpp, y, rados, obj, marker, path_prefix, + max_items, ids, listing.next_marker); + if (r < 0) { + return r; + } + + // load the user metadata for each + for (auto& id : ids) { + rgw_user uid; + uid.tenant = tenant; + uid.id = std::move(id); + + RGWUserInfo info; + r = ctl()->user->get_info_by_uid(dpp, uid, &info, y); + if (r == -ENOENT) { + continue; + } + if (r < 0) { + return r; + } + listing.users.push_back(std::move(info)); + } + + return 0; +} + +int RadosStore::count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) +{ + librados::Rados& rados = *getRados()->get_rados_handle(); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + const rgw_raw_obj& obj = rgwrados::account::get_groups_obj(zone, account_id); + return rgwrados::account::resource_count(dpp, y, rados, obj, count); +} + +int RadosStore::list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) +{ + // fetch the list of group ids from cls_user + librados::Rados& rados = *getRados()->get_rados_handle(); + const RGWZoneParams& zone = svc()->zone->get_zone_params(); + const rgw_raw_obj& obj = rgwrados::account::get_groups_obj(zone, account_id); + std::vector ids; + int r = rgwrados::groups::list(dpp, y, rados, obj, marker, path_prefix, + max_items, ids, listing.next_marker); + if (r < 0) { + return r; + } + + // load the group metadata for each + for (auto& id : ids) { + RGWGroupInfo info; + Attrs attrs; + ceph::real_time mtime_ignored; + RGWObjVersionTracker objv; + r = rgwrados::group::read(dpp, y, *svc()->sysobj, zone, id, + info, attrs, mtime_ignored, objv); + if (r == -ENOENT) { + continue; + } + if (r < 0) { + return r; + } + listing.groups.push_back(std::move(info)); + } + + return 0; +} + std::unique_ptr RadosStore::get_object(const rgw_obj_key& k) { return std::make_unique(this, k); diff --git a/src/rgw/driver/rados/rgw_sal_rados.h b/src/rgw/driver/rados/rgw_sal_rados.h index 07eeeb4618a..669bc73de13 100644 --- a/src/rgw/driver/rados/rgw_sal_rados.h +++ b/src/rgw/driver/rados/rgw_sal_rados.h @@ -226,6 +226,43 @@ class RadosStore : public StoreDriver { uint32_t max_items, UserList& listing) override; + int load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) override; + int remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) override; + int list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) override; + int count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) override; + int list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) override; + virtual std::unique_ptr get_object(const rgw_obj_key& k) override; std::unique_ptr get_bucket(const RGWBucketInfo& i) override; int load_bucket(const DoutPrefixProvider* dpp, const rgw_bucket& b, @@ -425,6 +462,9 @@ class RadosUser : public StoreUser { virtual int store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info = nullptr) override; virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override; virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override; + int list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) override; friend class RadosBucket; }; diff --git a/src/rgw/driver/rados/rgw_service.cc b/src/rgw/driver/rados/rgw_service.cc index d8b4610ecaf..938589f4807 100644 --- a/src/rgw/driver/rados/rgw_service.cc +++ b/src/rgw/driver/rados/rgw_service.cc @@ -30,6 +30,7 @@ #include "common/errno.h" #include "account.h" +#include "group.h" #include "rgw_bucket.h" #include "rgw_cr_rados.h" #include "rgw_datalog.h" @@ -395,6 +396,8 @@ int RGWCtlDef::init(RGWServices& svc, rgw::sal::Driver* driver, meta.role = std::make_unique(driver, svc.role); meta.account = rgwrados::account::create_metadata_handler( *svc.sysobj, svc.zone->get_zone_params()); + meta.group = rgwrados::group::create_metadata_handler( + *svc.sysobj, rados, svc.zone->get_zone_params()); user.reset(new RGWUserCtl(svc.zone, svc.user, (RGWUserMetadataHandler *)meta.user.get())); bucket.reset(new RGWBucketCtl(svc.zone, @@ -491,12 +494,19 @@ int RGWCtl::init(RGWServices *_svc, rgw::sal::Driver* driver, ldout(cct, 0) << "ERROR: failed to start init meta.account ctl (" << cpp_strerror(-r) << dendl; return r; } + r = meta.topic->attach(meta.mgr); if (r < 0) { ldout(cct, 0) << "ERROR: failed to start init topic ctl (" << cpp_strerror(-r) << dendl; return r; } + + r = _ctl.meta.group->attach(meta.mgr); + if (r < 0) { + ldout(cct, 0) << "ERROR: failed to start init meta.group ctl (" << cpp_strerror(-r) << dendl; + return r; + } return 0; } diff --git a/src/rgw/driver/rados/rgw_service.h b/src/rgw/driver/rados/rgw_service.h index 486df8c71be..ec7f73cf6eb 100644 --- a/src/rgw/driver/rados/rgw_service.h +++ b/src/rgw/driver/rados/rgw_service.h @@ -198,6 +198,7 @@ struct RGWCtlDef { std::unique_ptr role; std::unique_ptr topic; std::unique_ptr account; + std::unique_ptr group; std::unique_ptr> topic_cache; diff --git a/src/rgw/driver/rados/rgw_zone.h b/src/rgw/driver/rados/rgw_zone.h index c45b42102a8..c542abc76d6 100644 --- a/src/rgw/driver/rados/rgw_zone.h +++ b/src/rgw/driver/rados/rgw_zone.h @@ -116,6 +116,7 @@ struct RGWZoneParams : RGWSystemMetaObj { rgw_pool notif_pool; rgw_pool topics_pool; rgw_pool account_pool; + rgw_pool group_pool; RGWAccessKey system_key; @@ -180,6 +181,7 @@ struct RGWZoneParams : RGWSystemMetaObj { encode(notif_pool, bl); encode(topics_pool, bl); encode(account_pool, bl); + encode(group_pool, bl); ENCODE_FINISH(bl); } @@ -256,9 +258,11 @@ struct RGWZoneParams : RGWSystemMetaObj { if (struct_v >= 15) { decode(topics_pool, bl); decode(account_pool, bl); + decode(group_pool, bl); } else { topics_pool = name + ".rgw.meta:topics"; account_pool = name + ".rgw.meta:accounts"; + group_pool = name + ".rgw.meta:groups"; } DECODE_FINISH(bl); } diff --git a/src/rgw/rgw_common.cc b/src/rgw/rgw_common.cc index 349b33c1cce..bd08281a8db 100644 --- a/src/rgw/rgw_common.cc +++ b/src/rgw/rgw_common.cc @@ -2596,6 +2596,7 @@ void RGWUserInfo::generate_test_instances(list& o) i->path = "/"; i->create_date = ceph::real_time{std::chrono::hours(1)}; i->tags.emplace("key", "value"); + i->group_ids.insert("group"); RGWAccessKey k1, k2; k1.id = "id1"; k1.key = "key1"; @@ -2832,6 +2833,7 @@ void RGWUserInfo::dump(Formatter *f) const encode_json("path", path, f); encode_json("create_date", create_date, f); encode_json("tags", tags, f); + encode_json("group_ids", group_ids, f); } void RGWUserInfo::decode_json(JSONObj *obj) @@ -2889,6 +2891,7 @@ void RGWUserInfo::decode_json(JSONObj *obj) JSONDecoder::decode_json("path", path, obj); JSONDecoder::decode_json("create_date", create_date, obj); JSONDecoder::decode_json("tags", tags, obj); + JSONDecoder::decode_json("group_ids", group_ids, obj); } diff --git a/src/rgw/rgw_common.h b/src/rgw/rgw_common.h index 15f90171baa..72724c51a58 100644 --- a/src/rgw/rgw_common.h +++ b/src/rgw/rgw_common.h @@ -22,6 +22,8 @@ #include #include +#include +#include #include "common/ceph_crypto.h" #include "common/random_string.h" @@ -596,6 +598,7 @@ struct RGWUserInfo std::string path = "/"; ceph::real_time create_date; std::multimap tags; + boost::container::flat_set> group_ids; RGWUserInfo() : suspended(0), @@ -667,6 +670,7 @@ struct RGWUserInfo encode(path, bl); encode(create_date, bl); encode(tags, bl); + encode(group_ids, bl); ENCODE_FINISH(bl); } void decode(bufferlist::const_iterator& bl) { @@ -762,6 +766,7 @@ struct RGWUserInfo decode(path, bl); decode(create_date, bl); decode(tags, bl); + decode(group_ids, bl); } else { path = "/"; } diff --git a/src/rgw/rgw_sal.h b/src/rgw/rgw_sal.h index 8f0ec5228b9..93de433bcf7 100644 --- a/src/rgw/rgw_sal.h +++ b/src/rgw/rgw_sal.h @@ -259,6 +259,14 @@ struct UserList { std::string next_marker; }; +/// A list of groups +struct GroupList { + /// The list of results, sorted by name + std::vector groups; + /// The next marker to resume listing, or empty + std::string next_marker; +}; + /** A list of key-value attributes */ using Attrs = std::map; @@ -394,6 +402,53 @@ class Driver { uint32_t max_items, UserList& listing) = 0; + /// @group Group + ///@{ + /** Load an account's group by id. */ + virtual int load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) = 0; + /** Load an account's group by name. */ + virtual int load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) = 0; + /** Write or overwrite a group. */ + virtual int store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) = 0; + /** Remove a group. */ + virtual int remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) = 0; + /** Return a paginated listing of the group's users. */ + virtual int list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) = 0; + /** Count the number of groups belonging to the given account. */ + virtual int count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) = 0; + /** Return a paginated listing of the account's groups. */ + virtual int list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) = 0; + ///@} + /** Get a basic Object. This Object is not looked up, and is incomplete, since is * does not have a bucket. This should only be used when an Object is needed before * there is a Bucket, otherwise use the get_object() in the Bucket class. */ @@ -709,6 +764,10 @@ class User { virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) = 0; /** Verify multi-factor authentication for this user */ virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) = 0; + /** Return a paginated listing of the user's groups. */ + virtual int list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) = 0; /* dang temporary; will be removed when User is complete */ virtual RGWUserInfo& get_info() = 0; diff --git a/src/rgw/rgw_sal_dbstore.cc b/src/rgw/rgw_sal_dbstore.cc index e5518e21432..0b60ebf70ec 100644 --- a/src/rgw/rgw_sal_dbstore.cc +++ b/src/rgw/rgw_sal_dbstore.cc @@ -135,6 +135,13 @@ namespace rgw::sal { return 0; } + int DBUser::list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) + { + return -ENOTSUP; + } + int DBBucket::remove(const DoutPrefixProvider *dpp, bool delete_children, optional_yield y) { int ret; @@ -1689,6 +1696,70 @@ namespace rgw::sal { return -ENOTSUP; } + int DBStore::load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) + { + return -ENOTSUP; + } + + int DBStore::load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) + { + return -ENOTSUP; + } + + int DBStore::store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) + { + return -ENOTSUP; + } + + int DBStore::remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) + { + return -ENOTSUP; + } + + int DBStore::list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) + { + return -ENOTSUP; + } + + int DBStore::count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) + { + return -ENOTSUP; + } + + int DBStore::list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) + { + return -ENOTSUP; + } + std::string DBStore::get_cluster_id(const DoutPrefixProvider* dpp, optional_yield y) { return "PLACEHOLDER"; // for instance unique identifier diff --git a/src/rgw/rgw_sal_dbstore.h b/src/rgw/rgw_sal_dbstore.h index 378ad90bf4a..7139811544e 100644 --- a/src/rgw/rgw_sal_dbstore.h +++ b/src/rgw/rgw_sal_dbstore.h @@ -99,6 +99,9 @@ protected: virtual int store_user(const DoutPrefixProvider* dpp, optional_yield y, bool exclusive, RGWUserInfo* old_info = nullptr) override; virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override; virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override; + int list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) override; friend class DBBucket; }; @@ -807,6 +810,43 @@ public: uint32_t max_items, UserList& listing) override; + int load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) override; + int remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) override; + int list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) override; + int count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) override; + int list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) override; + virtual std::unique_ptr get_object(const rgw_obj_key& k) override; virtual std::string get_cluster_id(const DoutPrefixProvider* dpp, optional_yield y); std::unique_ptr get_bucket(const RGWBucketInfo& i) override; diff --git a/src/rgw/rgw_sal_filter.cc b/src/rgw/rgw_sal_filter.cc index 780f445710d..66a2466040b 100644 --- a/src/rgw/rgw_sal_filter.cc +++ b/src/rgw/rgw_sal_filter.cc @@ -300,6 +300,71 @@ int FilterDriver::list_account_users(const DoutPrefixProvider* dpp, marker, max_items, listing); } +int FilterDriver::load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) +{ + return next->load_group_by_id(dpp, y, id, info, attrs, objv); +} + +int FilterDriver::load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) +{ + return next->load_group_by_name(dpp, y, account_id, name, info, attrs, objv); +} + +int FilterDriver::store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) +{ + return next->store_group(dpp, y, info, attrs, objv, exclusive, old_info); +} + +int FilterDriver::remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) +{ + return next->remove_group(dpp, y, info, objv); +} + +int FilterDriver::list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) +{ + return next->list_group_users(dpp, y, tenant, id, marker, max_items, listing); +} + +int FilterDriver::count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) +{ + return next->count_account_groups(dpp, y, account_id, count); +} + +int FilterDriver::list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) +{ + return next->list_account_groups(dpp, y, account_id, path_prefix, + marker, max_items, listing); +} + std::unique_ptr FilterDriver::get_object(const rgw_obj_key& k) { std::unique_ptr o = next->get_object(k); @@ -707,6 +772,13 @@ int FilterUser::verify_mfa(const std::string& mfa_str, bool* verified, return next->verify_mfa(mfa_str, verified, dpp, y); } +int FilterUser::list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) +{ + return next->list_groups(dpp, y, marker, max_items, listing); +} + std::unique_ptr FilterBucket::get_object(const rgw_obj_key& k) { std::unique_ptr o = next->get_object(k); diff --git a/src/rgw/rgw_sal_filter.h b/src/rgw/rgw_sal_filter.h index 91724854a87..bbba9eafde5 100644 --- a/src/rgw/rgw_sal_filter.h +++ b/src/rgw/rgw_sal_filter.h @@ -235,6 +235,43 @@ public: uint32_t max_items, UserList& listing) override; + int load_group_by_id(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view id, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int load_group_by_name(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view name, + RGWGroupInfo& info, Attrs& attrs, + RGWObjVersionTracker& objv) override; + int store_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, const Attrs& attrs, + RGWObjVersionTracker& objv, bool exclusive, + const RGWGroupInfo* old_info) override; + int remove_group(const DoutPrefixProvider* dpp, optional_yield y, + const RGWGroupInfo& info, + RGWObjVersionTracker& objv) override; + int list_group_users(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view tenant, + std::string_view id, + std::string_view marker, + uint32_t max_items, + UserList& listing) override; + int count_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + uint32_t& count) override; + int list_account_groups(const DoutPrefixProvider* dpp, + optional_yield y, + std::string_view account_id, + std::string_view path_prefix, + std::string_view marker, + uint32_t max_items, + GroupList& listing) override; + virtual std::unique_ptr get_object(const rgw_obj_key& k) override; std::unique_ptr get_bucket(const RGWBucketInfo& i) override; int load_bucket(const DoutPrefixProvider* dpp, const rgw_bucket& b, @@ -490,6 +527,9 @@ public: virtual int remove_user(const DoutPrefixProvider* dpp, optional_yield y) override; virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override; + int list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + GroupList& listing) override; RGWUserInfo& get_info() override { return next->get_info(); } virtual void print(std::ostream& out) const override { return next->print(out); } diff --git a/src/rgw/rgw_sal_fwd.h b/src/rgw/rgw_sal_fwd.h index 3019c06c518..39039cf4c3f 100644 --- a/src/rgw/rgw_sal_fwd.h +++ b/src/rgw/rgw_sal_fwd.h @@ -48,6 +48,7 @@ namespace sal { struct RGWRoleInfo; class RGWRole; struct RoleList; + struct GroupList; class DataProcessor; class ObjectProcessor; class ReadStatsCB; diff --git a/src/rgw/rgw_zone.cc b/src/rgw/rgw_zone.cc index ecf3df31a67..8d8b44cd961 100644 --- a/src/rgw/rgw_zone.cc +++ b/src/rgw/rgw_zone.cc @@ -299,6 +299,7 @@ void RGWZoneParams::decode_json(JSONObj *obj) JSONDecoder::decode_json("notif_pool", notif_pool, obj); JSONDecoder::decode_json("topics_pool", topics_pool, obj); JSONDecoder::decode_json("account_pool", account_pool, obj); + JSONDecoder::decode_json("group_pool", group_pool, obj); JSONDecoder::decode_json("system_key", system_key, obj); JSONDecoder::decode_json("placement_pools", placement_pools, obj); JSONDecoder::decode_json("tier_config", tier_config, obj); @@ -325,6 +326,7 @@ void RGWZoneParams::dump(Formatter *f) const encode_json("notif_pool", notif_pool, f); encode_json("topics_pool", topics_pool, f); encode_json("account_pool", account_pool, f); + encode_json("group_pool", group_pool, f); encode_json_plain("system_key", system_key, f); encode_json("placement_pools", placement_pools, f); encode_json("tier_config", tier_config, f); @@ -485,6 +487,7 @@ void add_zone_pools(const RGWZoneParams& info, pools.insert(info.notif_pool); pools.insert(info.topics_pool); pools.insert(info.account_pool); + pools.insert(info.group_pool); for (const auto& [pname, placement] : info.placement_pools) { pools.insert(placement.index_pool); @@ -591,6 +594,7 @@ int RGWZoneParams::fix_pool_names(const DoutPrefixProvider *dpp, optional_yield notif_pool = fix_zone_pool_dup(pools, name ,".rgw.log:notif", notif_pool); topics_pool = fix_zone_pool_dup(pools, name, ".rgw.meta:topics", topics_pool); account_pool = fix_zone_pool_dup(pools, name, ".rgw.meta:accounts", account_pool); + group_pool = fix_zone_pool_dup(pools, name, ".rgw.meta:groups", group_pool); for(auto& iter : placement_pools) { iter.second.index_pool = fix_zone_pool_dup(pools, name, "." + default_bucket_index_pool_suffix, @@ -1255,6 +1259,7 @@ int init_zone_pool_names(const DoutPrefixProvider *dpp, optional_yield y, info.topics_pool = fix_zone_pool_dup(pools, info.name, ".rgw.meta:topics", info.topics_pool); info.account_pool = fix_zone_pool_dup(pools, info.name, ".rgw.meta:accounts", info.account_pool); + info.group_pool = fix_zone_pool_dup(pools, info.name, ".rgw.meta:groups", info.group_pool); for (auto& [pname, placement] : info.placement_pools) { placement.index_pool = fix_zone_pool_dup(pools, info.name, "." + default_bucket_index_pool_suffix, placement.index_pool); diff --git a/src/rgw/services/svc_user_rados.cc b/src/rgw/services/svc_user_rados.cc index 80625cb6d20..3ca8c36361b 100644 --- a/src/rgw/services/svc_user_rados.cc +++ b/src/rgw/services/svc_user_rados.cc @@ -21,6 +21,7 @@ #include "driver/rados/account.h" #include "driver/rados/buckets.h" +#include "driver/rados/group.h" #include "driver/rados/users.h" #define dout_subsys ceph_subsys_rgw @@ -370,6 +371,25 @@ public: << " to account " << info.account_id << dendl; } + for (const auto& group_id : info.group_ids) { + if (old_info && old_info->group_ids.count(group_id)) { + continue; + } + // link the user to its group + const RGWZoneParams& zone = svc.zone->get_zone_params(); + const auto& users = rgwrados::group::get_users_obj(zone, group_id); + ret = rgwrados::users::add(dpp, y, rados, users, info, false, + std::numeric_limits::max()); + if (ret < 0) { + ldpp_dout(dpp, 20) << "WARNING: failed to link user " + << info.user_id << " to group " << group_id + << ": " << cpp_strerror(ret) << dendl; + return ret; + } + ldpp_dout(dpp, 20) << "linked user " << info.user_id + << " to group " << group_id << dendl; + } + return 0; } @@ -430,6 +450,20 @@ public: } } + for (const auto& group_id : old_info.group_ids) { + if (info.group_ids.count(group_id)) { + continue; + } + // remove from the old group + const RGWZoneParams& zone = svc.zone->get_zone_params(); + const auto& users = rgwrados::group::get_users_obj(zone, group_id); + ret = rgwrados::users::remove(dpp, y, rados, users, old_info.display_name); + if (ret < 0 && ret != -ENOENT) { + set_err_msg("ERROR: could not unlink from group " + group_id); + return ret; + } + } + return 0; } @@ -571,6 +605,17 @@ int RGWSI_User_RADOS::remove_user_info(RGWSI_MetaBackend::Context *ctx, } } + for (const auto& group_id : info.group_ids) { + const RGWZoneParams& zone = svc.zone->get_zone_params(); + const auto& users = rgwrados::group::get_users_obj(zone, group_id); + ret = rgwrados::users::remove(dpp, y, *rados, users, info.display_name); + if (ret < 0 && ret != -ENOENT) { + ldpp_dout(dpp, 0) << "ERROR: could not unlink from group " + << group_id << ": " << cpp_strerror(ret) << dendl; + return ret; + } + } + ret = remove_uid_index(ctx, info, objv_tracker, y, dpp); if (ret < 0 && ret != -ENOENT) { return ret; diff --git a/src/test/rgw/test_rgw_lua.cc b/src/test/rgw/test_rgw_lua.cc index 01686b6e374..68f2e6ab066 100644 --- a/src/test/rgw/test_rgw_lua.cc +++ b/src/test/rgw/test_rgw_lua.cc @@ -118,6 +118,11 @@ public: virtual int verify_mfa(const std::string& mfa_str, bool* verified, const DoutPrefixProvider* dpp, optional_yield y) override { return 0; } + int list_groups(const DoutPrefixProvider* dpp, optional_yield y, + std::string_view marker, uint32_t max_items, + rgw::sal::GroupList& listing) override { + return 0; + } virtual ~TestUser() = default; }; diff --git a/src/tools/ceph-dencoder/rgw_types.h b/src/tools/ceph-dencoder/rgw_types.h index 0fc312a374b..da1fde8f485 100644 --- a/src/tools/ceph-dencoder/rgw_types.h +++ b/src/tools/ceph-dencoder/rgw_types.h @@ -249,6 +249,9 @@ TYPE(rgw_user) #include "rgw_oidc_provider.h" TYPE(RGWOIDCProviderInfo) +#include "driver/rados/groups.h" +TYPE(rgwrados::groups::resource_metadata) + #include "driver/rados/roles.h" TYPE(rgwrados::roles::resource_metadata)