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

yzheng pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new c2785bd61 Remove KMS policies when KMS is not configured and improved 
default KMS permission for RO/RW (#3493)
c2785bd61 is described below

commit c2785bd61732d2df16e8d32e49017710965fff6d
Author: Yong Zheng <[email protected]>
AuthorDate: Fri Jan 23 20:33:17 2026 -0600

    Remove KMS policies when KMS is not configured and improved default KMS 
permission for RO/RW (#3493)
---
 CHANGELOG.md                                       |  6 ++-
 .../aws/AwsCredentialsStorageIntegration.java      | 36 ++++++++-----
 .../aws/AwsCredentialsStorageIntegrationTest.java  | 62 +++++++++++++++++-----
 3 files changed, 76 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1eaec3be4..991155bff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -53,8 +53,10 @@ request adding CHANGELOG notes for breaking (!) changes and 
possibly other secti
 
 - The `gcpServiceAccount` configuration value now affects Polaris behavior 
(enables service account impersonation). This value was previously defined but 
unused. This change may affect existing deployments that have populated this 
property.
 - (Before/After)UpdateTableEvent is emitted for all table updates within a 
transaction.
-- Added KMS options to Polaris CLI
-- Changed from Poetry to UV for Python package management
+- Added KMS options to Polaris CLI.
+- Changed from Poetry to UV for Python package management.
+- Exclude KMS policies when KMS is not being used for S3.
+- Improved default KMS permission handling to better distinguish read-only and 
read-write access.
 
 ### Deprecations
 
diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java
index 8fce267af..265b227e0 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java
@@ -299,9 +299,16 @@ public class AwsCredentialsStorageIntegration
       String region,
       String accountId) {
 
-    IamStatement.Builder allowKms = buildBaseKmsStatement(canWrite);
     boolean hasCurrentKey = kmsKeyArn != null;
     boolean hasAllowedKeys = hasAllowedKmsKeys(allowedKmsKeys);
+    boolean isAwsS3 = region != null && accountId != null;
+
+    // Nothing to do if no keys are configured and not AWS S3
+    if (!hasCurrentKey && !hasAllowedKeys && !isAwsS3) {
+      return;
+    }
+
+    IamStatement.Builder allowKms = buildBaseKmsStatement(canWrite);
 
     if (hasCurrentKey) {
       addKmsKeyResource(kmsKeyArn, allowKms);
@@ -311,16 +318,16 @@ public class AwsCredentialsStorageIntegration
       addAllowedKmsKeyResources(allowedKmsKeys, allowKms);
     }
 
-    // Add KMS statement if we have any KMS key configuration
-    if (hasCurrentKey || hasAllowedKeys) {
+    // Only add wildcard KMS access for read-only operations on AWS S3 when no 
specific keys are
+    // configured. This does not apply to services like Minio where region and 
accountId are not
+    // available.
+    boolean shouldAddWildcard = !hasCurrentKey && !hasAllowedKeys && !canWrite 
&& isAwsS3;
+    if (shouldAddWildcard) {
+      addAllKeysResource(region, accountId, allowKms);
+    }
+
+    if (hasCurrentKey || hasAllowedKeys || shouldAddWildcard) {
       policyBuilder.addStatement(allowKms.build());
-    } else if (!canWrite) {
-      // Only add wildcard KMS access for read-only operations when no 
specific keys are configured
-      // this check is for minio because it doesn't have region or account id
-      if (region != null && accountId != null) {
-        addAllKeysResource(region, accountId, allowKms);
-        policyBuilder.addStatement(allowKms.build());
-      }
     }
   }
 
@@ -328,13 +335,14 @@ public class AwsCredentialsStorageIntegration
     IamStatement.Builder allowKms =
         IamStatement.builder()
             .effect(IamEffect.ALLOW)
-            .addAction("kms:GenerateDataKeyWithoutPlaintext")
             .addAction("kms:DescribeKey")
-            .addAction("kms:Decrypt")
-            .addAction("kms:GenerateDataKey");
+            .addAction("kms:Decrypt");
 
     if (canEncrypt) {
-      allowKms.addAction("kms:Encrypt");
+      allowKms
+          .addAction("kms:Encrypt")
+          .addAction("kms:GenerateDataKey")
+          .addAction("kms:GenerateDataKeyWithoutPlaintext");
     }
 
     return allowKms;
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
index 082eed3ec..16e0893f6 100644
--- 
a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
+++ 
b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java
@@ -468,11 +468,8 @@ class AwsCredentialsStorageIntegrationTest extends 
BaseStorageIntegrationTest {
                                         .returns(IamEffect.ALLOW, 
IamStatement::effect)
                                         .returns(
                                             List.of(
-                                                IamAction.create(
-                                                    
"kms:GenerateDataKeyWithoutPlaintext"),
                                                 
IamAction.create("kms:DescribeKey"),
-                                                
IamAction.create("kms:Decrypt"),
-                                                
IamAction.create("kms:GenerateDataKey")),
+                                                
IamAction.create("kms:Decrypt")),
                                             IamStatement::actions)
                                         .returns(
                                             List.of(
@@ -583,11 +580,8 @@ class AwsCredentialsStorageIntegrationTest extends 
BaseStorageIntegrationTest {
                                         .returns(IamEffect.ALLOW, 
IamStatement::effect)
                                         .returns(
                                             List.of(
-                                                IamAction.create(
-                                                    
"kms:GenerateDataKeyWithoutPlaintext"),
                                                 
IamAction.create("kms:DescribeKey"),
-                                                
IamAction.create("kms:Decrypt"),
-                                                
IamAction.create("kms:GenerateDataKey")),
+                                                
IamAction.create("kms:Decrypt")),
                                             IamStatement::actions)
                                         .returns(
                                             List.of(
@@ -824,11 +818,14 @@ class AwsCredentialsStorageIntegrationTest extends 
BaseStorageIntegrationTest {
                         assertThat(stmt.actions())
                             .containsAll(
                                 List.of(
-                                    
IamAction.create("kms:GenerateDataKeyWithoutPlaintext"),
                                     IamAction.create("kms:DescribeKey"),
-                                    IamAction.create("kms:Decrypt"),
-                                    IamAction.create("kms:GenerateDataKey")));
-                        
assertThat(stmt.actions()).doesNotContain(IamAction.create("kms:Encrypt"));
+                                    IamAction.create("kms:Decrypt")));
+                        assertThat(stmt.actions())
+                            .doesNotContainAnyElementsOf(
+                                List.of(
+                                    IamAction.create("kms:Encrypt"),
+                                    IamAction.create("kms:GenerateDataKey"),
+                                    
IamAction.create("kms:GenerateDataKeyWithoutPlaintext")));
                         assertThat(stmt.resources())
                             .containsExactlyInAnyOrder(
                                 IamResource.create(allowedKmsKeys.get(0)),
@@ -1473,4 +1470,45 @@ class AwsCredentialsStorageIntegrationTest extends 
BaseStorageIntegrationTest {
         
.isInstanceOf(software.amazon.awssdk.services.sts.model.StsException.class)
         .hasMessageContaining("sts:TagSession");
   }
+
+  @Test
+  public void testNoKmsForNonAwsReadOnly() {
+    StsClient stsClient = Mockito.mock(StsClient.class);
+    String roleARN = "arn:aws:iam::012345678901:role/jdoe";
+    String externalId = "externalId";
+    String bucket = "bucket";
+    String warehouseKeyPrefix = "path/to/warehouse";
+
+    // Test with no KMS keys and read-only for non-AWS (should not add any KMS 
statement)
+    Mockito.when(stsClient.assumeRole(Mockito.isA(AssumeRoleRequest.class)))
+        .thenAnswer(
+            invocation -> {
+              AssumeRoleRequest request = invocation.getArgument(0);
+              IamPolicy policy = IamPolicy.fromJson(request.policy());
+
+              // Verify no KMS statement exists
+              assertThat(policy.statements())
+                  .noneMatch(
+                      stmt ->
+                          stmt.actions().stream()
+                              .anyMatch(action -> 
action.value().startsWith("kms:")));
+              return ASSUME_ROLE_RESPONSE;
+            });
+
+    new AwsCredentialsStorageIntegration(
+            AwsStorageConfigurationInfo.builder()
+                .addAllowedLocation(s3Path(bucket, warehouseKeyPrefix))
+                .roleARN(roleARN)
+                .externalId(externalId)
+                .build(),
+            stsClient)
+        .getSubscopedCreds(
+            EMPTY_REALM_CONFIG,
+            true,
+            Set.of(s3Path(bucket, warehouseKeyPrefix + "/table")),
+            Set.of(),
+            POLARIS_PRINCIPAL,
+            Optional.empty(),
+            CredentialVendingContext.empty());
+  }
 }

Reply via email to