]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
rgw: Inject keystone userid into IAM policy 67752/head
authorSupriti Singh <supriti.singh@clyso.com>
Thu, 19 Mar 2026 09:26:32 +0000 (10:26 +0100)
committerSupriti Singh <supriti.singh@clyso.com>
Tue, 21 Apr 2026 11:40:32 +0000 (13:40 +0200)
Expose the Keystone token user UUID as condition key "keystone:userid"
so IAM and bucket policies can restrict access by user (e.g. per-user
buckets or user-specific allow/deny).

On-behalf-of: SAP <supriti.singh@clyso.com>
Signed-off-by: Supriti Singh <supriti.singh@clyso.com>
doc/radosgw/bucketpolicy.rst
doc/radosgw/keystone.rst
src/rgw/rgw_auth.cc
src/rgw/rgw_auth.h
src/rgw/rgw_auth_keystone.cc
src/test/rgw/test_rgw_iam_policy.cc

index a92f836680f13c4516545b6b0a38d38a2521d2ad..392fa192682cfb4bfecc841e24b823d0a9a2f929 100644 (file)
@@ -126,6 +126,7 @@ For all requests, condition keys we support are:
 Request that authenticate with Keystone also include:
 
 - keystone:role
+- keystone:userid
 
 We support certain S3 condition keys for bucket and object requests.
 
index 577ef5fd2c4cd91371524b3b775f73f0410b92ef..72e9236f08a2e9bbfcfc8fa8360405cc28e2fdd4 100644 (file)
@@ -181,10 +181,17 @@ S3 API (with AWS-like access and secret keys), if the ``rgw s3 auth
 use keystone`` option is set. For details, see
 :doc:`s3/authentication`.
 
-Requests authenticated via Keystone expose the Keystone role names in the
-IAM policy environment as the condition key ``keystone:role``. It can be
-used in bucket policies and idenitity policies to allow or deny access by
-role (e.g. ``StringEquals`` on ``keystone:role``).
+Requests authenticated via Keystone (Swift tokens or S3 with Keystone-managed
+credentials) expose the Keystone idenitity in the IAM policy environment
+as the condition keys: ``keystone:role`` (role names) and
+``keystone:userid`` (user UUID). These conditions can be used in bucket
+policies and idenitity policies with ``StringEquals``, ``StringNotEquals`` etc.
+
+- **keystone:role** - Allow or deny by *role* (e.g only users with role
+  ``reader`` get read access). Use for RBAC.
+- **keystone:userid** - Restrict to specific *user*. Use when policy
+  depends on a specific user, not just their role.
+
 See :doc:`bucketpolicy` for list of supported condition keys.
 
 Service Token Support
index 4de695839dd9b27ae5015949edf8e94e18721969..b152b65c57289dbff7cfad5ff3654e11e6cba836 100644 (file)
@@ -1052,6 +1052,9 @@ void rgw::auth::RemoteApplier::modify_request_state(const DoutPrefixProvider* dp
     s->env.emplace("keystone:role", std::move(role));
   }
 
+  if (!info.keystone_user_id.empty()) {
+    s->env.emplace("keystone:userid", info.keystone_user_id);
+  }
 }
 
 std::optional<rgw::ARN> rgw::auth::RemoteApplier::get_caller_identity() const 
index 3cb5f0c265a6fe0126cd96bae4324ca35081a737..37487e63bea47bf1313b526db2df4263689db0d0 100644 (file)
@@ -601,6 +601,7 @@ public:
     const std::string keystone_user;
     const std::optional<rgw::keystone::ScopeInfo> keystone_scope;
     const std::vector<std::string> keystone_roles;
+    const std::string keystone_user_id;
 
   public:
     enum class acct_privilege_t {
@@ -621,7 +622,9 @@ public:
              const std::string keystone_user,
              const uint32_t acct_type=TYPE_NONE,
              std::optional<rgw::keystone::ScopeInfo> keystone_scope=std::nullopt,
-             std::vector<std::string> keystone_roles = {})
+             std::vector<std::string> keystone_roles = {},
+             const std::string keystone_user_id = {}
+            )
     : acct_user(acct_user),
       acct_name(acct_name),
       perm_mask(perm_mask),
@@ -631,7 +634,8 @@ public:
       subuser(subuser),
       keystone_user(keystone_user),
       keystone_scope(std::move(keystone_scope)),
-      keystone_roles(std::move(keystone_roles)) {
+      keystone_roles(std::move(keystone_roles)),
+      keystone_user_id(keystone_user_id) {
     }
   };
 
index 31c65b991c11129eff6f58a74e83870240005c4e..d06d125b77f8f9d30fa0cf2541affe7f65717a5f 100644 (file)
@@ -172,7 +172,8 @@ TokenEngine::get_creds_info(const TokenEngine::token_envelope_t& token
     token.get_user_name(),
     TYPE_KEYSTONE,
     std::move(keystone_scope),
-    std::move(role_names)
+    std::move(role_names),
+    token.get_user_id()
   };
 }
 
@@ -689,7 +690,8 @@ EC2Engine::get_creds_info(const EC2Engine::token_envelope_t& token,
     token.get_user_name(),
     TYPE_KEYSTONE,
     std::move(keystone_scope),
-    std::move(role_names)
+    std::move(role_names),
+    token.get_user_id()
   };
 }
 
index 266b78bb2c82597782574a716768f50ba849e354..b0f83ac4ec708c43b8b7d7f8f16c7ffdfdbeccea 100644 (file)
@@ -1901,4 +1901,15 @@ TEST_F(ConditionTest, KeyStoneRolePolicyParsing)
   multi_env.emplace("keystone:role", "member");
   multi_env.emplace("keystone:role", "testrole");
   EXPECT_TRUE(p->statements[0].conditions[0].eval(multi_env));
+}
+
+TEST_F(ConditionTest, KeystoneUserIdStringEquals)
+{
+  const std::string key = "keystone:userid";
+  Condition cond{TokenID::StringEquals, key.data(), key.size(), false};
+  cond.vals.push_back("user-123");
+
+  EXPECT_FALSE(cond.eval({}));
+  EXPECT_TRUE(cond.eval({{key, "user-123"}}));
+  EXPECT_FALSE(cond.eval({{key, "user-456"}}));
 }
\ No newline at end of file