You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ozone.apache.org by sa...@apache.org on 2026/04/13 09:38:41 UTC

(ozone) branch HDDS-13323-sts updated: HDDS-14808. [STS] Part 4 - IAM Session Policy and ListBucket improvements (#9899)

This is an automated email from the ASF dual-hosted git repository.

sammichen pushed a commit to branch HDDS-13323-sts
in repository https://gitbox.apache.org/repos/asf/ozone.git


The following commit(s) were added to refs/heads/HDDS-13323-sts by this push:
     new 2a855ae8a72 HDDS-14808. [STS] Part 4 - IAM Session Policy and ListBucket improvements (#9899)
2a855ae8a72 is described below

commit 2a855ae8a7274036006f591cc5226035ebd8b598
Author: fmorg-git <fm...@gmail.com>
AuthorDate: Mon Apr 13 02:38:34 2026 -0700

    HDDS-14808. [STS] Part 4 - IAM Session Policy and ListBucket improvements (#9899)
---
 .../security/acl/iam/IamSessionPolicyResolver.java | 76 +++++++++++++------
 .../acl/iam/TestIamSessionPolicyResolver.java      | 85 ++++++++++++++--------
 2 files changed, 106 insertions(+), 55 deletions(-)

diff --git a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/acl/iam/IamSessionPolicyResolver.java b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/acl/iam/IamSessionPolicyResolver.java
index b8b032f1b35..e45c9662952 100644
--- a/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/acl/iam/IamSessionPolicyResolver.java
+++ b/hadoop-ozone/common/src/main/java/org/apache/hadoop/ozone/security/acl/iam/IamSessionPolicyResolver.java
@@ -97,6 +97,8 @@ public final class IamSessionPolicyResolver {
   private static final String[] S3_ACTION_PREFIXES = {"s3:Get", "s3:Put", "s3:List", "s3:Delete", "s3:Create"};
 
   private static final String ERROR_PREFIX = "IAM session policy: ";
+  private static final String STRING_EQUALS = "StringEquals";
+  private static final String STRING_LIKE = "StringLike";
 
   @VisibleForTesting
   static final Map<String, Set<S3Action>> S3_ACTION_MAP_CI = buildCaseInsensitiveS3ActionMap();
@@ -143,7 +145,7 @@ public static Set<AssumeRoleRequest.OzoneGrant> resolve(String policyJson, Strin
       final Set<String> resources = readStringOrArray(stmt.get("Resource"));
 
       // Parse prefixes from conditions, if any
-      final Set<String> prefixes = parsePrefixesFromConditions(stmt);
+      final Condition condition = parsePrefixesFromConditions(stmt);
 
       // Map actions to S3Action enum if possible
       final Set<S3Action> mappedS3Actions = mapPolicyActionsToS3Actions(actions);
@@ -156,7 +158,7 @@ public static Set<AssumeRoleRequest.OzoneGrant> resolve(String policyJson, Strin
       final Set<ResourceSpec> resourceSpecs = validateAndCategorizeResources(authorizerType, resources);
 
       // For each action, map to Ozone objects (paths) and acls based on resource specs and prefixes
-      createPathsAndPermissions(volumeName, authorizerType, mappedS3Actions, resourceSpecs, prefixes, objToAclsMap);
+      createPathsAndPermissions(volumeName, authorizerType, mappedS3Actions, resourceSpecs, condition, objToAclsMap);
     }
 
     // Group accumulated objects by their ACL sets to create final result
@@ -263,10 +265,10 @@ private static Set<String> readStringOrArray(JsonNode node) {
    * that if there is a Condition, there is only one and that the Condition
    * operator and key name are supported.
    * <p>
-   * Only the StringEquals operator and s3:prefix key name are supported.
+   * Only the StringEquals and StringLike operators and s3:prefix key name are supported.
    */
-  private static Set<String> parsePrefixesFromConditions(JsonNode stmt) throws OMException {
-    Set<String> prefixes = Collections.emptySet();
+  private static Condition parsePrefixesFromConditions(JsonNode stmt) throws OMException {
+    Condition condition = null;
     final JsonNode cond = stmt.get("Condition");
     if (cond != null && !cond.isMissingNode() && !cond.isNull()) {
       if (cond.size() != 1) {
@@ -275,12 +277,12 @@ private static Set<String> parsePrefixesFromConditions(JsonNode stmt) throws OME
 
       if (!cond.isObject()) {
         throw new OMException(
-            ERROR_PREFIX + "Invalid Condition (must have operator StringEquals or StringLike " +
-            "and key name s3:prefix) - " + cond, MALFORMED_POLICY_DOCUMENT);
+            ERROR_PREFIX + "Invalid Condition (must have operator " + STRING_EQUALS + " or " + STRING_LIKE +
+            " and key name s3:prefix) - " + cond, MALFORMED_POLICY_DOCUMENT);
       }
 
       final String operator = cond.fieldNames().next();
-      if (!"StringEquals".equals(operator) && !"StringLike".equals(operator)) {
+      if (!STRING_EQUALS.equals(operator) && !STRING_LIKE.equals(operator)) {
         throw new OMException(ERROR_PREFIX + "Unsupported Condition operator - " + operator, NOT_SUPPORTED_OPERATION);
       }
 
@@ -300,10 +302,11 @@ private static Set<String> parsePrefixesFromConditions(JsonNode stmt) throws OME
         throw new OMException(ERROR_PREFIX + "Unsupported Condition key name - " + keyName, NOT_SUPPORTED_OPERATION);
       }
 
-      prefixes = readStringOrArray(operatorValue.get(keyName));
+      final Set<String> prefixes = readStringOrArray(operatorValue.get(keyName));
+      condition = new Condition(operator, prefixes);
     }
 
-    return prefixes;
+    return condition;
   }
 
   /**
@@ -427,10 +430,11 @@ static Set<ResourceSpec> validateAndCategorizeResources(AuthorizerType authorize
    */
   @VisibleForTesting
   static void createPathsAndPermissions(String volumeName, AuthorizerType authorizerType, Set<S3Action> mappedS3Actions,
-      Set<ResourceSpec> resourceSpecs, Set<String> prefixes, Map<IOzoneObj, Set<ACLType>> objToAclsMap) {
+      Set<ResourceSpec> resourceSpecs, Condition condition, Map<IOzoneObj, Set<ACLType>> objToAclsMap) {
     // Process each resource spec with the given actions
     for (ResourceSpec resourceSpec : resourceSpecs) {
-      processResourceSpecWithActions(volumeName, authorizerType, mappedS3Actions, resourceSpec, prefixes, objToAclsMap);
+      processResourceSpecWithActions(
+          volumeName, authorizerType, mappedS3Actions, resourceSpec, condition, objToAclsMap);
     }
   }
 
@@ -461,7 +465,7 @@ static Set<AssumeRoleRequest.OzoneGrant> groupObjectsByAcls(Map<IOzoneObj, Set<A
    * IOzoneObj to ACLType mappings to the provided map.
    */
   private static void processResourceSpecWithActions(String volumeName, AuthorizerType authorizerType,
-      Set<S3Action> mappedS3Actions, ResourceSpec resourceSpec, Set<String> prefixes,
+      Set<S3Action> mappedS3Actions, ResourceSpec resourceSpec, Condition condition,
       Map<IOzoneObj, Set<ACLType>> objToAclsMap) {
 
     // Process based on ResourceSpec type
@@ -473,13 +477,13 @@ private static void processResourceSpecWithActions(String volumeName, Authorizer
       processResourceTypeAny(volumeName, mappedS3Actions, objToAclsMap);
       break;
     case BUCKET:
-      processBucketResource(volumeName, mappedS3Actions, resourceSpec, prefixes, authorizerType, objToAclsMap);
+      processBucketResource(volumeName, mappedS3Actions, resourceSpec, condition, authorizerType, objToAclsMap);
       break;
     case BUCKET_WILDCARD:
       Preconditions.checkArgument(
           authorizerType != AuthorizerType.NATIVE,
           "ResourceSpec type BUCKET_WILDCARD not supported for OzoneNativeAuthorizer");
-      processBucketResource(volumeName, mappedS3Actions, resourceSpec, prefixes, authorizerType, objToAclsMap);
+      processBucketResource(volumeName, mappedS3Actions, resourceSpec, condition, authorizerType, objToAclsMap);
       break;
     case OBJECT_EXACT:
       processObjectExactResource(volumeName, mappedS3Actions, resourceSpec, objToAclsMap);
@@ -520,7 +524,7 @@ private static void processResourceTypeAny(String volumeName, Set<S3Action> mapp
    *          "Resource": "arn:aws:s3:::*"
    */
   private static void processBucketResource(String volumeName, Set<S3Action> mappedS3Actions,
-      ResourceSpec resourceSpec, Set<String> prefixes, AuthorizerType authorizerType,
+      ResourceSpec resourceSpec, Condition condition, AuthorizerType authorizerType,
       Map<IOzoneObj, Set<ACLType>> objToAclsMap) {
     for (S3Action action : mappedS3Actions) {
       // The s3:ListAllMyBuckets action can use either "*" or
@@ -548,12 +552,16 @@ private static void processBucketResource(String volumeName, Set<S3Action> mappe
       if (action == S3Action.LIST_BUCKET || action == S3Action.ALL_S3) {
         // If condition prefixes are present, these would constrain the object permissions if the action
         // is s3:ListBucket or s3:* (which includes s3:ListBucket)
-        if (prefixes != null && !prefixes.isEmpty()) {
-          for (String prefix : prefixes) {
+        if (condition != null && condition.prefixes != null && !condition.prefixes.isEmpty()) {
+          for (String prefix : condition.prefixes) {
+            // If operator is StringEquals, we should ignore any prefix containing wildcards - this is AWS behavior
+            if (STRING_EQUALS.equals(condition.operator) && hasWildcard(prefix)) {
+              continue;
+            }
             createObjectResourcesFromConditionPrefix(
                 volumeName, authorizerType, resourceSpec, prefix, objToAclsMap, EnumSet.of(READ));
           }
-        } else {
+        } else if (condition == null) {
           // No condition prefixes, but we need READ access to all objects, so use "*" as the prefix
           createObjectResourcesFromConditionPrefix(
               volumeName, authorizerType, resourceSpec, "*", objToAclsMap, EnumSet.of(READ));
@@ -590,19 +598,21 @@ private static void processObjectExactResource(String volumeName, Set<S3Action>
   private static void processObjectPrefixResource(String volumeName, AuthorizerType authorizerType,
       Set<S3Action> mappedS3Actions, ResourceSpec resourceSpec, Map<IOzoneObj, Set<ACLType>> objToAclsMap) {
     for (S3Action action : mappedS3Actions) {
-      // Object actions apply to prefix/key resources
+      // Object actions apply to prefix/key resources - ensure to add the acls only for the appropriate action type
       if (action.kind == ActionKind.OBJECT) {
         addAclsForObj(objToAclsMap, volumeObj(volumeName), action.volumePerms);
         addAclsForObj(objToAclsMap, bucketObj(volumeName, resourceSpec.bucket), action.bucketPerms);
+        // Handle the resource prefix itself (e.g., my-bucket/*)
+        createObjectResourcesFromResourcePrefix(
+            volumeName, authorizerType, resourceSpec, objToAclsMap, action.objectPerms);
       } else if (action == S3Action.ALL_S3) {
         addAclsForObj(objToAclsMap, volumeObj(volumeName), EnumSet.of(READ));
         // For s3:*, ALL should only apply at the object/prefix level; grant READ at bucket level for navigation
         addAclsForObj(objToAclsMap, bucketObj(volumeName, resourceSpec.bucket), EnumSet.of(READ));
+        // Handle the resource prefix itself (e.g., my-bucket/*)
+        createObjectResourcesFromResourcePrefix(
+            volumeName, authorizerType, resourceSpec, objToAclsMap, action.objectPerms);
       }
-
-      // Handle the resource prefix itself (e.g., my-bucket/*)
-      createObjectResourcesFromResourcePrefix(
-          volumeName, authorizerType, resourceSpec, objToAclsMap, action.objectPerms);
     }
   }
 
@@ -708,6 +718,20 @@ enum S3ResourceType {
     OBJECT_EXACT
   }
 
+  /**
+   * Encapsulates the Condition operator and values.
+   */
+  @VisibleForTesting
+  public static final class Condition {
+    private final String operator;
+    private final Set<String> prefixes;
+
+    public Condition(String operator, Set<String> prefixes) {
+      this.operator = operator;
+      this.prefixes = prefixes;
+    }
+  }
+
   /**
    * Utility to help categorize IAM policy resources, whether for bucket, key, wildcards, etc.
    */
@@ -902,4 +926,8 @@ private static IOzoneObj volumeObj(String volumeName) {
         .setVolumeName(volumeName)
         .build();
   }
+
+  private static boolean hasWildcard(String prefix) {
+    return ((prefix.contains("*") || prefix.contains("?")));
+  }
 }
diff --git a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java
index 41d2fc338f3..ae066686e70 100644
--- a/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java
+++ b/hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java
@@ -707,11 +707,11 @@ public void testCreatePathsAndPermissionsWithResourceAny() {
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.ANY, "*", null, null));
 
     expectIllegalArgumentException(
-        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), new LinkedHashMap<>()),
+        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, new LinkedHashMap<>()),
         "ResourceSpec type ANY not supported for OzoneNativeAuthorizer");
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     final Set<IOzoneObj> readAndListObjects = objSet(volume(), bucket("*")); // volume, bucket level have READ, LIST
     final Set<IOzoneObj> readObject = objSet(key("*", "*")); // key level has READ
@@ -729,14 +729,14 @@ public void testCreatePathsAndPermissionsWithBucketResourceThatIsListBucket() {
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
     final Set<IOzoneObj> nativeReadObjects = objSet(volume(), prefix("bucket1", ""));
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(readAndListObject, acls(READ, LIST)), new OzoneGrant(nativeReadObjects, acls(READ)));
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
     final Set<IOzoneObj> rangerReadObjects = objSet(volume(), key("bucket1", "*"));
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(readAndListObject, acls(READ, LIST)), new OzoneGrant(rangerReadObjects, acls(READ)));
@@ -751,13 +751,13 @@ public void testCreatePathsAndPermissionsWithBucketResourceThatIsNotListBucket()
     final Set<IOzoneObj> readObject = objSet(volume());
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(createObject, acls(CREATE)), new OzoneGrant(readObject, acls(READ)));
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(createObject, acls(CREATE)), new OzoneGrant(readObject, acls(READ)));
@@ -772,11 +772,11 @@ public void testCreatePathsAndPermissionsWithBucketWildcardResource() {
     final Set<IOzoneObj> readVolume = objSet(volume());
 
     expectIllegalArgumentException(
-        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), new LinkedHashMap<>()),
+        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, new LinkedHashMap<>()),
         "ResourceSpec type BUCKET_WILDCARD not supported for OzoneNativeAuthorizer");
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(writeAclObject, acls(WRITE_ACL)), new OzoneGrant(readVolume, acls(READ)));
@@ -794,11 +794,11 @@ public void testCreatePathsAndPermissionsWithBucketsWildcardResourceAll() {
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET_WILDCARD, "*", null, null));
 
     expectIllegalArgumentException(
-        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), new LinkedHashMap<>()),
+        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, new LinkedHashMap<>()),
         "ResourceSpec type BUCKET_WILDCARD not supported for OzoneNativeAuthorizer");
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
 
     // Both the volume and the wildcard bucket should end up with READ + LIST permissions.
     // We also need READ access on the keys
@@ -817,12 +817,12 @@ public void testCreatePathsAndPermissionsWithObjectExactResource() {
     final Set<IOzoneObj> readObjects = objSet(key("bucket1", "key.txt"), bucket("bucket1"), volume());
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactly(new OzoneGrant(readObjects, acls(READ)));
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactly(new OzoneGrant(readObjects, acls(READ)));
   }
@@ -835,12 +835,12 @@ public void testCreatePathsAndPermissionsWithObjectPrefixResource() {
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX, "bucket1", "prefix/", null));
     final Set<IOzoneObj> nativeReadObjects = objSet(prefix("bucket1", "prefix/"), bucket("bucket1"), volume());
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactly(new OzoneGrant(nativeReadObjects, acls(READ)));
 
     expectIllegalArgumentException(
-        () -> createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), new LinkedHashMap<>()),
+        () -> createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, new LinkedHashMap<>()),
         "ResourceSpec type OBJECT_PREFIX not supported for RangerOzoneAuthorizer");
   }
 
@@ -851,12 +851,12 @@ public void testCreatePathsAndPermissionsWithObjectPrefixWildcardResource() {
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX_WILDCARD, "bucket1", "prefix/*", null));
 
     expectIllegalArgumentException(
-        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), new LinkedHashMap<>()),
+        () -> createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, new LinkedHashMap<>()),
         "ResourceSpec type OBJECT_PREFIX_WILDCARD not supported for OzoneNativeAuthorizer");
 
     final Set<IOzoneObj> rangerReadObjects = objSet(key("bucket1", "prefix/*"), bucket("bucket1"), volume());
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactly(new OzoneGrant(rangerReadObjects, acls(READ)));
   }
@@ -865,12 +865,14 @@ public void testCreatePathsAndPermissionsWithObjectPrefixWildcardResource() {
   public void testCreatePathsAndPermissionsWithConditionPrefixesForObjectActionMustIgnoreConditionPrefixes() {
     final Set<S3Action> actions = Collections.singleton(S3Action.GET_OBJECT);
     final Set<String> prefixes = strSet("folder1/", "folder2/");
+    final IamSessionPolicyResolver.Condition condition = new IamSessionPolicyResolver.Condition(
+        "StringEquals", prefixes);
 
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX, "bucket1", "", null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
     final Set<IOzoneObj> nativeReadObjects = objSet(prefix("bucket1", ""), bucket("bucket1"), volume());
-    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, prefixes, objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, condition, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactly(new OzoneGrant(nativeReadObjects, acls(READ)));
 
@@ -878,7 +880,7 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForObjectActionMus
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX_WILDCARD, "bucket1", "*", null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
     final Set<IOzoneObj> rangerReadObjects = objSet(key("bucket1", "*"), bucket("bucket1"), volume());
-    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, prefixes, objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, condition, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactly(new OzoneGrant(rangerReadObjects, acls(READ)));
   }
@@ -887,6 +889,8 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForObjectActionMus
   public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhenActionIsListBucket() {
     final Set<S3Action> actions = Collections.singleton(S3Action.LIST_BUCKET);
     final Set<String> prefixes = strSet("folder1/", "folder2/");
+    final IamSessionPolicyResolver.Condition condition = new IamSessionPolicyResolver.Condition(
+        "StringEquals", prefixes);
 
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, "bucket1", null, null));
@@ -894,7 +898,7 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
         prefix("bucket1", "folder1/"), prefix("bucket1", "folder2/"), volume());
     final Set<IOzoneObj> nativeReadAndListObject = objSet(bucket("bucket1"));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, prefixes, objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, condition, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(nativeReadObjects, acls(READ)), new OzoneGrant(nativeReadAndListObject, acls(READ, LIST)));
@@ -905,7 +909,7 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
         key("bucket1", "folder1/"), key("bucket1", "folder2/"), volume());
     final Set<IOzoneObj> rangerReadAndListObject = objSet(bucket("bucket1"));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, prefixes, objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, condition, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(rangerReadObjects, acls(READ)), new OzoneGrant(rangerReadAndListObject, acls(READ, LIST)));
@@ -915,13 +919,15 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
   public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhenActionIsNotListBucket() {
     final Set<S3Action> actions = Collections.singleton(S3Action.GET_BUCKET_ACL);
     final Set<String> prefixes = strSet("folder1/", "folder2/");
+    final IamSessionPolicyResolver.Condition condition = new IamSessionPolicyResolver.Condition(
+        "StringEquals", prefixes);
     final Set<IOzoneObj> readObject = objSet(volume());
     final Set<IOzoneObj> readAndReadAclObject = objSet(bucket("bucket1"));
 
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, "bucket1", null, null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, prefixes, objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, condition, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(readObject, acls(READ)), new OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
@@ -929,7 +935,7 @@ public void testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
     final Set<IamSessionPolicyResolver.ResourceSpec> rangerResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, "bucket1", null, null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, prefixes, objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, condition, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(readObject, acls(READ)), new OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
@@ -942,14 +948,14 @@ public void testCreatePathsAndPermissionsWithNoMappedActions() {
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX, "bucket1", null, null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).isEmpty();
 
     final Set<IamSessionPolicyResolver.ResourceSpec> rangerResourceSpecs = Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_PREFIX_WILDCARD, "bucket1", null, null));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).isEmpty();
   }
@@ -960,12 +966,12 @@ public void testCreatePathsAndPermissionsWithNoMappedResources() {
     final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = emptySet();
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).isEmpty();
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).isEmpty();
   }
@@ -981,13 +987,13 @@ public void testCreatePathsAndPermissionsDeduplicatesAcrossSameResourceTypes() {
     final Set<IOzoneObj> readObjects = objSet(bucket("bucket1"), volume());
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(readAndDeleteObject, acls(READ, DELETE)), new OzoneGrant(readObjects, acls(READ)));
 
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(readAndDeleteObject, acls(READ, DELETE)), new OzoneGrant(readObjects, acls(READ)));
@@ -1006,14 +1012,14 @@ public void testCreatePathsAndPermissionsWithAllS3ActionsOverridesAnyOtherAction
 
     final Set<IOzoneObj> nativeReadObjects = objSet(volume(), bucket("bucket1"), prefix("bucket2", ""));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, emptySet(), objToAclsMapNative);
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, null, objToAclsMapNative);
     final Set<OzoneGrant> resultNative = groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
         new OzoneGrant(allObjects, acls(ALL)), new OzoneGrant(nativeReadObjects, acls(READ)));
 
     final Set<IOzoneObj> rangerReadObjects = objSet(volume(), bucket("bucket1"), key("bucket2", "*"));
     final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new LinkedHashMap<>();
-    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, emptySet(), objToAclsMapRanger);
+    createPathsAndPermissions(VOLUME, RANGER, actions, resourceSpecs, null, objToAclsMapRanger);
     final Set<OzoneGrant> resultRanger = groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
         new OzoneGrant(allObjects, acls(ALL)), new OzoneGrant(rangerReadObjects, acls(READ)));
@@ -1519,7 +1525,7 @@ public void testIgnoresUnsupportedActionsWhenSupportedActionsAreIncluded() throw
         "      ],\n" +
         "      \"Resource\": \"arn:aws:s3:::bucket1\",\n" +
         "      \"Condition\": {\n" +
-        "        \"StringEquals\": {\n" +
+        "        \"StringLike\": {\n" +
         "          \"s3:prefix\": [ \"team/folder\", \"team/folder/*\" ]\n" +
         "        }\n" +
         "      }\n" +
@@ -1577,6 +1583,23 @@ public void testMultiplePrefixesWithWildcards() throws OMException {
     assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
   }
 
+  @Test
+  public void testListBucketOnObjectResourceReturnsEmpty() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:ListBucket\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::logs/*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, RANGER);
+
+    assertThat(resolvedFromNativeAuthorizer).isEmpty();
+    assertThat(resolvedFromRangerAuthorizer).isEmpty();
+  }
+
   @Test
   public void testObjectResourceWithWildcardInMiddle() throws OMException {
     final String json = "{\n" +


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@ozone.apache.org
For additional commands, e-mail: commits-help@ozone.apache.org