diff --git a/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerFactoryTest.java b/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerFactoryTest.java index f514205d893..a0bd42506b2 100644 --- a/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerFactoryTest.java +++ b/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerFactoryTest.java @@ -38,11 +38,13 @@ import java.time.Clock; import java.time.Duration; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import org.apache.polaris.core.auth.PolarisAuthorizableOperation; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; @@ -221,7 +223,8 @@ public void testFactoryPassesRealmToAuthorizerContext() throws Exception { OpaPolarisAuthorizer authorizer = (OpaPolarisAuthorizer) factory.create(mock(RealmConfig.class)); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("admin")); + PolarisPrincipal principal = + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("admin")); assertThatNoException() .isThrownBy( () -> @@ -277,7 +280,8 @@ public void testFactoryUsesDistinctRealmValues() throws Exception { OpaPolarisAuthorizer authorizer = (OpaPolarisAuthorizer) factory.create(mock(RealmConfig.class)); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("admin")); + PolarisPrincipal principal = + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("admin")); assertThatNoException() .isThrownBy( () -> @@ -337,7 +341,12 @@ public void testFactoryPassesResolvedRequestIdToAuthorizerContext() throws Excep OpaPolarisAuthorizer authorizer = (OpaPolarisAuthorizer) factory.create(realmConfig); PolarisPrincipal principal = - PolarisPrincipal.of("eve", Map.of("department", "finance"), Set.of("auditor")); + PolarisPrincipal.of( + "eve", + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "finance") + .build(), + Set.of("auditor")); PolarisResolvedPathWrapper target = new PolarisResolvedPathWrapper(List.of()); PolarisResolvedPathWrapper secondary = new PolarisResolvedPathWrapper(List.of()); @@ -401,7 +410,12 @@ public void testFactoryResolvesFreshRequestIdPerCreateCall() throws Exception { RealmConfig realmConfig = mock(RealmConfig.class); PolarisPrincipal principal = - PolarisPrincipal.of("eve", Map.of("department", "finance"), Set.of("auditor")); + PolarisPrincipal.of( + "eve", + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "finance") + .build(), + Set.of("auditor")); PolarisResolvedPathWrapper target = new PolarisResolvedPathWrapper(List.of()); PolarisResolvedPathWrapper secondary = new PolarisResolvedPathWrapper(List.of()); diff --git a/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerTest.java b/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerTest.java index bab45c2751a..639a57c4288 100644 --- a/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerTest.java +++ b/extensions/auth/opa/src/test/java/org/apache/polaris/extension/auth/opa/OpaPolarisAuthorizerTest.java @@ -37,7 +37,6 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.hc.client5.http.classic.methods.HttpPost; @@ -59,6 +58,9 @@ import org.apache.polaris.core.auth.PolarisSecurable; import org.apache.polaris.core.auth.RenameAuthorizationIntent; import org.apache.polaris.core.auth.SingleTargetAuthorizationIntent; +import org.apache.polaris.core.collection.AttributeMap; +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; @@ -96,7 +98,12 @@ void serializesBasicOpaInput() throws Exception { "test-realm"); PolarisPrincipal principal = - PolarisPrincipal.of("eve", Map.of("department", "finance"), Set.of("auditor")); + PolarisPrincipal.of( + "eve", + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "finance") + .build(), + Set.of("auditor")); Set entities = Set.of(); PolarisResolvedPathWrapper target = new PolarisResolvedPathWrapper(List.of()); @@ -153,7 +160,10 @@ void serializesHierarchicalTarget() throws Exception { PolarisPrincipal principal = PolarisPrincipal.of( "alice", - Map.of("department", "analytics", "level", "senior"), + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "analytics") + .put(new AttributeKey<>("level"), "senior") + .build(), Set.of("data_engineer", "analyst")); // Create a hierarchical resource structure: catalog.namespace.table @@ -293,7 +303,10 @@ void serializesMultiLevelNamespaceTarget() throws Exception { PolarisPrincipal principal = PolarisPrincipal.of( "bob", - Map.of("team", "ml", "project", "forecasting"), + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("team"), "ml") + .put(new AttributeKey<>("project"), "forecasting") + .build(), Set.of("data_scientist", "analyst")); // Create a multi-level namespace structure: catalog.department.team.table @@ -462,7 +475,8 @@ void authorizeOrThrowHandlesEmptyTargetsAndSecondaries() throws Exception { null, "test-realm"); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("admin")); + PolarisPrincipal principal = + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("admin")); Set entities = Set.of(); @@ -545,7 +559,7 @@ T httpClientExecute( }; PolarisPrincipal mockPrincipal = - PolarisPrincipal.of("test-user", Map.of(), Collections.emptySet()); + PolarisPrincipal.of("test-user", AttributeMap.EMPTY, Collections.emptySet()); PolarisAuthorizableOperation mockOperation = PolarisAuthorizableOperation.LOAD_TABLE; PolarisResolvedPathWrapper target = null; @@ -591,7 +605,7 @@ T httpClientExecute( // Create mock principal and entities PolarisPrincipal mockPrincipal = - PolarisPrincipal.of("test-user", Map.of(), Collections.emptySet()); + PolarisPrincipal.of("test-user", AttributeMap.EMPTY, Collections.emptySet()); PolarisAuthorizableOperation mockOperation = PolarisAuthorizableOperation.LOAD_TABLE; PolarisResolvedPathWrapper target = null; @@ -620,7 +634,7 @@ void resolveAuthorizationInputsResolvesAll() { "test-realm"); PolarisResolutionManifest resolutionManifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(resolutionManifest); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")); authorizer.resolveAuthorizationInputs(authzState, requestWithCatalogTarget(principal)); @@ -630,7 +644,7 @@ void resolveAuthorizationInputsResolvesAll() { @Test void authorizeUsesIntentInputsAndAllows() throws Exception { final String[] capturedRequestBody = new String[1]; - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")); AuthorizationRequest request = requestWithCatalogTarget(principal); HttpEntity mockEntity = HttpEntities.create("{\"result\":{\"allow\":true}}"); @SuppressWarnings("resource") @@ -680,7 +694,7 @@ T httpClientExecute( @Test void authorizeDeniesWhenOpaDenies() { - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")); AuthorizationRequest request = requestWithCatalogTarget(principal); HttpEntity mockEntity = HttpEntities.create("{\"result\":{\"allow\":false}}"); @SuppressWarnings("resource") @@ -722,7 +736,7 @@ void authorizeIncludesStructuredParentsFromSecurable() throws Exception { final String[] capturedRequestBody = new String[1]; AuthorizationRequest request = new AuthorizationRequest( - PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")), List.of( new SingleTargetAuthorizationIntent( PolarisAuthorizableOperation.LOAD_TABLE, @@ -820,7 +834,7 @@ void authorizeResolvedCatalogTargetPreservesRootParent() throws Exception { .isThrownBy( () -> authorizer.authorizeOrThrow( - PolarisPrincipal.of("root", Map.of(), Set.of("service_admin")), + PolarisPrincipal.of("root", AttributeMap.EMPTY, Set.of("service_admin")), Set.of(), PolarisAuthorizableOperation.GET_CATALOG, new PolarisResolvedPathWrapper( @@ -885,7 +899,7 @@ void authorizeRootScopedOperationSerializesRootTarget() throws Exception { .isThrownBy( () -> authorizer.authorizeOrThrow( - PolarisPrincipal.of("root", Map.of(), Set.of("service_admin")), + PolarisPrincipal.of("root", AttributeMap.EMPTY, Set.of("service_admin")), Set.of(), PolarisAuthorizableOperation.LIST_CATALOGS, new PolarisResolvedPathWrapper(List.of(createResolvedEntity(rootEntity))), @@ -922,7 +936,7 @@ void authorizeRenameIncludesTargetAndSecondaryPaths() throws Exception { AuthorizationRequest request = new AuthorizationRequest( - PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")), List.of( new RenameAuthorizationIntent( PolarisAuthorizableOperation.RENAME_TABLE, @@ -1000,7 +1014,7 @@ void authorizeSingleOperationMultiIntentRequestEvaluatesSequentially() throws Ex PolarisResolutionManifest resolutionManifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(resolutionManifest); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")); OpaPolarisAuthorizer authorizer = new OpaPolarisAuthorizer( @@ -1083,7 +1097,7 @@ void authorizeUpdateTableMultiIntentRequestEvaluatesSequentially() { PolarisResolutionManifest resolutionManifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(resolutionManifest); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role-1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role-1")); PolarisSecurable tableTarget = PolarisSecurable.of( new PathSegment(PolarisEntityType.CATALOG, "catalog-1"), @@ -1140,7 +1154,8 @@ void serializesInputWithRealm() throws Exception { null, "explicit-realm"); - PolarisPrincipal principal = PolarisPrincipal.of("eve", Map.of(), Set.of("auditor")); + PolarisPrincipal principal = + PolarisPrincipal.of("eve", AttributeMap.EMPTY, Set.of("auditor")); assertThatNoException() .isThrownBy( @@ -1183,7 +1198,8 @@ void serializesRealmInAuthorizePath() throws Exception { PolarisResolutionManifest resolutionManifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(resolutionManifest); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("admin")); + PolarisPrincipal principal = + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("admin")); AuthorizationRequest request = requestWithCatalogTarget(principal); assertThatNoException().isThrownBy(() -> authorizer.authorize(authzState, request)); @@ -1215,7 +1231,12 @@ void requestIdIsIncludedInContextWhenProvided() throws Exception { "test-realm"); PolarisPrincipal principal = - PolarisPrincipal.of("eve", Map.of("department", "finance"), Set.of("auditor")); + PolarisPrincipal.of( + "eve", + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "finance") + .build(), + Set.of("auditor")); PolarisResolvedPathWrapper target = new PolarisResolvedPathWrapper(List.of()); PolarisResolvedPathWrapper secondary = new PolarisResolvedPathWrapper(List.of()); @@ -1256,7 +1277,12 @@ void requestIdFallsBackToRandomUuidWhenNull() throws Exception { "test-realm"); PolarisPrincipal principal = - PolarisPrincipal.of("eve", Map.of("department", "finance"), Set.of("auditor")); + PolarisPrincipal.of( + "eve", + ImmutableAttributeMap.builder() + .put(new AttributeKey<>("department"), "finance") + .build(), + Set.of("auditor")); PolarisResolvedPathWrapper target = new PolarisResolvedPathWrapper(List.of()); PolarisResolvedPathWrapper secondary = new PolarisResolvedPathWrapper(List.of()); diff --git a/extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/utils/RangerUtils.java b/extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/utils/RangerUtils.java index 117e982d307..cd8db1eff9b 100644 --- a/extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/utils/RangerUtils.java +++ b/extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/utils/RangerUtils.java @@ -28,6 +28,7 @@ import org.apache.commons.lang3.StringUtils; import org.apache.polaris.core.auth.PolarisAuthorizableOperation; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.entity.PrincipalEntity; @@ -130,7 +131,8 @@ private static Map getResourceAttributes( private static Map getUserAttributes(PolarisPrincipal principal) { Map properties = principal - .getAttribute(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, PrincipalEntity.class) + .getAttributes() + .getOptional(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY) .map(PrincipalEntity::getInternalPropertiesAsMap) .orElse(Collections.emptyMap()); diff --git a/extensions/auth/ranger/src/test/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizerTest.java b/extensions/auth/ranger/src/test/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizerTest.java index d35fee899f4..af4a31cc4de 100644 --- a/extensions/auth/ranger/src/test/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizerTest.java +++ b/extensions/auth/ranger/src/test/java/org/apache/polaris/extension/auth/ranger/RangerPolarisAuthorizerTest.java @@ -40,6 +40,7 @@ import org.apache.polaris.core.auth.PolarisAuthorizableOperation; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; @@ -199,7 +200,7 @@ public PolarisPrincipal deserialize(JsonParser parser, DeserializationContext co String name = nameNode != null ? nameNode.asString() : null; - return PolarisPrincipal.of(name, Collections.emptyMap(), Collections.emptySet()); + return PolarisPrincipal.of(name, AttributeMap.EMPTY, Collections.emptySet()); } } diff --git a/extensions/events/kafka/src/test/java/org/apache/polaris/extensions/events/kafka/KafkaEventListenerTest.java b/extensions/events/kafka/src/test/java/org/apache/polaris/extensions/events/kafka/KafkaEventListenerTest.java index 81cb845bd24..207e3e1f947 100644 --- a/extensions/events/kafka/src/test/java/org/apache/polaris/extensions/events/kafka/KafkaEventListenerTest.java +++ b/extensions/events/kafka/src/test/java/org/apache/polaris/extensions/events/kafka/KafkaEventListenerTest.java @@ -42,6 +42,7 @@ import org.apache.kafka.common.serialization.StringDeserializer; import org.apache.kafka.common.serialization.UUIDDeserializer; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.service.events.EventAttributeMap; import org.apache.polaris.service.events.EventAttributes; import org.apache.polaris.service.events.ImmutablePolarisEventMetadata; @@ -77,7 +78,7 @@ class KafkaEventListenerTest { private static final String KAFKA_GROUP_ID = "polaris-group-id"; private static final String TEST_CATALOG = "test_catalog"; private static final PolarisPrincipal PRINCIPAL = - PolarisPrincipal.of(TEST_USER, Map.of(), Set.of("role1", "role2")); + PolarisPrincipal.of(TEST_USER, AttributeMap.EMPTY, Set.of("role1", "role2")); private static final TableIdentifier TEST_TABLE_IDENTIFIER = TableIdentifier.of("test_namespace", "test_table"); private static final JsonMapper OBJECT_MAPPER = JsonMapper.shared(); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationPreConditions.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationPreConditions.java index 452dee68be8..a9d6397b0ee 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationPreConditions.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationPreConditions.java @@ -18,6 +18,7 @@ */ package org.apache.polaris.core.auth; +import java.util.Optional; import org.apache.iceberg.exceptions.ForbiddenException; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; @@ -57,8 +58,10 @@ && mustRotateCredentials(polarisPrincipal)) { } private static boolean mustRotateCredentials(PolarisPrincipal principal) { - return principal - .getAttribute(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, PrincipalEntity.class) + return Optional.ofNullable( + principal + .getAttributes() + .get(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY)) .map(PrincipalEntity::getInternalPropertiesAsMap) .map( map -> diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipal.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipal.java index bfae04857d7..671b2b0603f 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipal.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipal.java @@ -19,53 +19,14 @@ package org.apache.polaris.core.auth; import java.security.Principal; -import java.util.Map; -import java.util.Optional; import java.util.Set; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.immutables.PolarisImmutable; -import org.immutables.value.Value; /** Represents a {@link Principal} in the Polaris system. */ @PolarisImmutable public interface PolarisPrincipal extends Principal { - /** - * Attribute key for the principal entity attribute, of type {@link - * org.apache.polaris.core.entity.PrincipalEntity}. - * - *

Note: callers must never assume that this attribute is present. - */ - String PRINCIPAL_ENTITY_ATTRIBUTE_KEY = "org.apache.polaris.core.auth.PRINCIPAL_ENTITY"; - - /** - * Attribute key, of type {@link Boolean}, used to determine how authorizers should resolve - * principal roles. - * - *

When absent or false, authorizers should resolve only the roles returned by {@link - * #getRoles()}; if an empty set is returned, then no principal roles should be resolved. - * - *

When present and true, authorizers should resolve all roles granted to the principal at the - * time of check, ignoring roles returned by {@link #getRoles()} (even if an empty set is - * returned). - * - *

This attribute is generally present and true when the principal presented a credential - * including the pseudo-role {@code PRINCIPAL_ROLE:ALL}. - * - *

Note: Callers must not assume that this attribute is always present. - */ - String PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY = "org.apache.polaris.core.auth.PRINCIPAL_ROLE:ALL"; - - /** - * Attribute key used to store or retrieve a JSON Web Token (JWT) associated with the {@link - * PolarisPrincipal}, of type {@link String}. - * - *

The associated value is expected to represent the JWT provided during authentication and may - * be used for further validation or for deriving additional claims. - * - *

Note: callers must never assume that this attribute is present. - */ - String JWT_ATTRIBUTE_KEY = "org.apache.polaris.core.auth.JWT"; - /** * Creates a new instance of {@link PolarisPrincipal} with the specified name, roles, and * attributes. @@ -74,23 +35,16 @@ public interface PolarisPrincipal extends Principal { * @param attributes the attributes of the principal * @param roles the set of roles associated with the principal */ - static PolarisPrincipal of(String name, Map attributes, Set roles) { + static PolarisPrincipal of(String name, AttributeMap attributes, Set roles) { return ImmutablePolarisPrincipal.builder() .name(name) - .attributes(attributes) + .attributes(AttributeMap.copyOf(attributes)) .roles(roles) .build(); } /** Returns the principal attributes. */ - @Value.Redacted - Map getAttributes(); - - /** Returns the attribute value associated with the given key, if any. */ - default Optional getAttribute(String key, Class type) { - Object value = getAttributes().get(key); - return type.isInstance(value) ? Optional.of(type.cast(value)) : Optional.empty(); - } + AttributeMap getAttributes(); /** * Returns the set of activated principal role names. diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipalAttributes.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipalAttributes.java new file mode 100644 index 00000000000..c2cdf263630 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipalAttributes.java @@ -0,0 +1,68 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.polaris.core.auth; + +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.entity.PrincipalEntity; + +public final class PolarisPrincipalAttributes { + + private PolarisPrincipalAttributes() {} + + /** + * Attribute key for the principal entity attribute, of type {@link PrincipalEntity}. + * + *

Note: callers must never assume that this attribute is present. + */ + public static final AttributeKey PRINCIPAL_ENTITY_ATTRIBUTE_KEY = + new AttributeKey<>("org.apache.polaris.core.auth.PRINCIPAL_ENTITY"); + + /** + * Attribute key, of type {@link Boolean}, used to determine how authorizers should resolve + * principal roles. + * + *

When absent or false, authorizers should resolve only the roles returned by {@link + * PolarisPrincipal#getRoles()}; if an empty set is returned, then no principal roles should be + * resolved. + * + *

When present and true, authorizers should resolve all roles granted to the principal at the + * time of check, ignoring roles returned by {@link PolarisPrincipal#getRoles()} (even if an empty + * set is returned). + * + *

This attribute is generally present and true when the principal presented a credential + * including the pseudo-role {@code PRINCIPAL_ROLE:ALL}. + * + *

Note: Callers must not assume that this attribute is always present. + */ + public static final AttributeKey PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY = + new AttributeKey<>("org.apache.polaris.core.auth.PRINCIPAL_ROLE:ALL"); + + /** + * Attribute key used to store or retrieve a JSON Web Token (JWT) associated with the {@link + * PolarisPrincipal}, of type {@link String}. + * + *

The associated value is expected to represent the JWT provided during authentication and may + * be used for further validation or for deriving additional claims. + * + *

Note: callers must never assume that this attribute is present. + */ + public static final AttributeKey JWT_ATTRIBUTE_KEY = + new AttributeKey<>("org.apache.polaris.core.auth.JWT"); +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/Resolver.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/Resolver.java index c120eec8338..87476f135e0 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/Resolver.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/Resolver.java @@ -31,6 +31,7 @@ import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisChangeTrackingVersions; @@ -793,7 +794,8 @@ private ResolverStatus resolveCallerPrincipalAndPrincipalRoles( // activate all principal roles specified in the authenticated principal if (resolvePrincipalRoles) { if (this.polarisPrincipal - .getAttribute(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, Boolean.class) + .getAttributes() + .getOptional(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY) .orElse(false)) { resolvedCallerPrincipalRoles = resolveAllPrincipalRoles(toValidate, resolvedCallerPrincipal); diff --git a/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationPreConditionsTest.java b/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationPreConditionsTest.java index f6bc2bfaab1..0ffda57fd43 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationPreConditionsTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationPreConditionsTest.java @@ -23,9 +23,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.Map; import java.util.Set; import org.apache.iceberg.exceptions.ForbiddenException; +import org.apache.polaris.core.collection.AttributeMap; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.entity.PrincipalEntity; @@ -36,12 +37,14 @@ public class AuthorizationPreConditionsTest { private static final PolarisPrincipal PRINCIPAL = PolarisPrincipal.of( "alice", - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - new PrincipalEntity.Builder() - .setName("alice") - .setCredentialRotationRequiredState() - .build()), + ImmutableAttributeMap.builder() + .put( + PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, + new PrincipalEntity.Builder() + .setName("alice") + .setCredentialRotationRequiredState() + .build()) + .build(), Set.of("role")); private static RealmConfig realmConfigWithEnforcement(boolean enforce) { @@ -88,7 +91,7 @@ public void testFlagOn_nonRotationOp_propertySet_throwsForbidden() { @Test public void testFlagOn_nonRotationOp_propertyNotSet_noException() { - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); assertThatCode( () -> diff --git a/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationRequestTest.java b/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationRequestTest.java index 60c6bf9e42e..408489a3c79 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationRequestTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/auth/AuthorizationRequestTest.java @@ -21,8 +21,8 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; import java.util.List; -import java.util.Map; import java.util.Set; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.entity.PolarisEntityType; import org.junit.jupiter.api.Test; @@ -43,7 +43,7 @@ void requestRequiresAtLeastOneIntent() { assertThatThrownBy( () -> new AuthorizationRequest( - PolarisPrincipal.of("alice", Map.of(), Set.of("role")), List.of())) + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")), List.of())) .isInstanceOf(IllegalArgumentException.class) .hasMessageContaining("must contain at least one intent"); } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java b/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java index 1b847d6df53..6288771199a 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisAuthorizerImplTest.java @@ -33,9 +33,9 @@ import java.util.EnumSet; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.iceberg.exceptions.ForbiddenException; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.entity.PolarisEntity; @@ -70,7 +70,7 @@ void resolveAuthorizationInputsResolvesAll() { PolarisAuthorizerImpl authorizer = new PolarisAuthorizerImpl(mock(RealmConfig.class)); PolarisResolutionManifest manifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(manifest); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); AuthorizationRequest request = new AuthorizationRequest( principal, @@ -93,7 +93,7 @@ void authorizeUsesRootTargetForRootGrantRequestWithoutPrimaryTarget() { AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper rootWrapper = mock(PolarisResolvedPathWrapper.class); PolarisResolvedPathWrapper principalRoleWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedRootContainerEntityAsPath()).thenReturn(rootWrapper); when(manifest.getResolvedTopLevelEntity("analytics-admin", PolarisEntityType.PRINCIPAL_ROLE)) @@ -137,7 +137,7 @@ void authorizeUsesRootTargetForListCatalogsRequestWithoutPrimaryTarget() { PolarisResolutionManifest manifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper rootWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedRootContainerEntityAsPath()).thenReturn(rootWrapper); when(manifest.getAllActivatedCatalogRoleAndPrincipalRoles()).thenReturn(Set.of()); @@ -175,7 +175,7 @@ void authorizeResolvesNamespaceTargetUsingCatalog() { PolarisResolutionManifest manifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper namespaceWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedPath( ResolvedPathKey.of(List.of("ns"), PolarisEntityType.NAMESPACE), true)) @@ -221,7 +221,7 @@ void authorizeSingleOperationMultiIntentRequestEvaluatesSequentially() { AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper firstCatalogWrapper = mock(PolarisResolvedPathWrapper.class); PolarisResolvedPathWrapper secondCatalogWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedTopLevelEntity("catalog1", PolarisEntityType.CATALOG)) .thenReturn(firstCatalogWrapper); @@ -275,7 +275,7 @@ void authorizeUpdateTableMultiIntentRequestEvaluatesSequentially() { PolarisResolutionManifest manifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper tableWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedPath( ResolvedPathKey.of(List.of("ns", "table"), PolarisEntityType.TABLE_LIKE), true)) @@ -326,7 +326,7 @@ void authorizeUpdateTableMultiIntentRequestEvaluatesSequentially() { @Test void authorizationRequestThrowsWhenIntentsAreEmpty() { - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); org.assertj.core.api.Assertions.assertThatThrownBy( () -> new AuthorizationRequest(principal, List.of())) @@ -340,7 +340,7 @@ void authorizeReturnsDenyDecision() { PolarisResolutionManifest manifest = mock(PolarisResolutionManifest.class); AuthorizationState authzState = new AuthorizationState(manifest); PolarisResolvedPathWrapper catalogWrapper = mock(PolarisResolvedPathWrapper.class); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role")); when(manifest.getResolvedTopLevelEntity("catalog", PolarisEntityType.CATALOG)) .thenReturn(catalogWrapper); @@ -378,7 +378,7 @@ void authorizeOrThrowLogsMissingPrivilegeDetailsServerSide() { assertThatThrownBy( () -> authorizer.authorizeOrThrow( - PolarisPrincipal.of("alice", Map.of(), Set.of("reader")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("reader")), Set.of(), PolarisAuthorizableOperation.CREATE_TABLE_DIRECT, List.of(namespace), @@ -405,7 +405,7 @@ void authorizeOrThrowLogsAllMissingTargetPrivilegesWithoutShortCircuit() { assertThatThrownBy( () -> authorizer.authorizeOrThrow( - PolarisPrincipal.of("alice", Map.of(), Set.of("reader")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("reader")), Set.of(), PolarisAuthorizableOperation.CREATE_TABLE_DIRECT_WITH_WRITE_DELEGATION, List.of(namespace), @@ -432,7 +432,7 @@ void authorizeOrThrowLogsSecondaryMissingPrivilegeServerSide() { assertThatThrownBy( () -> authorizer.authorizeOrThrow( - PolarisPrincipal.of("alice", Map.of(), Set.of("reader")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("reader")), Set.of(), PolarisAuthorizableOperation.RENAME_TABLE, List.of(srcTable), @@ -467,7 +467,7 @@ void findMissingPrivilegesReturnsEmptyWhenNothingRequiredFails() { List missing = authorizer.findMissingPrivileges( - PolarisPrincipal.of("alice", Map.of(), Set.of("reader")), + PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("reader")), Set.of(), PolarisAuthorizableOperation.CREATE_TABLE_DIRECT, List.of(namespace), diff --git a/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisPrincipalTest.java b/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisPrincipalTest.java new file mode 100644 index 00000000000..8789d007acd --- /dev/null +++ b/polaris-core/src/test/java/org/apache/polaris/core/auth/PolarisPrincipalTest.java @@ -0,0 +1,44 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.polaris.core.auth; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +import java.util.Set; +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.collection.MutableAttributeMap; +import org.junit.jupiter.api.Test; + +class PolarisPrincipalTest { + + @Test + void attributesAreDefensivelyCopied() { + AttributeKey key = new AttributeKey<>("key"); + MutableAttributeMap attributes = MutableAttributeMap.builder().put(key, "value1").build(); + + PolarisPrincipal principal = PolarisPrincipal.of("alice", attributes, Set.of("role")); + attributes.put(key, "value2"); + + assertThat(principal.getAttributes().get(key)).isEqualTo("value1"); + assertThatThrownBy(() -> principal.getAttributes().put(key, "value2")) + .isInstanceOf(UnsupportedOperationException.class); + } +} diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisResolutionManifestLookupKeyTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisResolutionManifestLookupKeyTest.java index c880d9e4dfa..4a21fe9b64d 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisResolutionManifestLookupKeyTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisResolutionManifestLookupKeyTest.java @@ -25,10 +25,10 @@ import static org.mockito.Mockito.when; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.resolver.PolarisResolutionManifest; @@ -61,7 +61,7 @@ void pathKeyLookupResolvesRegisteredPath() { new PolarisDefaultDiagServiceImpl(), realmContext, resolverFactory, - PolarisPrincipal.of("p", Map.of(), Set.of()), + PolarisPrincipal.of("p", AttributeMap.EMPTY, Set.of()), "catalog"); ResolverPath tablePath = new ResolverPath(List.of("ns1", "tbl1"), PolarisEntityType.TABLE_LIKE); @@ -89,7 +89,7 @@ void pathKeyLookupFailsForUnregisteredPath() { new PolarisDefaultDiagServiceImpl(), realmContext, resolverFactory, - PolarisPrincipal.of("p", Map.of(), Set.of()), + PolarisPrincipal.of("p", AttributeMap.EMPTY, Set.of()), "catalog"); manifest.resolveAll(); @@ -122,7 +122,7 @@ void pathKeyLookupUsesLastRegistrationForDuplicateCanonicalPath() { new PolarisDefaultDiagServiceImpl(), realmContext, resolverFactory, - PolarisPrincipal.of("p", Map.of(), Set.of()), + PolarisPrincipal.of("p", AttributeMap.EMPTY, Set.of()), "catalog"); ResolverPath nonOptionalPath = diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java index edf5de2de8d..856b407ed76 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java @@ -22,10 +22,10 @@ import java.time.Clock; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.resolver.Resolvable; import org.apache.polaris.core.persistence.resolver.Resolver; @@ -81,7 +81,7 @@ public void testResolveSelectionsSkipsCallerPrincipalForReferenceCatalog() { diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); ResolverStatus status = resolver.resolveSelections(Set.of(Resolvable.REFERENCE_CATALOG)); @@ -95,7 +95,7 @@ public void testResolveSelectionsSkipsCallerPrincipalForRequestedPaths() { diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); pathResolver.addPath(new ResolverPath(List.of("N1"), PolarisEntityType.NAMESPACE)); @@ -110,7 +110,7 @@ public void testResolveSelectionsSkipsCallerPrincipalForRequestedTopLevelEntitie diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, null); entityResolver.addEntityByName(PolarisEntityType.PRINCIPAL, "P1"); @@ -126,7 +126,7 @@ public void testResolveSelectionsRequiresCallerPrincipalForCallerCatalogRoles() diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); ResolverStatus status = resolver.resolveSelections(Set.of(Resolvable.CALLER_CATALOG_ROLES)); @@ -142,7 +142,7 @@ public void testResolveSelectionsRequiresCallerPrincipalForCallerCatalogRoles() diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); resolver.addOptionalEntityByName(PolarisEntityType.CATALOG_ROLE, "role1"); @@ -161,7 +161,7 @@ public void testResolveSelectionsRequiresCallerPrincipalForCallerPrincipal() { diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); ResolverStatus status = resolver.resolveSelections(Set.of(Resolvable.CALLER_PRINCIPAL)); @@ -176,7 +176,7 @@ public void testResolveSelectionsRequiresCallerPrincipalForCallerPrincipalRoles( diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); ResolverStatus status = resolver.resolveSelections(Set.of(Resolvable.CALLER_PRINCIPAL_ROLES)); @@ -191,7 +191,7 @@ public void testResolveSelectionsThrowsOnGetResolvedCallerPrincipal() { diagServices, callCtx(), metaStoreManager(), - PolarisPrincipal.of("missing", Map.of(), Set.of()), + PolarisPrincipal.of("missing", AttributeMap.EMPTY, Set.of()), null, "test"); ResolverStatus status = resolver.resolveSelections(Set.of(Resolvable.REFERENCE_CATALOG)); diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegrationTest.java index a09d8d4ee7d..6ec8613dd2e 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -26,10 +26,10 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Optional; import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; @@ -109,7 +109,7 @@ private static List toGrants( .build(); public static final String AWS_PARTITION = "aws"; public static final PolarisPrincipal POLARIS_PRINCIPAL = - PolarisPrincipal.of("test-principal", Map.of(), Set.of()); + PolarisPrincipal.of("test-principal", AttributeMap.EMPTY, Set.of()); private static final CredentialVendingContext CONTEXT_WITH_PRINCIPAL = CredentialVendingContext.builder() .principalName(Optional.of(POLARIS_PRINCIPAL.getName())) diff --git a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java index 23556d80051..90791ac1363 100644 --- a/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java +++ b/polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java @@ -28,6 +28,8 @@ import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityCore; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -424,11 +426,12 @@ private Resolver allocateResolver( PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - principalRolesScope == null), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put( + PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, + principalRolesScope == null) + .build(), Optional.ofNullable(principalRolesScope).orElse(Set.of())); return new Resolver( diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/AuthenticatingAugmentor.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/AuthenticatingAugmentor.java index 5697272243b..6559193540f 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/AuthenticatingAugmentor.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/AuthenticatingAugmentor.java @@ -61,11 +61,14 @@ public Uni augment( private SecurityIdentity authenticatePolarisPrincipal(SecurityIdentity identity) { PolarisPrincipal polarisPrincipal = authenticator.authenticate(identity); - return QuarkusSecurityIdentity.builder(identity) - .setAnonymous(false) - .setPrincipal(polarisPrincipal) - .addRoles(polarisPrincipal.getRoles()) - .addAttributes(polarisPrincipal.getAttributes()) - .build(); + QuarkusSecurityIdentity.Builder builder = + QuarkusSecurityIdentity.builder(identity) + .setAnonymous(false) + .setPrincipal(polarisPrincipal) + .addRoles(polarisPrincipal.getRoles()); + polarisPrincipal + .getAttributes() + .forEach(attribute -> builder.addAttribute(attribute.key().key(), attribute.value())); + return builder.build(); } } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultAuthenticator.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultAuthenticator.java index fd6ab7c7a1c..998e53eb07e 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultAuthenticator.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultAuthenticator.java @@ -19,7 +19,6 @@ package org.apache.polaris.service.auth; import com.google.common.base.Throwables; -import com.google.common.collect.ImmutableMap; import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.identity.SecurityIdentity; import io.smallrye.common.annotation.Identifier; @@ -33,6 +32,10 @@ import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.AttributeMap; +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityType; @@ -99,7 +102,7 @@ public PolarisPrincipal authenticate(SecurityIdentity identity) { PrincipalEntity principalEntity = resolvePrincipalEntity(credentials); PrincipalRoleSelection principalRoles = resolvePrincipalRoles(credentials, principalEntity); - Map principalAttributes = + AttributeMap principalAttributes = resolvePrincipalAttributes(identity, principalEntity, principalRoles.allRolesRequested()); PolarisPrincipal polarisPrincipal = PolarisPrincipal.of(principalEntity.getName(), principalAttributes, principalRoles.roles()); @@ -159,17 +162,19 @@ protected PrincipalEntity resolvePrincipalEntity(PolarisCredential credentials) return principal; } - protected Map resolvePrincipalAttributes( + protected AttributeMap resolvePrincipalAttributes( SecurityIdentity identity, PrincipalEntity principalEntity, boolean allRolesRequested) { - ImmutableMap.Builder principalAttributes = - ImmutableMap.builder() - .putAll(identity.getAttributes()) - .put(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) - .put(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, allRolesRequested); + ImmutableAttributeMap.Builder principalAttributes = ImmutableAttributeMap.builder(); + identity + .getAttributes() + .forEach((key, value) -> principalAttributes.put(new AttributeKey<>(key), value)); + principalAttributes + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, allRolesRequested); if (identity.getPrincipal() instanceof JsonWebToken jwt) { - principalAttributes.put(PolarisPrincipal.JWT_ATTRIBUTE_KEY, jwt.getRawToken()); + principalAttributes.put(PolarisPrincipalAttributes.JWT_ATTRIBUTE_KEY, jwt.getRawToken()); } - return principalAttributes.buildKeepingLast(); + return principalAttributes.build(); } /** diff --git a/runtime/service/src/test/java/org/apache/polaris/service/admin/ManagementServiceTest.java b/runtime/service/src/test/java/org/apache/polaris/service/admin/ManagementServiceTest.java index 52ab0a5cefe..df382b3ea8d 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/admin/ManagementServiceTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/admin/ManagementServiceTest.java @@ -45,6 +45,8 @@ import org.apache.polaris.core.admin.model.UpdateCatalogRequest; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -378,7 +380,9 @@ private PolarisAdminService setupPolarisAdminService( PolarisPrincipal principal = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .build(), Set.of(PolarisEntityConstants.getNameOfPrincipalServiceAdminRole())); return new PolarisAdminService( callContext, diff --git a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceAuthzTest.java index 9cc68bd942f..abadc5c2542 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceAuthzTest.java @@ -29,6 +29,8 @@ import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.CatalogRoleEntity; import org.apache.polaris.core.entity.PolarisPrivilege; @@ -45,11 +47,10 @@ private PolarisAdminService newTestAdminService() { final PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); return new PolarisAdminService( callContext, @@ -66,7 +67,9 @@ private PolarisAdminService newTestAdminService(Set activatedPrincipalRo final PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), activatedPrincipalRoles); return PolarisAdminServiceTestSupport.newAdminService( callContext, diff --git a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java index a5b39813ef7..2f02ebb69d0 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAuthzTestBase.java @@ -56,6 +56,8 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; @@ -222,11 +224,10 @@ public void before(TestInfo testInfo) { this.authenticatedRoot = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - rootPrincipal, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); QuarkusMock.installMockForType(authenticatedRoot, PolarisPrincipal.class); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticatingAugmentorTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticatingAugmentorTest.java index 440a911c5c1..04982117b82 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticatingAugmentorTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticatingAugmentorTest.java @@ -29,6 +29,9 @@ import io.smallrye.mutiny.Uni; import java.security.Principal; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; +import org.apache.polaris.core.entity.PrincipalEntity; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -82,11 +85,18 @@ public void testAugmentSuccessfulAuthentication() { // Given PolarisPrincipal polarisPrincipal = mock(PolarisPrincipal.class); when(polarisPrincipal.getName()).thenReturn("user1"); + PrincipalEntity principalEntity = new PrincipalEntity.Builder().setName("user1").build(); + when(polarisPrincipal.getAttributes()) + .thenReturn( + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build()); PolarisCredential credential = mock(PolarisCredential.class); SecurityIdentity identity = QuarkusSecurityIdentity.builder() .setPrincipal(polarisPrincipal) .addCredential(credential) + .addAttribute("attr1", "value1") .build(); when(authenticator.authenticate(identity)).thenReturn(polarisPrincipal); @@ -99,5 +109,12 @@ public void testAugmentSuccessfulAuthentication() { assertThat(result).isNotNull(); assertThat(result.getPrincipal()).isSameAs(polarisPrincipal); assertThat(result.getPrincipal().getName()).isEqualTo("user1"); + // original attribute + assertThat(result.getAttribute("attr1")).isEqualTo("value1"); + // attribute added by the authenticator + assertThat( + result.getAttribute( + PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY.key())) + .isEqualTo(principalEntity); } } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/DefaultAuthenticatorTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/DefaultAuthenticatorTest.java index 4d8a9d58bd3..fd6c917d1b7 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/DefaultAuthenticatorTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/DefaultAuthenticatorTest.java @@ -35,12 +35,15 @@ import io.smallrye.common.annotation.Identifier; import jakarta.inject.Inject; import jakarta.ws.rs.ServiceUnavailableException; -import java.util.Map; import java.util.Set; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.admin.model.PrincipalWithCredentialsCredentials; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.AttributeMap; +import org.apache.polaris.core.collection.AttributeMap.AttributeKey; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntityType; @@ -98,7 +101,9 @@ public void setup(TestInfo testInfo) { PolarisPrincipal root = PolarisPrincipal.of( PolarisEntityConstants.getRootPrincipalName(), - Map.of(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); authenticatedRoot = root; identityAssociation.setIdentity(QuarkusSecurityIdentity.builder().setPrincipal(root).build()); @@ -193,8 +198,9 @@ void testPrincipalFoundWithAllRolesRequested() { // Then: should return principal with all assigned roles assertPrincipal(result, principalEntity, PRINCIPAL_ROLE1, PRINCIPAL_ROLE2); - assertThat(result.getAttributes()) - .containsEntry(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true); + assertThat( + result.getAttributes().get(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY)) + .isTrue(); } @Test @@ -211,8 +217,9 @@ void testPrincipalFoundWithSubsetOfRolesRequested() { // Then: should return principal with only the requested role assertPrincipal(result, principalEntity, PRINCIPAL_ROLE1); - assertThat(result.getAttributes()) - .containsEntry(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, false); + assertThat( + result.getAttributes().get(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY)) + .isFalse(); } @Test @@ -308,8 +315,9 @@ void testEmptyRolesRequestedReturnsEmptyRoles() { // Then: should return principal with empty roles set assertPrincipal(result, principalEntity); - assertThat(result.getAttributes()) - .containsEntry(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, false); + assertThat( + result.getAttributes().get(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY)) + .isFalse(); } @Test @@ -393,7 +401,8 @@ void testJwtAttributeAbsentWhenNoJwtPrincipal() { PolarisPrincipal result = authenticator.authenticate(identityFor(credentials)); // Then: JWT attribute must not be present - assertThat(result.getAttribute(PolarisPrincipal.JWT_ATTRIBUTE_KEY, String.class)).isEmpty(); + assertThat(result.getAttributes().containsKey(PolarisPrincipalAttributes.JWT_ATTRIBUTE_KEY)) + .isFalse(); } @Test @@ -415,8 +424,8 @@ void testJwtAttributePresentWhenJwtPrincipal() { PolarisPrincipal result = authenticator.authenticate(jwtIdentity); // Then: JWT attribute must carry the raw token - assertThat(result.getAttribute(PolarisPrincipal.JWT_ATTRIBUTE_KEY, String.class)) - .hasValue("raw.jwt.token"); + assertThat(result.getAttributes().get(PolarisPrincipalAttributes.JWT_ATTRIBUTE_KEY)) + .isEqualTo("raw.jwt.token"); } @Test @@ -435,7 +444,8 @@ void testInputIdentityAttributesPassedThrough() { PolarisPrincipal result = authenticator.authenticate(identityWithAttrs); // Then: custom attribute must be present alongside the Polaris-specific ones - assertThat(result.getAttribute("custom-key", String.class)).hasValue("custom-value"); + assertThat(result.getAttributes().get(new AttributeKey("custom-key"))) + .isEqualTo("custom-value"); } private PrincipalEntity createPrincipal(String name, String... roles) { @@ -485,16 +495,15 @@ private void assertPrincipal(PolarisPrincipal result, PrincipalEntity entity, St assertThat(result).isNotNull(); assertThat(result.getName()).isEqualTo(entity.getName()); assertThat(result.getRoles()).containsExactlyInAnyOrder(roles); - assertThat(result.getAttributes()) - .hasSize(2) - .containsKey(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY) - .containsKey(PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY); + AttributeMap attributes = result.getAttributes(); + assertThat(attributes.size()).isEqualTo(2); + assertThat(attributes.keySet()) + .contains(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY) + .contains(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY); assertThat( - result - .getAttribute( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, PrincipalEntity.class) - .map(PrincipalEntity::getInternalPropertiesAsMap) - .orElseThrow()) + attributes + .getRequired(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY) + .getInternalPropertiesAsMap()) .containsKey(PolarisEntityConstants.getClientIdPropertyName()); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/RootPrincipalAugmentor.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/RootPrincipalAugmentor.java index 8d9ff1dab31..451fbb9ee2a 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/RootPrincipalAugmentor.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/RootPrincipalAugmentor.java @@ -26,9 +26,10 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; -import java.util.Map; import java.util.Set; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PrincipalEntity; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -58,7 +59,9 @@ public Uni augment( PolarisPrincipal principal = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .build(), Set.of("service_admin")); return Uni.createFrom() diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/AbstractPolarisGenericTableCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/AbstractPolarisGenericTableCatalogTest.java index 97c9b0e3073..77f2835eded 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/AbstractPolarisGenericTableCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/AbstractPolarisGenericTableCatalogTest.java @@ -43,6 +43,8 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -154,11 +156,10 @@ public void before(TestInfo testInfo) { authenticatedRoot = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - rootPrincipal, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); polarisPrincipalHolder.set(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalogHandlerAuthzTest.java index 3433a0119f9..9d464890c55 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/generic/PolarisGenericTableCatalogHandlerAuthzTest.java @@ -27,7 +27,9 @@ import java.util.stream.Stream; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.catalog.FederatedCatalogFactory; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.credentials.PolarisCredentialManager; import org.apache.polaris.core.entity.PolarisGrantRecord; import org.apache.polaris.core.entity.PolarisPrivilege; @@ -49,11 +51,10 @@ private GenericTableCatalogHandler newWrapper() { return newWrapper( PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of())); } @@ -61,7 +62,9 @@ private GenericTableCatalogHandler newWrapper(Set activatedPrincipalRole return newWrapper( PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), activatedPrincipalRoles)); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java index 8270e1e8344..8c636bc2db0 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractIcebergCatalogHandlerAuthzTest.java @@ -70,7 +70,9 @@ import org.apache.polaris.core.admin.model.PrincipalWithCredentialsCredentials; import org.apache.polaris.core.admin.model.StorageConfigInfo; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.catalog.LocalCatalogFactory; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfiguration; import org.apache.polaris.core.config.RealmConfig; @@ -125,11 +127,10 @@ protected IcebergCatalogHandler newHandler() { PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); return icebergCatalogHandlerFactory.createHandler(CATALOG_NAME, authenticatedPrincipal); } @@ -143,7 +144,9 @@ private IcebergCatalogHandler newHandler( PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), activatedPrincipalRoles); IcebergCatalogHandler handler = icebergCatalogHandlerFactory.createHandler(catalogName, authenticatedPrincipal); @@ -187,7 +190,9 @@ Stream testInsufficientPermissionsPriorToSecretRotation() { PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)); Supplier handler = @@ -236,7 +241,9 @@ Stream testInsufficientPermissionsPriorToSecretRotation() { PolarisPrincipal authenticatedPrincipal1 = PolarisPrincipal.of( refreshPrincipal.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, refreshPrincipal), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, refreshPrincipal) + .build(), Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)); Supplier refreshedWrapper = @@ -1187,11 +1194,10 @@ private IcebergCatalogHandler newHandlerWithFineGrainedAuthzDisabled() { PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); // Create a custom CallContext that returns a custom RealmConfig diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractLocalIcebergCatalogViewTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractLocalIcebergCatalogViewTest.java index 7e11ab1952c..7401bc7b424 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractLocalIcebergCatalogViewTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/AbstractLocalIcebergCatalogViewTest.java @@ -49,6 +49,8 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -170,11 +172,10 @@ public void before(TestInfo testInfo) { authenticatedRoot = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - rootPrincipal, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); authorizer = new PolarisAuthorizerImpl(realmConfig); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerFineGrainedDisabledTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerFineGrainedDisabledTest.java index 842e09fbaaf..5248cc8a920 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerFineGrainedDisabledTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerFineGrainedDisabledTest.java @@ -30,6 +30,8 @@ import org.apache.iceberg.MetadataUpdate; import org.apache.iceberg.rest.requests.UpdateTableRequest; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.service.Profiles; import org.apache.polaris.service.admin.PolarisAuthzTestBase; @@ -51,11 +53,10 @@ private IcebergCatalogHandler newHandler() { PolarisPrincipal authenticatedPrincipal = PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); IcebergCatalogHandler handler = icebergCatalogHandlerFactory.createHandler(CATALOG_NAME, authenticatedPrincipal); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerTest.java index a72206efad0..bea941afa2c 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandlerTest.java @@ -63,6 +63,7 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.LocalCatalogFactory; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -157,7 +158,7 @@ private IcebergCatalogHandler newHandler() { return ImmutableIcebergCatalogHandler.builder() .catalogName(CATALOG_NAME) - .polarisPrincipal(PolarisPrincipal.of("test", Map.of(), Set.of())) + .polarisPrincipal(PolarisPrincipal.of("test", AttributeMap.EMPTY, Set.of())) .callContext(callContext) .metaStoreManager(mock(PolarisMetaStoreManager.class)) .resolutionManifestFactory(resolutionManifestFactory) diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/LocalIcebergCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/LocalIcebergCatalogTest.java index 53fa29b63b4..6393f09ad67 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/LocalIcebergCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/iceberg/LocalIcebergCatalogTest.java @@ -39,6 +39,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.CatalogEntity; @@ -74,7 +75,8 @@ class LocalIcebergCatalogTest { @Mock PolarisEntity ns2; @Mock PolarisEntity table3; - private final PolarisPrincipal principal = PolarisPrincipal.of("test", Map.of(), Set.of()); + private final PolarisPrincipal principal = + PolarisPrincipal.of("test", AttributeMap.EMPTY, Set.of()); private LocalIcebergCatalog catalog; private ResolvedPolarisEntity rns1 = new ResolvedPolarisEntity(ns1, List.of(), List.of()); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/AbstractPolicyCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/AbstractPolicyCatalogTest.java index 3a1152bd5e2..083ed755611 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/AbstractPolicyCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/AbstractPolicyCatalogTest.java @@ -35,7 +35,6 @@ import java.lang.reflect.Method; import java.util.Arrays; import java.util.List; -import java.util.Map; import java.util.Set; import org.apache.iceberg.CatalogProperties; import org.apache.iceberg.Schema; @@ -51,6 +50,8 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -176,11 +177,10 @@ public void before(TestInfo testInfo) { authenticatedRoot = PolarisPrincipal.of( rootPrincipal.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - rootPrincipal, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, rootPrincipal) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of()); polarisPrincipalHolder.set(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandlerAuthzTest.java index a42f9e59a21..fc104587056 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandlerAuthzTest.java @@ -21,11 +21,12 @@ import io.quarkus.test.junit.QuarkusTest; import io.quarkus.test.junit.TestProfile; import java.util.Arrays; -import java.util.Map; import java.util.Set; import java.util.stream.Stream; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.core.policy.PredefinedPolicyTypes; import org.apache.polaris.service.Profiles; @@ -47,11 +48,10 @@ private PolicyCatalogHandler newHandler() { return newHandler( PolarisPrincipal.of( principalEntity.getName(), - Map.of( - PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, - principalEntity, - PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, - true), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .put(PolarisPrincipalAttributes.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY, true) + .build(), Set.of())); } @@ -59,7 +59,9 @@ private PolicyCatalogHandler newHandler(Set activatedPrincipalRoles) { return newHandler( PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), activatedPrincipalRoles)); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListenerTest.java b/runtime/service/src/test/java/org/apache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListenerTest.java index 4e7b74a6fc6..5c0f9714748 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListenerTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/events/jsonEventListener/aws/cloudwatch/AwsCloudWatchEventListenerTest.java @@ -27,11 +27,11 @@ import java.math.BigInteger; import java.time.Clock; import java.time.Duration; -import java.util.Map; import java.util.Set; import java.util.UUID; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.service.config.PolarisIcebergObjectMapperCustomizer; import org.apache.polaris.service.events.EventAttributeMap; import org.apache.polaris.service.events.EventAttributes; @@ -65,7 +65,7 @@ class AwsCloudWatchEventListenerTest { private static final String REALM = "test-realm"; private static final String TEST_USER = "test-user"; private static final PolarisPrincipal PRINCIPAL = - PolarisPrincipal.of(TEST_USER, Map.of(), Set.of("role1", "role2")); + PolarisPrincipal.of(TEST_USER, AttributeMap.EMPTY, Set.of("role1", "role2")); private static final Clock CLOCK = Clock.systemUTC(); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/PolarisPersistenceEventListenerTest.java b/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/PolarisPersistenceEventListenerTest.java index 9a11c59d171..8f84576a596 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/PolarisPersistenceEventListenerTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/PolarisPersistenceEventListenerTest.java @@ -43,6 +43,7 @@ import org.apache.iceberg.rest.responses.LoadTableResponse; import org.apache.iceberg.types.Types; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.entity.EventEntity; import org.apache.polaris.service.events.DefaultEventSanitizer; import org.apache.polaris.service.events.EventAttributeMap; @@ -204,7 +205,7 @@ void shouldFallbackWhenNoCatalogOrResourceAttributesExist() { void shouldPersistRequestUserAndTimestampMetadataFields() { CapturingPersistenceListener listener = new CapturingPersistenceListener(); Instant timestamp = Instant.parse("2024-01-02T03:04:05Z"); - PolarisPrincipal principal = PolarisPrincipal.of("alice", Map.of(), Set.of("role1")); + PolarisPrincipal principal = PolarisPrincipal.of("alice", AttributeMap.EMPTY, Set.of("role1")); PolarisEventMetadata metadata = PolarisEventMetadata.builder() .realmId(REALM_ID) diff --git a/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/opentelemetry/OpenTelemetryEventListenerTest.java b/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/opentelemetry/OpenTelemetryEventListenerTest.java index 62292446406..4da43712ca9 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/opentelemetry/OpenTelemetryEventListenerTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/events/listeners/opentelemetry/OpenTelemetryEventListenerTest.java @@ -66,6 +66,7 @@ import org.apache.polaris.core.admin.model.TableGrant; import org.apache.polaris.core.admin.model.TablePrivilege; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.collection.AttributeMap; import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.service.events.EventAttributeMap; import org.apache.polaris.service.events.EventAttributes; @@ -307,7 +308,7 @@ private static PolarisEventMetadata metadata(Map openTelemetryCo .timestamp(Instant.parse("2026-06-19T00:00:00Z")) .realmId("test_realm") .requestId("request-1") - .user(PolarisPrincipal.of("test_user", Map.of(), Set.of("role1", "role2"))) + .user(PolarisPrincipal.of("test_user", AttributeMap.EMPTY, Set.of("role1", "role2"))) .openTelemetryContext(openTelemetryContext) .build(); } diff --git a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java index f6a026c7bad..728e6683198 100644 --- a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java +++ b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java @@ -44,8 +44,10 @@ import org.apache.polaris.core.auth.AuthorizationState; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipalAttributes; import org.apache.polaris.core.catalog.FederatedCatalogFactory; import org.apache.polaris.core.catalog.LocalCatalogFactory; +import org.apache.polaris.core.collection.ImmutableAttributeMap; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.config.RealmConfigImpl; import org.apache.polaris.core.config.RealmConfigurationSource; @@ -300,7 +302,9 @@ public TestServices build() { PolarisPrincipal principal = PolarisPrincipal.of( principalEntity.getName(), - Map.of(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity), + ImmutableAttributeMap.builder() + .put(PolarisPrincipalAttributes.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, principalEntity) + .build(), Set.of()); SecurityContext securityContext =