fmorg-git commented on code in PR #9315:
URL: https://github.com/apache/ozone/pull/9315#discussion_r2597143462


##########
hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java:
##########
@@ -903,22 +914,1013 @@ public void 
testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
 
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = 
Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket1", null, null));
-    final Set<AssumeRoleRequest.OzoneGrant> resultNative = 
createPathsAndPermissions(
-        VOLUME, NATIVE, actions, nativeResourceSpecs, prefixes);
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, 
prefixes, objToAclsMapNative);
+    final Set<OzoneGrant> resultNative = 
groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
-        new AssumeRoleRequest.OzoneGrant(readObject, acls(READ)),
-        new AssumeRoleRequest.OzoneGrant(readAndReadAclObject, acls(READ, 
READ_ACL)));
+        new OzoneGrant(readObject, acls(READ)), new 
OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
 
     final Set<IamSessionPolicyResolver.ResourceSpec> rangerResourceSpecs = 
Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket1", null, null));
-    final Set<AssumeRoleRequest.OzoneGrant> resultRanger = 
createPathsAndPermissions(
-        VOLUME, RANGER, actions, rangerResourceSpecs, prefixes);
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, 
prefixes, objToAclsMapRanger);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).containsExactlyInAnyOrder(
+        new OzoneGrant(readObject, acls(READ)), new 
OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
+  }
+
+  @Test
+  public void testCreatePathsAndPermissionsWithNoMappedActions() {
+    final Set<S3Action> actions = emptySet();
+
+    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);
+    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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).isEmpty();
+  }
+
+  @Test
+  public void testCreatePathsAndPermissionsWithNoMappedResources() {
+    final Set<S3Action> actions = Collections.singleton(S3Action.GET_OBJECT);
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = 
emptySet();
+
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, 
emptySet(), 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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).isEmpty();
+  }
+
+  @Test
+  public void 
testCreatePathsAndPermissionsDeduplicatesAcrossSameResourceTypes() {
+    final Set<S3Action> actions = Stream.of(
+        S3Action.GET_OBJECT, S3Action.GET_OBJECT_TAGGING, 
S3Action.DELETE_OBJECT, S3Action.DELETE_OBJECT_TAGGING)
+        .collect(Collectors.toSet());
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = 
Collections.singleton(
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_EXACT, 
"bucket1", null, "key.txt"));
+    final Set<IOzoneObj> readAndDeleteObject = objSet(key("bucket1", 
"key.txt"));
+    final Set<IOzoneObj> readObjects = objSet(bucket("bucket1"), volume());
+
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, 
emptySet(), 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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
-        new AssumeRoleRequest.OzoneGrant(readObject, acls(READ)),
-        new AssumeRoleRequest.OzoneGrant(readAndReadAclObject, acls(READ, 
READ_ACL)));
+        new OzoneGrant(readAndDeleteObject, acls(READ, DELETE)), new 
OzoneGrant(readObjects, acls(READ)));
+  }
+
+  @Test
+  public void 
testCreatePathsAndPermissionsWithAllS3ActionsOverridesAnyOtherAction() {
+    final Set<S3Action> actions = Stream.of(
+        S3Action.ALL_S3, S3Action.GET_OBJECT, S3Action.DELETE_OBJECT, 
S3Action.LIST_BUCKET)
+        .collect(Collectors.toSet());
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = Stream.of(
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_EXACT, 
"bucket1", null, "key.txt"),
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket2", null, null))
+        .collect(Collectors.toSet());
+    final Set<IOzoneObj> allObjects = objSet(key("bucket1", "key.txt"), 
bucket("bucket2"));
+
+    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);
+    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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).containsExactlyInAnyOrder(
+        new OzoneGrant(allObjects, acls(ALL)), new 
OzoneGrant(rangerReadObjects, acls(READ)));
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsWhenSameStatementsArePresent() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
key "*"  READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsForSameActionsButDifferentResource() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket2\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"), 
bucket("my-bucket2"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(
+        objSet(volume(), prefix("my-bucket2", ""), prefix("my-bucket", "")), 
acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(
+        objSet(volume(), key("my-bucket2", "*"), key("my-bucket", "*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsForDifferentActionsButSameResource() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:CreateBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL, CREATE; 
volume, prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL, 
CREATE);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL, CREATE; 
volume, key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testDeduplicatesAcrossMultipleStatementsWhenAllActionPresent() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": \"s3:*\",\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket ALL (instead of individual actions); volume 
and prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket ALL (instead of individual actions); volume 
and key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllowGetPutOnKey() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket/folder/file.txt\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedFromBothAuthorizers = new 
LinkedHashSet<>();
+    // Expected: READ, CREATE, WRITE on key; bucket READ; volume READ
+    final Set<IOzoneObj> keySet = objSet(key("my-bucket", "folder/file.txt"));
+    final Set<ACLType> keyAcls = acls(READ, CREATE, WRITE);
+    expectedResolvedFromBothAuthorizers.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    expectedResolvedFromBothAuthorizers.add(new OzoneGrant(keySet, keyAcls));
+
+    
assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedFromBothAuthorizers);
+    
assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedFromBothAuthorizers);
+  }
+
+  @Test
+  public void testAllActionsForKey() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket/*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all key ACLs on prefix "" under bucket; bucket 
READ, volume READ
+    final Set<IOzoneObj> keyPrefixSet = objSet(prefix("my-bucket", ""));
+    final Set<ACLType> allKeyAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, allKeyAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    // Expected for Ranger: all key acls for resource type KEY with key name 
"*"
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    final Set<IOzoneObj> rangerKeySet = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, allKeyAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllActionsForBucket() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all Bucket ACLs for bucket; volume, prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> allBucketAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, allBucketAcls));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    // Expected for Ranger: all Bucket ACLs for bucket; volume, key "*" READ
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, allBucketAcls));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMultipleResourcesInSeparateStatements() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": \"s3:*\",\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket/*\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    // Expected for native: all key ACLs on prefix "" under bucket
+    final Set<IOzoneObj> keyPrefixSet = objSet(prefix("my-bucket", ""));
+    final Set<ACLType> keyAllAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, keyAllAcls));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    // Expected for Ranger: all key acls for resource type KEY with key name 
"*"
+    final Set<IOzoneObj> rangerKeySet = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, keyAllAcls));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMultipleResourcesInOneStatement() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:*\"\n" +
+        "      ],\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +
+        "      ]\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all for bucket and key acls; volume READ
+    final Set<IOzoneObj> resourceSetNative = objSet(bucket("my-bucket"), 
prefix("my-bucket", ""));
+    expectedResolvedNative.add(new OzoneGrant(resourceSetNative, acls(ALL)));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: all for bucket and key acls; volume READ
+    final Set<IOzoneObj> resourceSetRanger = objSet(bucket("my-bucket"), 
key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(resourceSetRanger, acls(ALL)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testMultipleResourcesWithDifferentBucketsAndDeepPathsInOneStatement() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:*\"\n" +
+        "      ],\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket/team/folder1/security/*\",\n" +
+        "        \"arn:aws:s3:::my-bucket2/team/folder2/misc/*\"\n" +
+        "      ]\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all key ACLs on prefix "team/folder1/security/" 
under
+    // my-bucket and all key ACLs on prefix "team/folder2/misc/" under 
my-bucket2; bucket READ; volume READ
+    final Set<IOzoneObj> keyPrefixSet = objSet(
+        prefix("my-bucket", "team/folder1/security/"), prefix("my-bucket2", 
"team/folder2/misc/"));
+    final Set<ACLType> keyAllAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, keyAllAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket"), bucket("my-bucket2")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: all key acls for resource type KEY with key name
+    // "team/folder1/security/*" under my-bucket and "team/folder2/misc/*" 
under my-bucket2; bucket READ; volume READ
+    final Set<IOzoneObj> rangerKeySet = objSet(
+        key("my-bucket", "team/folder1/security/*"), key("my-bucket2", 
"team/folder2/misc/*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, keyAllAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket"), bucket("my-bucket2")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testUnsupportedActionIgnoredWhenItIsTheOnlyAction() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:ReplicateObject\",\n" +         // unsupported 
action
+        "    \"Resource\": \"*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    assertThat(resolvedFromNativeAuthorizer.isEmpty());
+    assertThat(resolvedFromRangerAuthorizer.isEmpty());

Review Comment:
   updated



##########
hadoop-ozone/common/src/test/java/org/apache/hadoop/ozone/security/acl/iam/TestIamSessionPolicyResolver.java:
##########
@@ -903,22 +914,1013 @@ public void 
testCreatePathsAndPermissionsWithConditionPrefixesForBucketActionWhe
 
     final Set<IamSessionPolicyResolver.ResourceSpec> nativeResourceSpecs = 
Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket1", null, null));
-    final Set<AssumeRoleRequest.OzoneGrant> resultNative = 
createPathsAndPermissions(
-        VOLUME, NATIVE, actions, nativeResourceSpecs, prefixes);
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, nativeResourceSpecs, 
prefixes, objToAclsMapNative);
+    final Set<OzoneGrant> resultNative = 
groupObjectsByAcls(objToAclsMapNative);
     assertThat(resultNative).containsExactlyInAnyOrder(
-        new AssumeRoleRequest.OzoneGrant(readObject, acls(READ)),
-        new AssumeRoleRequest.OzoneGrant(readAndReadAclObject, acls(READ, 
READ_ACL)));
+        new OzoneGrant(readObject, acls(READ)), new 
OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
 
     final Set<IamSessionPolicyResolver.ResourceSpec> rangerResourceSpecs = 
Collections.singleton(
         new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket1", null, null));
-    final Set<AssumeRoleRequest.OzoneGrant> resultRanger = 
createPathsAndPermissions(
-        VOLUME, RANGER, actions, rangerResourceSpecs, prefixes);
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapRanger = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, RANGER, actions, rangerResourceSpecs, 
prefixes, objToAclsMapRanger);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).containsExactlyInAnyOrder(
+        new OzoneGrant(readObject, acls(READ)), new 
OzoneGrant(readAndReadAclObject, acls(READ, READ_ACL)));
+  }
+
+  @Test
+  public void testCreatePathsAndPermissionsWithNoMappedActions() {
+    final Set<S3Action> actions = emptySet();
+
+    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);
+    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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).isEmpty();
+  }
+
+  @Test
+  public void testCreatePathsAndPermissionsWithNoMappedResources() {
+    final Set<S3Action> actions = Collections.singleton(S3Action.GET_OBJECT);
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = 
emptySet();
+
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, 
emptySet(), 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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).isEmpty();
+  }
+
+  @Test
+  public void 
testCreatePathsAndPermissionsDeduplicatesAcrossSameResourceTypes() {
+    final Set<S3Action> actions = Stream.of(
+        S3Action.GET_OBJECT, S3Action.GET_OBJECT_TAGGING, 
S3Action.DELETE_OBJECT, S3Action.DELETE_OBJECT_TAGGING)
+        .collect(Collectors.toSet());
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = 
Collections.singleton(
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_EXACT, 
"bucket1", null, "key.txt"));
+    final Set<IOzoneObj> readAndDeleteObject = objSet(key("bucket1", 
"key.txt"));
+    final Set<IOzoneObj> readObjects = objSet(bucket("bucket1"), volume());
+
+    final Map<IOzoneObj, Set<ACLType>> objToAclsMapNative = new 
LinkedHashMap<>();
+    createPathsAndPermissions(VOLUME, NATIVE, actions, resourceSpecs, 
emptySet(), 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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
     assertThat(resultRanger).containsExactlyInAnyOrder(
-        new AssumeRoleRequest.OzoneGrant(readObject, acls(READ)),
-        new AssumeRoleRequest.OzoneGrant(readAndReadAclObject, acls(READ, 
READ_ACL)));
+        new OzoneGrant(readAndDeleteObject, acls(READ, DELETE)), new 
OzoneGrant(readObjects, acls(READ)));
+  }
+
+  @Test
+  public void 
testCreatePathsAndPermissionsWithAllS3ActionsOverridesAnyOtherAction() {
+    final Set<S3Action> actions = Stream.of(
+        S3Action.ALL_S3, S3Action.GET_OBJECT, S3Action.DELETE_OBJECT, 
S3Action.LIST_BUCKET)
+        .collect(Collectors.toSet());
+    final Set<IamSessionPolicyResolver.ResourceSpec> resourceSpecs = Stream.of(
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.OBJECT_EXACT, 
"bucket1", null, "key.txt"),
+        new IamSessionPolicyResolver.ResourceSpec(S3ResourceType.BUCKET, 
"bucket2", null, null))
+        .collect(Collectors.toSet());
+    final Set<IOzoneObj> allObjects = objSet(key("bucket1", "key.txt"), 
bucket("bucket2"));
+
+    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);
+    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);
+    final Set<OzoneGrant> resultRanger = 
groupObjectsByAcls(objToAclsMapRanger);
+    assertThat(resultRanger).containsExactlyInAnyOrder(
+        new OzoneGrant(allObjects, acls(ALL)), new 
OzoneGrant(rangerReadObjects, acls(READ)));
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsWhenSameStatementsArePresent() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
key "*"  READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsForSameActionsButDifferentResource() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket2\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"), 
bucket("my-bucket2"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(
+        objSet(volume(), prefix("my-bucket2", ""), prefix("my-bucket", "")), 
acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume and 
key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(
+        objSet(volume(), key("my-bucket2", "*"), key("my-bucket", "*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testDeduplicatesAcrossMultipleStatementsForDifferentActionsButSameResource() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:CreateBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL, CREATE; 
volume, prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL, 
CREATE);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL, CREATE; 
volume, key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testDeduplicatesAcrossMultipleStatementsWhenAllActionPresent() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": \"s3:*\",\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket ALL (instead of individual actions); volume 
and prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket ALL (instead of individual actions); volume 
and key "*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllowGetPutOnKey() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": [\"s3:GetObject\", \"s3:PutObject\"],\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket/folder/file.txt\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedFromBothAuthorizers = new 
LinkedHashSet<>();
+    // Expected: READ, CREATE, WRITE on key; bucket READ; volume READ
+    final Set<IOzoneObj> keySet = objSet(key("my-bucket", "folder/file.txt"));
+    final Set<ACLType> keyAcls = acls(READ, CREATE, WRITE);
+    expectedResolvedFromBothAuthorizers.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    expectedResolvedFromBothAuthorizers.add(new OzoneGrant(keySet, keyAcls));
+
+    
assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedFromBothAuthorizers);
+    
assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedFromBothAuthorizers);
+  }
+
+  @Test
+  public void testAllActionsForKey() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket/*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all key ACLs on prefix "" under bucket; bucket 
READ, volume READ
+    final Set<IOzoneObj> keyPrefixSet = objSet(prefix("my-bucket", ""));
+    final Set<ACLType> allKeyAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, allKeyAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    // Expected for Ranger: all key acls for resource type KEY with key name 
"*"
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    final Set<IOzoneObj> rangerKeySet = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, allKeyAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllActionsForBucket() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all Bucket ACLs for bucket; volume, prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> allBucketAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
prefix("my-bucket", "")), acls(READ)));
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, allBucketAcls));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    // Expected for Ranger: all Bucket ACLs for bucket; volume, key "*" READ
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
key("my-bucket", "*")), acls(READ)));
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, allBucketAcls));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMultipleResourcesInSeparateStatements() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:PutBucketAcl\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +
+        "    },\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": \"s3:*\",\n" +
+        "      \"Resource\": \"arn:aws:s3:::my-bucket/*\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, LIST, READ_ACL, WRITE_ACL; volume READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    // Expected for native: all key ACLs on prefix "" under bucket
+    final Set<IOzoneObj> keyPrefixSet = objSet(prefix("my-bucket", ""));
+    final Set<ACLType> keyAllAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, keyAllAcls));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, LIST, READ_ACL, WRITE_ACL; volume READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    // Expected for Ranger: all key acls for resource type KEY with key name 
"*"
+    final Set<IOzoneObj> rangerKeySet = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, keyAllAcls));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMultipleResourcesInOneStatement() throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:*\"\n" +
+        "      ],\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +
+        "      ]\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all for bucket and key acls; volume READ
+    final Set<IOzoneObj> resourceSetNative = objSet(bucket("my-bucket"), 
prefix("my-bucket", ""));
+    expectedResolvedNative.add(new OzoneGrant(resourceSetNative, acls(ALL)));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: all for bucket and key acls; volume READ
+    final Set<IOzoneObj> resourceSetRanger = objSet(bucket("my-bucket"), 
key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(resourceSetRanger, acls(ALL)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void 
testMultipleResourcesWithDifferentBucketsAndDeepPathsInOneStatement() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:*\"\n" +
+        "      ],\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket/team/folder1/security/*\",\n" +
+        "        \"arn:aws:s3:::my-bucket2/team/folder2/misc/*\"\n" +
+        "      ]\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: all key ACLs on prefix "team/folder1/security/" 
under
+    // my-bucket and all key ACLs on prefix "team/folder2/misc/" under 
my-bucket2; bucket READ; volume READ
+    final Set<IOzoneObj> keyPrefixSet = objSet(
+        prefix("my-bucket", "team/folder1/security/"), prefix("my-bucket2", 
"team/folder2/misc/"));
+    final Set<ACLType> keyAllAcls = acls(ALL);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, keyAllAcls));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket"), bucket("my-bucket2")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: all key acls for resource type KEY with key name
+    // "team/folder1/security/*" under my-bucket and "team/folder2/misc/*" 
under my-bucket2; bucket READ; volume READ
+    final Set<IOzoneObj> rangerKeySet = objSet(
+        key("my-bucket", "team/folder1/security/*"), key("my-bucket2", 
"team/folder2/misc/*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, keyAllAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), 
bucket("my-bucket"), bucket("my-bucket2")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testUnsupportedActionIgnoredWhenItIsTheOnlyAction() throws 
OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:ReplicateObject\",\n" +         // unsupported 
action
+        "    \"Resource\": \"*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    assertThat(resolvedFromNativeAuthorizer.isEmpty());
+    assertThat(resolvedFromRangerAuthorizer.isEmpty());
+  }
+
+  @Test
+  public void testUnsupportedResourceArnThrows() {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:ListBucket\",\n" +
+        "    \"Resource\": 
\"arn:aws:dynamodb:us-east-2:123456789012:table/example-table\"\n" +
+        "  }]\n" +
+        "}";
+
+    expectResolveThrowsForBothAuthorizers(
+        json, "Unsupported Resource Arn - " +
+        "arn:aws:dynamodb:us-east-2:123456789012:table/example-table", 
NOT_SUPPORTED_OPERATION);
+  }
+
+  @Test
+  public void testListBucketWithWildcard() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:ListBucket\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::proj-*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ and LIST on wildcard pattern; volume 
and key "*" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("proj-*"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST);
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), key("proj-*", 
"*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testListBucketOperationsWithNoPrefixes() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:ListBucket\",\n" +
+        "        \"s3:ListBucketMultipartUploads\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::proj\"\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ and LIST; volume, prefix "" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("proj"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST);
+    final Set<IOzoneObj> nativeReadObjects = objSet(volume(), prefix("proj", 
""));
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(nativeReadObjects, acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    // Expected for Ranger: bucket READ and LIST; volume, key "*" READ
+    final Set<IOzoneObj> rangerReadObjects = objSet(volume(), key("proj", 
"*"));
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(rangerReadObjects, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testIgnoresUnsupportedActionsWhenSupportedActionsAreIncluded() 
throws OMException {
+    final String json = "{\n" +
+        "  \"Version\": \"2012-10-17\",\n" +
+        "  \"Statement\": [\n" +
+        "    {\n" +
+        "      \"Sid\": \"AllowListingOfDataLakeFolder\",\n" +
+        "      \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:GetAccelerateConfiguration\",\n" +    // unsupported 
action
+        "        \"s3:GetBucketAcl\",\n" +
+        "        \"s3:GetObject\",\n" +                     // object-level 
action not applied for bucket
+        "        \"s3:GetObjectAcl\",\n" +                  // unsupported 
action
+        "        \"s3:ListBucket\",\n" +
+        "        \"s3:ListBucketMultipartUploads\"\n" +
+        "      ],\n" +
+        "      \"Resource\": \"arn:aws:s3:::bucket1\",\n" +
+        "      \"Condition\": {\n" +
+        "        \"StringEquals\": {\n" +
+        "          \"s3:prefix\": [ \"team/folder\", \"team/folder/*\" ]\n" +
+        "        }\n" +
+        "      }\n" +
+        "    }\n" +
+        "  ]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+
+    // Expected for native: READ, LIST, READ_ACL bucket acls; volume and 
prefixes "team/folder", "team/folder/" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("bucket1"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST, READ_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedNative.add(new OzoneGrant(
+        objSet(volume(), prefix("bucket1", "team/folder"), prefix("bucket1", 
"team/folder/")), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ, LIST, READ_ACL bucket acls; volume and keys 
"team/folder" and "team/folder/*" READ
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(
+        objSet(volume(), key("bucket1", "team/folder"), key("bucket1", 
"team/folder/*")), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMultiplePrefixesWithWildcards() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:GetObject\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::logs/*\",\n" +
+        "    \"Condition\": { \"StringEquals\": { \"s3:prefix\": [\"a/*\", 
\"b/*\"] } }\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: READ acl on prefix "" (condition prefixes are 
ignored); bucket READ; volume READ;
+    final Set<IOzoneObj> readObjectsNative = objSet(prefix("logs", ""), 
bucket("logs"), volume());
+    expectedResolvedNative.add(new OzoneGrant(readObjectsNative, acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ acl on key "*" (condition prefixes are 
ignored)
+    final Set<IOzoneObj> keySet = objSet(key("logs", "*"), bucket("logs"), 
volume());
+    expectedResolvedRanger.add(new OzoneGrant(keySet, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testObjectResourceWithWildcardInMiddle() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:GetObject\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::logs/file*.log\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards in middle of object resource are not supported for Native 
authorizer
+    expectResolveThrows(
+        json, NATIVE, "Wildcard prefix patterns are not supported for Ozone 
native " +
+        "authorizer if wildcard is not at the end", NOT_SUPPORTED_OPERATION);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ acl on key "file*.log", bucket READ, volume 
READ
+    final Set<IOzoneObj> readObjectsRanger = objSet(key("logs", "file*.log"), 
bucket("logs"), volume());
+    expectedResolvedRanger.add(new OzoneGrant(readObjectsRanger, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testObjectResourceWithPrefixWildcard() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:GetObject\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::myBucket/file*\"\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: READ acl on prefix "file" under bucket, bucket 
READ, volume READ
+    final Set<IOzoneObj> readObjectsNative = objSet(prefix("myBucket", 
"file"), bucket("myBucket"), volume());
+    expectedResolvedNative.add(new OzoneGrant(readObjectsNative, acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ acl on key "file*", bucket READ, volume READ
+    final Set<IOzoneObj> readObjectsRanger = objSet(key("myBucket", "file*"), 
bucket("myBucket"), volume());
+    expectedResolvedRanger.add(new OzoneGrant(readObjectsRanger, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testBucketActionOnAllResources() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "      \"Action\": [\n" +
+        "        \"s3:ListAllMyBuckets\",\n" +
+        "        \"s3:ListBucket\"\n" +
+        "      ],\n" +
+        "    \"Resource\": \"*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ and LIST on volume and bucket (wildcard), 
READ on key "*"
+    final Set<IOzoneObj> resourceSet = objSet(volume(), bucket("*"));
+    expectedResolvedRanger.add(new OzoneGrant(resourceSet, acls(READ, LIST)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(key("*", "*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testObjectActionOnAllResources() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:PutObject\",\n" +
+        "    \"Resource\": \"*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: CREATE and WRITE key acls on wildcard pattern, 
bucket READ, volume READ
+    final Set<IOzoneObj> keySet = objSet(key("*", "*"));
+    final Set<ACLType> keyAcls = acls(CREATE, WRITE);
+    expectedResolvedRanger.add(new OzoneGrant(keySet, keyAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), bucket("*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllActionsOnAllResources() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ, LIST acl on volume, ALL acl bucket 
(wildcard) and key (wildcard)
+    final Set<IOzoneObj> resourceSet = objSet(bucket("*"), key("*", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(resourceSet, acls(ALL)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ, 
LIST)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllActionsOnAllBucketResources() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: ALL bucket acls on wildcard pattern, volume READ, 
key "*" READ
+    final Set<IOzoneObj> bucketSet = objSet(bucket("*"));
+    final Set<ACLType> bucketAcls = acls(ALL);
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), key("*", "*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testAllActionsOnAllObjectResources() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:*\",\n" +
+        "    \"Resource\": \"arn:aws:s3:::*/*\"\n" +
+        "  }]\n" +
+        "}";
+
+    // Wildcards on bucket are not supported for Native authorizer
+    expectBucketWildcardUnsupportedExceptionForNativeAuthorizer(json);
+
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: ALL key acls on wildcard pattern; bucket READ; 
volume READ
+    final Set<IOzoneObj> keySet = objSet(key("*", "*"));
+    final Set<ACLType> keyAcls = acls(ALL);
+    expectedResolvedRanger.add(new OzoneGrant(keySet, keyAcls));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume(), bucket("*")), 
acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testWildcardActionGroupGetStar() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:Get*\",\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +
+        "      ]\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, READ_ACL acls
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, READ_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    // Expected for native: READ acl on prefix "" under bucket; volume READ
+    final Set<IOzoneObj> readObjectsNative = objSet(prefix("my-bucket", ""), 
volume());
+    expectedResolvedNative.add(new OzoneGrant(readObjectsNative, acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, READ_ACL acls
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    // Expected for Ranger: READ key acl for resource type KEY with key name 
"*"; volume READ
+    final Set<IOzoneObj> readObjectsRanger = objSet(key("my-bucket", "*"), 
volume());
+    expectedResolvedRanger.add(new OzoneGrant(readObjectsRanger, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testWildcardActionGroupListStar() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:List*\",\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +  // 
ListMultipartUploadParts has READ effect on file/object resources
+        "      ]\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: READ, LIST bucket acls
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcls = acls(READ, LIST);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcls));
+    // Expected for native: READ acl on prefix "" under bucket; volume READ
+    final Set<IOzoneObj> readObjectsNative = objSet(prefix("my-bucket", ""), 
volume());
+    expectedResolvedNative.add(new OzoneGrant(readObjectsNative, acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: READ, LIST bucket acls
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcls));
+    // Expected for Ranger: READ key acl for resource type KEY with key name 
"*"; volume READ
+    final Set<IOzoneObj> readObjectsRanger = objSet(key("my-bucket", "*"), 
volume());
+    expectedResolvedRanger.add(new OzoneGrant(readObjectsRanger, acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testWildcardActionGroupPutStar() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:Put*\",\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +
+        "      ]\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: bucket READ, WRITE_ACL acl
+    final Set<IOzoneObj> bucketSet = objSet(bucket("my-bucket"));
+    final Set<ACLType> bucketAcl = acls(READ, WRITE_ACL);
+    expectedResolvedNative.add(new OzoneGrant(bucketSet, bucketAcl));
+    // Expected for native: CREATE, WRITE acls on prefix "" under bucket
+    final Set<IOzoneObj> keyPrefixSet = objSet(prefix("my-bucket", ""));
+    final Set<ACLType> keyAcls = acls(CREATE, WRITE);
+    expectedResolvedNative.add(new OzoneGrant(keyPrefixSet, keyAcls));
+    // Expected for native: volume READ
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: bucket READ, WRITE_ACL acl
+    expectedResolvedRanger.add(new OzoneGrant(bucketSet, bucketAcl));
+    // Expected for Ranger: CREATE, WRITE key acls for resource type KEY with 
key name "*"
+    final Set<IOzoneObj> rangerKeySet = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(rangerKeySet, keyAcls));
+    // Expected for Ranger: volume READ
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
   }
 
-  // TODO sts - add more createPathsAndPermissions tests in the next PR
+  @Test
+  public void testWildcardActionGroupDeleteStar() throws OMException {
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:Delete*\",\n" +
+        "      \"Resource\": [\n" +
+        "        \"arn:aws:s3:::my-bucket\",\n" +
+        "        \"arn:aws:s3:::my-bucket/*\"\n" +
+        "      ]\n" +
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    final Set<OzoneGrant> expectedResolvedNative = new LinkedHashSet<>();
+    // Expected for native: DELETE on prefix "" under bucket; bucket READ, 
DELETE; volume READ
+    final Set<IOzoneObj> resourceSetNative = objSet(prefix("my-bucket", ""));
+    expectedResolvedNative.add(new OzoneGrant(resourceSetNative, 
acls(DELETE)));
+    expectedResolvedNative.add(new OzoneGrant(objSet(bucket("my-bucket")), 
acls(READ, DELETE)));
+    expectedResolvedNative.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromNativeAuthorizer).isEqualTo(expectedResolvedNative);
+
+    final Set<OzoneGrant> expectedResolvedRanger = new LinkedHashSet<>();
+    // Expected for Ranger: DELETE on resource type KEY with key name "*"; 
bucket READ, DELETE; volume READ
+    final Set<IOzoneObj> resourceSetRanger = objSet(key("my-bucket", "*"));
+    expectedResolvedRanger.add(new OzoneGrant(resourceSetRanger, 
acls(DELETE)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(bucket("my-bucket")), 
acls(READ, DELETE)));
+    expectedResolvedRanger.add(new OzoneGrant(objSet(volume()), acls(READ)));
+    assertThat(resolvedFromRangerAuthorizer).isEqualTo(expectedResolvedRanger);
+  }
+
+  @Test
+  public void testMismatchedActionAndResourceReturnsEmpty() throws OMException 
{
+    final String json = "{\n" +
+        "  \"Statement\": [{\n" +
+        "    \"Effect\": \"Allow\",\n" +
+        "    \"Action\": \"s3:GetObject\",\n" +             // object-level 
action
+        "    \"Resource\": \"arn:aws:s3:::my-bucket\"\n" +  // bucket-level 
resource
+        "  }]\n" +
+        "}";
+
+    final Set<OzoneGrant> resolvedFromNativeAuthorizer = resolve(json, VOLUME, 
NATIVE);
+    final Set<OzoneGrant> resolvedFromRangerAuthorizer = resolve(json, VOLUME, 
RANGER);
+
+    // Ensure what we got is what we expected
+    assertThat(resolvedFromNativeAuthorizer.isEmpty());
+    assertThat(resolvedFromRangerAuthorizer.isEmpty());

Review Comment:
   updated



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to