]> git.apps.os.sepia.ceph.com Git - ceph-ci.git/commitdiff
rgw/sal: add backend interfaces for group metadata
authorCasey Bodley <cbodley@redhat.com>
Sun, 11 Feb 2024 17:15:41 +0000 (12:15 -0500)
committerCasey Bodley <cbodley@redhat.com>
Wed, 10 Apr 2024 17:09:16 +0000 (13:09 -0400)
Signed-off-by: Casey Bodley <cbodley@redhat.com>
24 files changed:
src/rgw/CMakeLists.txt
src/rgw/driver/rados/account.cc
src/rgw/driver/rados/account.h
src/rgw/driver/rados/group.cc [new file with mode: 0644]
src/rgw/driver/rados/group.h [new file with mode: 0644]
src/rgw/driver/rados/groups.cc [new file with mode: 0644]
src/rgw/driver/rados/groups.h [new file with mode: 0644]
src/rgw/driver/rados/rgw_sal_rados.cc
src/rgw/driver/rados/rgw_sal_rados.h
src/rgw/driver/rados/rgw_service.cc
src/rgw/driver/rados/rgw_service.h
src/rgw/driver/rados/rgw_zone.h
src/rgw/rgw_common.cc
src/rgw/rgw_common.h
src/rgw/rgw_sal.h
src/rgw/rgw_sal_dbstore.cc
src/rgw/rgw_sal_dbstore.h
src/rgw/rgw_sal_filter.cc
src/rgw/rgw_sal_filter.h
src/rgw/rgw_sal_fwd.h
src/rgw/rgw_zone.cc
src/rgw/services/svc_user_rados.cc
src/test/rgw/test_rgw_lua.cc
src/tools/ceph-dencoder/rgw_types.h

index 957d5f06397a9c8a6ec7034f0b2e2c2596b1b8db..33ed562ea6fa77069b968f23745f7e699093602d 100644 (file)
@@ -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
index 79bc376b16302d4d3fcbf69ee63e21c4958a4613..02a269932274e2d2853fa314ee99cd02f5f443f8 100644 (file)
@@ -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);
 }
index f9d4f534a7fdc6b5c1755d3bf8be9540f26a3abb..635f096820061c02fcbb1df8d91738d8bf087996 100644 (file)
@@ -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 (file)
index 0000000..7cdd948
--- /dev/null
@@ -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 <boost/algorithm/string.hpp>
+#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<std::string, ceph::buffer::list>& 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<std::string, ceph::buffer::list>& 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<std::string, ceph::buffer::list>& 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<NameObj> 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<uint32_t>::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<std::string, bufferlist> 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<std::string>& oids,
+                        std::list<std::string>& 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<MetadataObject*>(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<std::string, ceph::buffer::list> 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<std::string, ceph::buffer::list> 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<int()> f) override
+  {
+    return -ENOTSUP; // unused
+  }
+
+  int list_keys_init(const DoutPrefixProvider* dpp,
+                     const std::string& marker, void** phandle) override
+  {
+    auto lister = std::make_unique<MetadataLister>(
+        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<std::string>& keys, bool* truncated) override
+  {
+    auto lister = static_cast<MetadataLister*>(handle);
+    return lister->get_next(dpp, max, keys, truncated);
+  }
+
+  void list_keys_complete(void* handle) override
+  {
+    delete static_cast<MetadataLister*>(handle);
+  }
+
+  std::string get_marker(void* handle) override
+  {
+    auto lister = static_cast<MetadataLister*>(handle);
+    return lister->get_marker();
+  }
+};
+
+auto create_metadata_handler(RGWSI_SysObj& sysobj, librados::Rados& rados,
+                             const RGWZoneParams& zone)
+    -> std::unique_ptr<RGWMetadataHandler>
+{
+  return std::make_unique<MetadataHandler>(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 (file)
index 0000000..b96d1cc
--- /dev/null
@@ -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 <map>
+#include <memory>
+#include <string>
+#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<RGWMetadataHandler>;
+
+/// 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<std::string, ceph::buffer::list>& 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<std::string, ceph::buffer::list>& 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<std::string, ceph::buffer::list>& 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 (file)
index 0000000..21f66e7
--- /dev/null
@@ -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<std::string>& 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<cls_user_account_resource> 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<resource_metadata*>& 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 (file)
index 0000000..50ebcad
--- /dev/null
@@ -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 <list>
+#include <string>
+#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<std::string>& 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<resource_metadata*>& o);
+};
+WRITE_CLASS_ENCODER(resource_metadata);
+
+} // namespace rgwrados::groups
index f2ea4078b5377fc396e4dc11990405af85a9c92f..e26c48bac52156a175b19ef62567d47906770521 100644 (file)
@@ -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<std::string> 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<std::string> 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<Object> RadosStore::get_object(const rgw_obj_key& k)
 {
   return std::make_unique<RadosObject>(this, k);
index 07eeeb4618a3c86b750921d3184fccb460c391f2..669bc73de1376a9a9ad368e26298d08f84b55008 100644 (file)
@@ -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<Object> get_object(const rgw_obj_key& k) override;
     std::unique_ptr<Bucket> 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;
 };
index d8b4610ecafc7016f69d37eee4f981e8c70e4e41..938589f480717332a03a86fe06f553d6990f88a9 100644 (file)
@@ -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<rgw::sal::RGWRoleMetadataHandler>(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;
 }
 
index 486df8c71bea74d76d13a86d0a5abce0ac9b7ac9..ec7f73cf6eb50fb94e7b853307ea40196ac677a1 100644 (file)
@@ -198,6 +198,7 @@ struct RGWCtlDef {
     std::unique_ptr<RGWMetadataHandler> role;
     std::unique_ptr<RGWMetadataHandler> topic;
     std::unique_ptr<RGWMetadataHandler> account;
+    std::unique_ptr<RGWMetadataHandler> group;
 
     std::unique_ptr<RGWChainedCacheImpl<rgwrados::topic::cache_entry>> topic_cache;
 
index c45b42102a8c323513d6bd60ba6ac444b4afc1fe..c542abc76d6871833eecb18bc4b96cd0b489c6bc 100644 (file)
@@ -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);
   }
index d7543701fd53527e0723ee2162dab771ea149032..6ce92b742b1c84c8ad117a776bc9906231e5b194 100644 (file)
@@ -2597,6 +2597,7 @@ void RGWUserInfo::generate_test_instances(list<RGWUserInfo*>& 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";
@@ -2833,6 +2834,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)
@@ -2890,6 +2892,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);
 }
 
 
index aad3cef59150ab056fa1b7d11fea65e424ab516d..f8ca6988e783ce4907f52ee1a4339949c9c2211f 100644 (file)
@@ -22,6 +22,8 @@
 #include <unordered_map>
 
 #include <fmt/format.h>
+#include <boost/container/flat_map.hpp>
+#include <boost/container/flat_set.hpp>
 
 #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<std::string, std::string> tags;
+  boost::container::flat_set<std::string, std::less<>> 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 = "/";
     }
index 8f0ec5228b97cca81dbc245f5aeb3686676889d6..93de433bcf70775fd6833ade2a42019c3e81657e 100644 (file)
@@ -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<RGWGroupInfo> groups;
+  /// The next marker to resume listing, or empty
+  std::string next_marker;
+};
+
 /** A list of key-value attributes */
   using Attrs = std::map<std::string, ceph::buffer::list>;
 
@@ -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;
index e5518e21432d359ac8c34235a589643b119e6de7..0b60ebf70ec56d2f939830a58fc333f2d09b8b6f 100644 (file)
@@ -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
index 378ad90bf4acd0878c305598eeeff94c9c3d0a12..7139811544e8edaa7707e1b68631aaaf04eccaba 100644 (file)
@@ -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<Object> get_object(const rgw_obj_key& k) override;
       virtual std::string get_cluster_id(const DoutPrefixProvider* dpp, optional_yield y);
       std::unique_ptr<Bucket> get_bucket(const RGWBucketInfo& i) override;
index 780f445710d3aba9424b8729d8ddad8e33b34f72..66a2466040ba56f1c51893fa52f291a9fbbb3e3a 100644 (file)
@@ -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<Object> FilterDriver::get_object(const rgw_obj_key& k)
 {
   std::unique_ptr<Object> 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<Object> FilterBucket::get_object(const rgw_obj_key& k)
 {
   std::unique_ptr<Object> o = next->get_object(k);
index 91724854a8710017cf5584be0c65b87784bfb7e0..bbba9eafde57b48e2f309a5823057786e9212b92 100644 (file)
@@ -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<Object> get_object(const rgw_obj_key& k) override;
   std::unique_ptr<Bucket> 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); }
index 3019c06c5188edf88274c7f1d5c97274411f23d7..39039cf4c3f8012737440db063edac5ec337dab0 100644 (file)
@@ -48,6 +48,7 @@ namespace sal {
   struct RGWRoleInfo;
   class RGWRole;
   struct RoleList;
+  struct GroupList;
   class DataProcessor;
   class ObjectProcessor;
   class ReadStatsCB;
index ecf3df31a67b27ce0379ce9f14a3bdf46d1770a9..8d8b44cd961555fece1e79b3dee9c5477e614c82 100644 (file)
@@ -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);
index 80625cb6d2061e29a5c5c489f86dffb926149c02..3ca8c36361baf52827f8003092aa4e92776a1924 100644 (file)
@@ -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<uint32_t>::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;
index 01686b6e3741886ed80323d7dc5ae219f672df7b..68f2e6ab0666d9048e02c2ef515c3fea9b9cab17 100644 (file)
@@ -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;
 };
 
index cd4b6f794ffa2b8dcd80d122f330c4c592b59209..8cd0acfc62459d4b810683e95359d28fa9cfcca5 100644 (file)
@@ -250,6 +250,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)