diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthenticatedPolarisPrincipal.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthenticatedPolarisPrincipal.java deleted file mode 100644 index b097d1583f..0000000000 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/AuthenticatedPolarisPrincipal.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * 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 jakarta.annotation.Nonnull; -import java.util.Set; -import org.apache.polaris.core.entity.PolarisEntity; - -/** Holds the results of request authentication. */ -public class AuthenticatedPolarisPrincipal implements java.security.Principal { - private final PolarisEntity principalEntity; - private final Set activatedPrincipalRoleNames; - - public AuthenticatedPolarisPrincipal( - @Nonnull PolarisEntity principalEntity, @Nonnull Set activatedPrincipalRoles) { - this.principalEntity = principalEntity; - this.activatedPrincipalRoleNames = Set.copyOf(activatedPrincipalRoles); - } - - @Override - public String getName() { - return principalEntity.getName(); - } - - public PolarisEntity getPrincipalEntity() { - return principalEntity; - } - - /** - * Returns the set of activated principal role names. Activated role names are the roles that were - * explicitly requested by the client when authenticating, through JWT claims or other means. - * - *

By convention, this method returns an empty set when the principal is requesting all - * available principal roles. - */ - public Set getActivatedPrincipalRoleNames() { - return activatedPrincipalRoleNames; - } - - @Override - public String toString() { - return "principalEntity=" - + getPrincipalEntity() - + ";activatedPrincipalRoleNames=" - + getActivatedPrincipalRoleNames(); - } -} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java index 31e69b0832..55c3792067 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java @@ -29,14 +29,14 @@ public interface PolarisAuthorizer { void authorizeOrThrow( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable PolarisResolvedPathWrapper target, @Nullable PolarisResolvedPathWrapper secondary); void authorizeOrThrow( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable List targets, diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java index baae15fc09..72be7f88cb 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java @@ -558,13 +558,13 @@ public boolean matchesOrIsSubsumedBy( @Override public void authorizeOrThrow( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable PolarisResolvedPathWrapper target, @Nullable PolarisResolvedPathWrapper secondary) { authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, activatedEntities, authzOp, target == null ? null : List.of(target), @@ -573,7 +573,7 @@ public void authorizeOrThrow( @Override public void authorizeOrThrow( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable List targets, @@ -582,20 +582,18 @@ public void authorizeOrThrow( realmConfig.getConfig( FeatureConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING); if (enforceCredentialRotationRequiredState - && authenticatedPrincipal - .getPrincipalEntity() - .getInternalPropertiesAsMap() + && polarisPrincipal + .getProperties() .containsKey(PolarisEntityConstants.PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE) && authzOp != PolarisAuthorizableOperation.ROTATE_CREDENTIALS) { throw new ForbiddenException( "Principal '%s' is not authorized for op %s due to PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE", - authenticatedPrincipal.getName(), authzOp); - } else if (!isAuthorized( - authenticatedPrincipal, activatedEntities, authzOp, targets, secondaries)) { + polarisPrincipal.getName(), authzOp); + } else if (!isAuthorized(polarisPrincipal, activatedEntities, authzOp, targets, secondaries)) { throw new ForbiddenException( "Principal '%s' with activated PrincipalRoles '%s' and activated grants via '%s' is not authorized for op %s", - authenticatedPrincipal.getName(), - authenticatedPrincipal.getActivatedPrincipalRoleNames(), + polarisPrincipal.getName(), + polarisPrincipal.getRoles(), activatedEntities.stream().map(PolarisEntityCore::getName).collect(Collectors.toSet()), authzOp); } @@ -607,13 +605,13 @@ public void authorizeOrThrow( * the operation. */ public boolean isAuthorized( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPolarisPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable PolarisResolvedPathWrapper target, @Nullable PolarisResolvedPathWrapper secondary) { return isAuthorized( - authenticatedPolarisPrincipal, + polarisPrincipal, activatedEntities, authzOp, target == null ? null : List.of(target), @@ -621,7 +619,7 @@ public boolean isAuthorized( } public boolean isAuthorized( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPolarisPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable List targets, @@ -636,8 +634,7 @@ public boolean isAuthorized( authzOp, privilegeOnTarget); for (PolarisResolvedPathWrapper target : targets) { - if (!hasTransitivePrivilege( - authenticatedPolarisPrincipal, entityIdSet, privilegeOnTarget, target)) { + if (!hasTransitivePrivilege(polarisPrincipal, entityIdSet, privilegeOnTarget, target)) { // TODO: Collect missing privileges to report all at the end and/or return to code // that throws NotAuthorizedException for more useful messages. return false; @@ -652,7 +649,7 @@ public boolean isAuthorized( privilegeOnSecondary); for (PolarisResolvedPathWrapper secondary : secondaries) { if (!hasTransitivePrivilege( - authenticatedPolarisPrincipal, entityIdSet, privilegeOnSecondary, secondary)) { + polarisPrincipal, entityIdSet, privilegeOnSecondary, secondary)) { return false; } } @@ -670,7 +667,7 @@ public boolean isAuthorized( * errors/exceptions. */ public boolean hasTransitivePrivilege( - @Nonnull AuthenticatedPolarisPrincipal authenticatedPolarisPrincipal, + @Nonnull PolarisPrincipal polarisPrincipal, Set activatedGranteeIds, PolarisPrivilege desiredPrivilege, PolarisResolvedPathWrapper resolvedPath) { @@ -693,7 +690,7 @@ public boolean hasTransitivePrivilege( desiredPrivilege, grantRecord, resolvedSecurableEntity, - authenticatedPolarisPrincipal.getName(), + polarisPrincipal.getName(), activatedGranteeIds); return true; } @@ -704,7 +701,7 @@ public boolean hasTransitivePrivilege( LOGGER.debug( "Failed to satisfy privilege {} for principalName {} on resolvedPath {}", desiredPrivilege, - authenticatedPolarisPrincipal.getName(), + polarisPrincipal.getName(), resolvedPath); return false; } 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 new file mode 100644 index 0000000000..cfa5612a32 --- /dev/null +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisPrincipal.java @@ -0,0 +1,89 @@ +/* + * 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 java.security.Principal; +import java.util.Map; +import java.util.Set; +import org.apache.polaris.core.entity.PrincipalEntity; +import org.apache.polaris.immutables.PolarisImmutable; + +/** Represents a {@link Principal} in the Polaris system. */ +@PolarisImmutable +public interface PolarisPrincipal extends Principal { + + /** + * Creates a new instance of {@link PolarisPrincipal} from the given {@link PrincipalEntity} and + * roles. + * + *

The created principal will have the same ID and name as the {@link PrincipalEntity}, and its + * properties will be derived from the internal properties of the entity. + * + * @param principalEntity the principal entity representing the user or service + * @param roles the set of roles associated with the principal + */ + static PolarisPrincipal of(PrincipalEntity principalEntity, Set roles) { + return of( + principalEntity.getId(), + principalEntity.getName(), + principalEntity.getInternalPropertiesAsMap(), + roles); + } + + /** + * Creates a new instance of {@link PolarisPrincipal} with the specified ID, name, roles, and + * properties. + * + * @param id the unique identifier of the principal + * @param name the name of the principal + * @param properties additional properties associated with the principal + * @param roles the set of roles associated with the principal + */ + static PolarisPrincipal of( + long id, String name, Map properties, Set roles) { + return ImmutablePolarisPrincipal.builder() + .id(id) + .name(name) + .properties(properties) + .roles(roles) + .build(); + } + + /** + * Returns the unique identifier of the principal. + * + *

This identifier is used to uniquely identify the principal within a Polaris realm. + */ + long getId(); + + /** + * Returns the set of activated principal role names. Activated role names are the roles that were + * explicitly requested by the client when authenticating, through JWT claims or other means. It + * may be a subset of the roles that the principal has in the system. + */ + Set getRoles(); + + /** + * Returns the properties of this principal. + * + *

Properties are key-value pairs that provide additional information about the principal, such + * as permissions, preferences, or other metadata. + */ + Map getProperties(); +} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/PolarisResolutionManifest.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/PolarisResolutionManifest.java index e5c149c82e..f4504d6e18 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/PolarisResolutionManifest.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/PolarisResolutionManifest.java @@ -29,7 +29,7 @@ import java.util.Map; import java.util.Set; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; @@ -83,7 +83,7 @@ public PolarisResolutionManifest( this.diagnostics.checkNotNull(securityContext, "null_security_context_for_resolution_manifest"); this.securityContext = securityContext; diagnostics.check( - securityContext.getUserPrincipal() instanceof AuthenticatedPolarisPrincipal, + securityContext.getUserPrincipal() instanceof PolarisPrincipal, "invalid_principal_type_for_resolution_manifest", "principal={}", securityContext.getUserPrincipal()); 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 4d3d0cff5b..08f257b5c4 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 @@ -32,7 +32,7 @@ import java.util.stream.Collectors; import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisChangeTrackingVersions; @@ -68,7 +68,7 @@ public class Resolver { @Nullable private final EntityCache cache; // the id of the principal making the call or 0 if unknown - private final @Nonnull AuthenticatedPolarisPrincipal polarisPrincipal; + private final @Nonnull PolarisPrincipal polarisPrincipal; private final @Nonnull SecurityContext securityContext; // reference catalog name for name resolution @@ -116,7 +116,7 @@ public class Resolver { * * @param polarisCallContext the polaris call context * @param polarisMetaStoreManager meta store manager - * @param securityContext The {@link AuthenticatedPolarisPrincipal} for the current request + * @param securityContext The {@link SecurityContext} for the current request * @param cache shared entity cache * @param referenceCatalogName if not null, specifies the name of the reference catalog. The * reference catalog is the catalog used to resolve catalog roles and catalog path. Also, if a @@ -147,12 +147,12 @@ public Resolver( this.diagnostics.checkNotNull( securityContext.getUserPrincipal(), "principal_must_be_specified"); this.diagnostics.check( - securityContext.getUserPrincipal() instanceof AuthenticatedPolarisPrincipal, + securityContext.getUserPrincipal() instanceof PolarisPrincipal, "unexpected_principal_type", "class={}", securityContext.getUserPrincipal().getClass().getName()); - this.polarisPrincipal = (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); // paths to resolve this.pathsToResolve = new ArrayList<>(); this.resolvedPaths = new ArrayList<>(); @@ -758,7 +758,7 @@ private ResolverStatus resolveCallerPrincipalAndPrincipalRoles( toValidate, PolarisEntityType.PRINCIPAL, PolarisEntityConstants.getNullId(), - polarisPrincipal.getPrincipalEntity().getId()); + polarisPrincipal.getId()); // if the principal was not found, we can end right there if (this.resolvedCallerPrincipal == null @@ -768,10 +768,9 @@ private ResolverStatus resolveCallerPrincipalAndPrincipalRoles( // activate all principal roles specified in the authenticated principal resolvedCallerPrincipalRoles = - this.polarisPrincipal.getActivatedPrincipalRoleNames().isEmpty() + this.polarisPrincipal.getRoles().isEmpty() ? resolveAllPrincipalRoles(toValidate, resolvedCallerPrincipal) - : resolvePrincipalRolesByName( - toValidate, this.polarisPrincipal.getActivatedPrincipalRoleNames()); + : resolvePrincipalRolesByName(toValidate, this.polarisPrincipal.getRoles()); // total success return new ResolverStatus(ResolverStatus.StatusEnum.SUCCESS); 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 c02dc4155a..c0daa7c449 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 @@ -31,7 +31,7 @@ import java.util.Set; import java.util.stream.Collectors; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityCore; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -481,8 +481,8 @@ private Resolver allocateResolver( .filter(Optional::isPresent) .map(Optional::get) .collect(Collectors.toList())); - AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal( + PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of( PrincipalEntity.of(P1), Optional.ofNullable(principalRolesScope).orElse(Set.of())); return new Resolver( callCtx(), diff --git a/runtime/service/build.gradle.kts b/runtime/service/build.gradle.kts index 699b0a8c88..87e95c5300 100644 --- a/runtime/service/build.gradle.kts +++ b/runtime/service/build.gradle.kts @@ -36,6 +36,9 @@ dependencies { implementation(project(":polaris-runtime-defaults")) implementation(project(":polaris-runtime-common")) + compileOnly(project(":polaris-immutables")) + annotationProcessor(project(":polaris-immutables", configuration = "processor")) + implementation(platform(libs.iceberg.bom)) implementation("org.apache.iceberg:iceberg-api") implementation("org.apache.iceberg:iceberg-core") diff --git a/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java b/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java index 5970ff8663..4d5f763b4f 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java @@ -74,9 +74,9 @@ import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; import org.apache.polaris.core.admin.model.ViewGrant; import org.apache.polaris.core.admin.model.ViewPrivilege; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; 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.catalog.PolarisCatalogHelpers; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.connection.AuthenticationParametersDpo; @@ -140,7 +140,7 @@ public class PolarisAdminService { private final CallContext callContext; private final ResolutionManifestFactory resolutionManifestFactory; private final SecurityContext securityContext; - private final AuthenticatedPolarisPrincipal authenticatedPrincipal; + private final PolarisPrincipal polarisPrincipal; private final PolarisAuthorizer authorizer; private final PolarisMetaStoreManager metaStoreManager; private final UserSecretsManager userSecretsManager; @@ -165,12 +165,11 @@ public PolarisAdminService( diagServices.checkNotNull(securityContext, "null_security_context"); diagServices.checkNotNull(securityContext.getUserPrincipal(), "null_security_context"); diagServices.check( - securityContext.getUserPrincipal() instanceof AuthenticatedPolarisPrincipal, + securityContext.getUserPrincipal() instanceof PolarisPrincipal, "unexpected_principal_type", "class={}", securityContext.getUserPrincipal().getClass().getName()); - this.authenticatedPrincipal = - (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); this.authorizer = authorizer; this.userSecretsManager = userSecretsManager; this.reservedProperties = reservedProperties; @@ -251,7 +250,7 @@ private void authorizeBasicRootOperationOrThrow(PolarisAuthorizableOperation op) PolarisResolvedPathWrapper rootContainerWrapper = resolutionManifest.getResolvedRootContainerEntityAsPath(); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedPrincipalRoleEntities(), op, rootContainerWrapper, @@ -286,7 +285,7 @@ private void authorizeBasicTopLevelEntityOperationOrThrow( // TODO: If we do add more "self" privilege operations for PRINCIPAL targets this should // be extracted into an EnumSet and/or pushed down into PolarisAuthorizer. if (topLevelEntityWrapper.getResolvedLeafEntity().getEntity().getId() - == authenticatedPrincipal.getPrincipalEntity().getId() + == polarisPrincipal.getId() && (op.equals(PolarisAuthorizableOperation.ROTATE_CREDENTIALS) || op.equals(PolarisAuthorizableOperation.RESET_CREDENTIALS))) { LOGGER @@ -296,7 +295,7 @@ private void authorizeBasicTopLevelEntityOperationOrThrow( return; } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, topLevelEntityWrapper, @@ -317,7 +316,7 @@ private void authorizeBasicCatalogRoleOperationOrThrow( throw new NotFoundException("CatalogRole does not exist: %s", catalogRoleName); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -347,7 +346,7 @@ private void authorizeGrantOnRootContainerToPrincipalRoleOperationOrThrow( principalRoleName, PolarisEntityType.PRINCIPAL_ROLE); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, rootContainerWrapper, @@ -383,7 +382,7 @@ private void authorizeGrantOnTopLevelEntityToPrincipalRoleOperationOrThrow( principalRoleName, PolarisEntityType.PRINCIPAL_ROLE); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, topLevelEntityWrapper, @@ -413,7 +412,7 @@ private void authorizeGrantOnPrincipalRoleToPrincipalOperationOrThrow( resolutionManifest.getResolvedTopLevelEntity(principalName, PolarisEntityType.PRINCIPAL); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, principalRoleWrapper, @@ -452,7 +451,7 @@ private void authorizeGrantOnCatalogRoleToPrincipalRoleOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, catalogRoleWrapper, @@ -482,7 +481,7 @@ private void authorizeGrantOnCatalogOperationOrThrow( PolarisResolvedPathWrapper catalogRoleWrapper = resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, catalogWrapper, @@ -522,7 +521,7 @@ private void authorizeGrantOnNamespaceOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, namespaceWrapper, @@ -568,7 +567,7 @@ private void authorizeGrantOnTableLikeOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, tableLikeWrapper, @@ -607,7 +606,7 @@ private void authorizeGrantOnPolicyOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, policyWrapper, diff --git a/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java b/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java index 1c57a37f71..2391abdb9e 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java @@ -61,8 +61,8 @@ import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; import org.apache.polaris.core.admin.model.ViewGrant; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; @@ -118,8 +118,7 @@ public PolarisServiceImpl( private PolarisAdminService newAdminService( RealmContext realmContext, SecurityContext securityContext) { - AuthenticatedPolarisPrincipal authenticatedPrincipal = - (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + PolarisPrincipal authenticatedPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); if (authenticatedPrincipal == null) { throw new NotAuthorizedException("Failed to find authenticatedPrincipal in SecurityContext"); } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesAugmentor.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesAugmentor.java index 29b8dba8f5..48abac56b0 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesAugmentor.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesAugmentor.java @@ -27,7 +27,7 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Set; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; /** * A custom {@link SecurityIdentityAugmentor} that adds active roles to the {@link @@ -62,11 +62,10 @@ public Uni augment( } private SecurityIdentity validateActiveRoles(SecurityIdentity identity) { - if (!(identity.getPrincipal() instanceof AuthenticatedPolarisPrincipal)) { + if (!(identity.getPrincipal() instanceof PolarisPrincipal)) { throw new AuthenticationFailedException("No Polaris principal found"); } - AuthenticatedPolarisPrincipal polarisPrincipal = - identity.getPrincipal(AuthenticatedPolarisPrincipal.class); + PolarisPrincipal polarisPrincipal = identity.getPrincipal(PolarisPrincipal.class); Set validRoleNames = activeRolesProvider.getActiveRoles(polarisPrincipal); return QuarkusSecurityIdentity.builder() .setAnonymous(false) diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java index 0da63e620c..43bbb9c2b9 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java @@ -19,7 +19,7 @@ package org.apache.polaris.service.auth; import java.util.Set; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; /** * Provides the active roles for a given principal. Implementations may rely on the active request @@ -32,5 +32,5 @@ public interface ActiveRolesProvider { * @param principal the currently authenticated principal * @return the active roles */ - Set getActiveRoles(AuthenticatedPolarisPrincipal principal); + Set getActiveRoles(PolarisPrincipal principal); } 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 35a04a29c7..3bff00b5c5 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 @@ -26,24 +26,22 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.iceberg.exceptions.NotAuthorizedException; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; /** * A custom {@link SecurityIdentityAugmentor} that, after Quarkus OIDC or Internal Auth extracted * and validated the principal credentials, augments the {@link SecurityIdentity} by authenticating - * the principal and setting an {@link AuthenticatedPolarisPrincipal} as the identity's principal. + * the principal and setting a {@link PolarisPrincipal} as the identity's principal. */ @ApplicationScoped public class AuthenticatingAugmentor implements SecurityIdentityAugmentor { public static final int PRIORITY = 1000; - private final Authenticator authenticator; + private final Authenticator authenticator; @Inject - public AuthenticatingAugmentor( - Authenticator authenticator) { + public AuthenticatingAugmentor(Authenticator authenticator) { this.authenticator = authenticator; } @@ -58,12 +56,12 @@ public Uni augment( if (identity.isAnonymous()) { return Uni.createFrom().item(identity); } - PrincipalAuthInfo authInfo = extractPrincipalAuthInfo(identity); + PolarisCredential authInfo = extractPolarisCredential(identity); return context.runBlocking(() -> authenticatePolarisPrincipal(identity, authInfo)); } - private PrincipalAuthInfo extractPrincipalAuthInfo(SecurityIdentity identity) { - PrincipalAuthInfo credential = identity.getCredential(PrincipalAuthInfo.class); + private PolarisCredential extractPolarisCredential(SecurityIdentity identity) { + PolarisCredential credential = identity.getCredential(PolarisCredential.class); if (credential == null) { throw new AuthenticationFailedException("No token credential available"); } @@ -71,12 +69,9 @@ private PrincipalAuthInfo extractPrincipalAuthInfo(SecurityIdentity identity) { } private SecurityIdentity authenticatePolarisPrincipal( - SecurityIdentity identity, PrincipalAuthInfo authInfo) { + SecurityIdentity identity, PolarisCredential polarisCredential) { try { - AuthenticatedPolarisPrincipal polarisPrincipal = - authenticator - .authenticate(authInfo) - .orElseThrow(() -> new NotAuthorizedException("Authentication failed")); + PolarisPrincipal polarisPrincipal = authenticator.authenticate(polarisCredential); return QuarkusSecurityIdentity.builder(identity).setPrincipal(polarisPrincipal).build(); } catch (RuntimeException e) { throw new AuthenticationFailedException(e); diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/Authenticator.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/Authenticator.java index 5ea5f72685..269390b65b 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/Authenticator.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/Authenticator.java @@ -18,31 +18,30 @@ */ package org.apache.polaris.service.auth; -import java.security.Principal; -import java.util.Optional; import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.iceberg.exceptions.ServiceFailureException; +import org.apache.polaris.core.auth.PolarisPrincipal; /** - * An interface for authenticating principals based on provided credentials. + * An interface for authenticating {@linkplain PolarisPrincipal principals} based on provided + * {@linkplain PolarisCredential credentials}. * - * @param the type of credentials used for authentication - * @param

the type of principal that is returned upon successful authentication + *

Authenticators are used in both internal and external authentication scenarios. */ -public interface Authenticator { +public interface Authenticator { /** - * Authenticates the given credentials and returns an optional principal. + * Authenticates the given {@link PolarisCredential} and returns an authenticated {@link + * PolarisPrincipal}. * - *

If the credentials are not valid or if the authentication fails, implementations may choose - * to return an empty optional or throw an exception. Returning empty will generally translate - * into a {@link NotAuthorizedException}. + *

If the credentials are not valid or if the authentication fails, implementations MUST throw + * {@link NotAuthorizedException}. * * @param credentials the credentials to authenticate - * @return an optional principal if authentication is successful, or an empty optional if - * authentication fails. + * @return the authenticated principal * @throws NotAuthorizedException if the credentials are not authorized * @throws ServiceFailureException if there is a failure in the authentication service */ - Optional

authenticate(C credentials) throws NotAuthorizedException, ServiceFailureException; + PolarisPrincipal authenticate(PolarisCredential credentials) + throws NotAuthorizedException, ServiceFailureException; } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/DecodedToken.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/DecodedToken.java index 73fada6752..a66a607d65 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/DecodedToken.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/DecodedToken.java @@ -23,10 +23,10 @@ import java.util.stream.Collectors; /** - * A specialized {@link PrincipalAuthInfo} used for internal authentication, when Polaris is the + * A specialized {@link PolarisCredential} used for internal authentication, when Polaris is the * identity provider. */ -public interface DecodedToken extends PrincipalAuthInfo { +public interface DecodedToken extends PolarisCredential { String getClientId(); diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java index 1d4a507877..f0bc6c7cb0 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java @@ -25,9 +25,11 @@ import java.util.Set; import java.util.function.Predicate; import java.util.stream.Collectors; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.iceberg.exceptions.NotAuthorizedException; +import org.apache.iceberg.exceptions.ServiceFailureException; import org.apache.polaris.core.PolarisCallContext; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; @@ -41,9 +43,8 @@ /** * Default implementation of the {@link ActiveRolesProvider} looks up the grant records for a - * principal to determine roles that are available. {@link - * AuthenticatedPolarisPrincipal#getActivatedPrincipalRoleNames()} is used to determine which of the - * available roles are active for this request. + * principal to determine roles that are available. {@link PolarisPrincipal#getRoles()} is used to + * determine which of the available roles are active for this request. */ @RequestScoped @Identifier("default") @@ -54,11 +55,18 @@ public class DefaultActiveRolesProvider implements ActiveRolesProvider { @Inject MetaStoreManagerFactory metaStoreManagerFactory; @Override - public Set getActiveRoles(AuthenticatedPolarisPrincipal principal) { + public Set getActiveRoles(PolarisPrincipal principal) { + if (!(principal instanceof PersistedPolarisPrincipal persistedPolarisPrincipal)) { + LOGGER.error( + "Expected an PersistedPolarisPrincipal, but got {}: {}", + principal.getClass().getName(), + ExceptionUtils.getStackTrace(new ServiceFailureException("Invalid principal type"))); + throw new NotAuthorizedException("Unable to authenticate"); + } List activeRoles = loadActivePrincipalRoles( - principal.getActivatedPrincipalRoleNames(), - principal.getPrincipalEntity(), + principal.getRoles(), + persistedPolarisPrincipal.getEntity(), metaStoreManagerFactory.getOrCreateMetaStoreManager(callContext.getRealmContext())); return activeRoles.stream().map(PrincipalRoleEntity::getName).collect(Collectors.toSet()); } 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 eca25ceff9..6808c2341d 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 @@ -22,12 +22,11 @@ import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; import java.util.HashSet; -import java.util.Optional; import java.util.Set; import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.iceberg.exceptions.ServiceFailureException; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; @@ -38,15 +37,13 @@ import org.slf4j.LoggerFactory; /** - * The default authenticator that resolves a {@link PrincipalAuthInfo} to an {@link - * AuthenticatedPolarisPrincipal}. + * The default {@link Authenticator}. * *

This authenticator is used in both internal and external authentication scenarios. */ @RequestScoped @Identifier("default") -public class DefaultAuthenticator - implements Authenticator { +public class DefaultAuthenticator implements Authenticator { public static final String PRINCIPAL_ROLE_ALL = "PRINCIPAL_ROLE:ALL"; public static final String PRINCIPAL_ROLE_PREFIX = "PRINCIPAL_ROLE:"; @@ -57,7 +54,7 @@ public class DefaultAuthenticator @Inject CallContext callContext; @Override - public Optional authenticate(PrincipalAuthInfo credentials) { + public PolarisPrincipal authenticate(PolarisCredential credentials) { LOGGER.debug("Resolving principal for credentials={}", credentials); PolarisMetaStoreManager metaStoreManager = metaStoreManagerFactory.getOrCreateMetaStoreManager(callContext.getRealmContext()); @@ -108,8 +105,6 @@ public Optional authenticate(PrincipalAuthInfo cr LOGGER.debug("Resolved principal: {}", principal); - AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal(new PrincipalEntity(principal), activatedPrincipalRoles); - return Optional.of(authenticatedPrincipal); + return PersistedPolarisPrincipal.of(new PrincipalEntity(principal), activatedPrincipalRoles); } } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/PersistedPolarisPrincipal.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/PersistedPolarisPrincipal.java new file mode 100644 index 0000000000..5597366645 --- /dev/null +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/PersistedPolarisPrincipal.java @@ -0,0 +1,66 @@ +/* + * 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.service.auth; + +import java.util.Map; +import java.util.Set; +import org.apache.polaris.core.auth.PolarisPrincipal; +import org.apache.polaris.core.entity.PrincipalEntity; +import org.apache.polaris.immutables.PolarisImmutable; +import org.immutables.value.Value; + +/** + * A persisted {@link PolarisPrincipal}, exposing the underlying {@link PrincipalEntity}. + * + *

Note: This class is intended for internal use within the Polaris authentication system. + */ +@PolarisImmutable +abstract class PersistedPolarisPrincipal implements PolarisPrincipal { + + /** + * Creates a new instance of {@link PersistedPolarisPrincipal} from the given {@link + * PrincipalEntity} and roles. + */ + static PersistedPolarisPrincipal of(PrincipalEntity principalEntity, Set principalRoles) { + return ImmutablePersistedPolarisPrincipal.builder() + .entity(principalEntity) + .roles(principalRoles) + .build(); + } + + abstract PrincipalEntity getEntity(); + + @Value.Derived + @Override + public String getName() { + return getEntity().getName(); + } + + @Value.Derived + @Override + public long getId() { + return getEntity().getId(); + } + + @Value.Lazy + @Override + public Map getProperties() { + return getEntity().getInternalPropertiesAsMap(); + } +} diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/PrincipalAuthInfo.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/PolarisCredential.java similarity index 67% rename from runtime/service/src/main/java/org/apache/polaris/service/auth/PrincipalAuthInfo.java rename to runtime/service/src/main/java/org/apache/polaris/service/auth/PolarisCredential.java index 05d298ef4e..26cb7b2f63 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/PrincipalAuthInfo.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/PolarisCredential.java @@ -21,19 +21,23 @@ import io.quarkus.security.credential.Credential; import jakarta.annotation.Nullable; import java.util.Set; +import org.apache.polaris.immutables.PolarisImmutable; /** - * Principal information extracted from authentication data (typically, an access token) by the - * configured authentication mechanism. Used to determine the principal id, name, and roles while - * authenticating a request. - * - *

This interface also implements Quarkus {@link Credential}, thus allowing it to be used as a - * {@linkplain io.quarkus.security.identity.SecurityIdentity#getCredential(Class) security identity - * credential}. - * - * @see DefaultAuthenticator + * A Quarkus Security {@link Credential} exposing Polaris-specific attributes: the principal id, + * name, and roles. */ -public interface PrincipalAuthInfo extends Credential { +@PolarisImmutable +public interface PolarisCredential extends Credential { + + static PolarisCredential of( + @Nullable Long principalId, @Nullable String principalName, Set principalRoles) { + return ImmutablePolarisCredential.builder() + .principalId(principalId) + .principalName(principalName) + .principalRoles(principalRoles) + .build(); + } /** The principal id, or null if unknown. Used for principal lookups by id. */ @Nullable @@ -44,7 +48,9 @@ public interface PrincipalAuthInfo extends Credential { String getPrincipalName(); /** - * The principal roles present in the token. The special {@link + * The principal roles present in the token. + * + *

When using the default authenticator, the special {@link * DefaultAuthenticator#PRINCIPAL_ROLE_ALL} can be used to denote a request for all principal * roles that the principal has access to. */ diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcConfiguration.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcConfiguration.java index ade5f8f221..47a5f3fa09 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcConfiguration.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcConfiguration.java @@ -25,6 +25,7 @@ import io.smallrye.config.WithParentName; import io.smallrye.config.WithUnnamedKey; import java.util.Map; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; import org.apache.polaris.service.auth.external.tenant.OidcTenantResolver; /** Polaris-specific configuration for OIDC tenants. */ diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentor.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentor.java similarity index 79% rename from runtime/service/src/main/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentor.java rename to runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentor.java index 5ef20210ca..30fe86cccd 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentor.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentor.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.getOidcTenantConfig; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.getOidcTenantConfig; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.SecurityIdentity; @@ -26,24 +26,24 @@ import io.quarkus.security.runtime.QuarkusSecurityIdentity; import io.smallrye.common.annotation.Identifier; import io.smallrye.mutiny.Uni; -import jakarta.annotation.Nullable; import jakarta.enterprise.context.ApplicationScoped; import jakarta.enterprise.inject.Any; import jakarta.enterprise.inject.Instance; import jakarta.inject.Inject; import java.util.Set; import org.apache.polaris.service.auth.AuthenticatingAugmentor; -import org.apache.polaris.service.auth.PrincipalAuthInfo; +import org.apache.polaris.service.auth.PolarisCredential; import org.apache.polaris.service.auth.external.mapping.PrincipalMapper; import org.apache.polaris.service.auth.external.mapping.PrincipalRolesMapper; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; import org.eclipse.microprofile.jwt.JsonWebToken; /** * A {@link SecurityIdentityAugmentor} that maps the access token claims, as provided by the OIDC - * authentication mechanism, to Polaris-specific properties (principal and principal roles). + * authentication mechanism, to a {@link PolarisCredential}. */ @ApplicationScoped -public class PrincipalAuthInfoAugmentor implements SecurityIdentityAugmentor { +public class OidcPolarisCredentialAugmentor implements SecurityIdentityAugmentor { // must run before the authenticating augmentor public static final int PRIORITY = AuthenticatingAugmentor.PRIORITY + 100; @@ -52,7 +52,7 @@ public class PrincipalAuthInfoAugmentor implements SecurityIdentityAugmentor { private final Instance principalRoleMappers; @Inject - public PrincipalAuthInfoAugmentor( + public OidcPolarisCredentialAugmentor( @Any Instance principalMappers, @Any Instance principalRoleMappers) { this.principalMappers = principalMappers; @@ -77,11 +77,12 @@ public Uni augment( principalRoleMappers .select(Identifier.Literal.of(config.principalRolesMapper().type())) .get(); - return Uni.createFrom() - .item(() -> setPrincipalAuthInfo(identity, principalMapper, principalRolesMapper)); + // The mappers may do expensive work, hence we run within a blocking context + return context.runBlocking( + () -> setPolarisCredential(identity, principalMapper, principalRolesMapper)); } - protected SecurityIdentity setPrincipalAuthInfo( + protected SecurityIdentity setPolarisCredential( SecurityIdentity identity, PrincipalMapper principalMapper, PrincipalRolesMapper rolesMapper) { @@ -89,15 +90,9 @@ protected SecurityIdentity setPrincipalAuthInfo( principalMapper.mapPrincipalId(identity).stream().boxed().findFirst().orElse(null); String principalName = principalMapper.mapPrincipalName(identity).orElse(null); Set principalRoles = rolesMapper.mapPrincipalRoles(identity); - var credential = new OidcPrincipalAuthInfo(principalId, principalName, principalRoles); + PolarisCredential credential = PolarisCredential.of(principalId, principalName, principalRoles); // Note: we don't change the identity roles here, this will be done later on - // by the ActiveRolesAugmentor, which will also validate them + // by the AuthenticatingAugmentor, which will also validate them. return QuarkusSecurityIdentity.builder(identity).addCredential(credential).build(); } - - protected record OidcPrincipalAuthInfo( - @Nullable Long getPrincipalId, - @Nullable String getPrincipalName, - Set getPrincipalRoles) - implements PrincipalAuthInfo {} } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapper.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapper.java index e213be6612..23f4821b1a 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapper.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapper.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external.mapping; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.getOidcTenantConfig; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.getOidcTenantConfig; import io.quarkus.security.identity.SecurityIdentity; import io.smallrye.common.annotation.Identifier; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapper.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapper.java index 103de2ee4d..9af58b7a74 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapper.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapper.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external.mapping; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.getOidcTenantConfig; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.getOidcTenantConfig; import io.quarkus.security.identity.SecurityIdentity; import io.smallrye.common.annotation.Identifier; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolver.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolver.java index 722e6cd602..7475ecc82c 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolver.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolver.java @@ -24,7 +24,6 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import org.apache.polaris.service.auth.external.OidcConfiguration; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantConfiguration.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantConfiguration.java similarity index 98% rename from runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantConfiguration.java rename to runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantConfiguration.java index f4fd620929..acf5adce2c 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantConfiguration.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantConfiguration.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.polaris.service.auth.external; +package org.apache.polaris.service.auth.external.tenant; import io.smallrye.config.WithDefault; import java.util.List; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolver.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolver.java index 0ea6e8b778..ffa296abe8 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolver.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolver.java @@ -19,7 +19,6 @@ package org.apache.polaris.service.auth.external.tenant; import io.quarkus.security.identity.SecurityIdentity; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; /** Resolves the Polaris OIDC tenant to use for the given {@link SecurityIdentity}. */ public interface OidcTenantResolver { diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentor.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolvingAugmentor.java similarity index 90% rename from runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentor.java rename to runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolvingAugmentor.java index fa5c05a0ec..8c22f8a44b 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentor.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/external/tenant/OidcTenantResolvingAugmentor.java @@ -16,7 +16,7 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.polaris.service.auth.external; +package org.apache.polaris.service.auth.external.tenant; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.SecurityIdentity; @@ -25,7 +25,7 @@ import io.smallrye.mutiny.Uni; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.polaris.service.auth.external.tenant.OidcTenantResolver; +import org.apache.polaris.service.auth.external.OidcPolarisCredentialAugmentor; import org.eclipse.microprofile.jwt.JsonWebToken; /** @@ -37,8 +37,8 @@ public class OidcTenantResolvingAugmentor implements SecurityIdentityAugmentor { public static final String TENANT_CONFIG_ATTRIBUTE = "polaris-tenant-config"; - // must run before PrincipalAuthInfoAugmentor - public static final int PRIORITY = PrincipalAuthInfoAugmentor.PRIORITY + 100; + // must run before OidcPolarisCredentialAugmentor + public static final int PRIORITY = OidcPolarisCredentialAugmentor.PRIORITY + 100; public static OidcTenantConfiguration getOidcTenantConfig(SecurityIdentity identity) { return identity.getAttribute(TENANT_CONFIG_ATTRIBUTE); diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanism.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanism.java index cba3e90ad9..f0e63efb1a 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanism.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanism.java @@ -22,7 +22,6 @@ import io.netty.handler.codec.http.HttpHeaderNames; import io.netty.handler.codec.http.HttpResponseStatus; import io.quarkus.security.AuthenticationFailedException; -import io.quarkus.security.credential.TokenCredential; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; import io.quarkus.security.identity.request.AuthenticationRequest; @@ -33,7 +32,6 @@ import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; -import jakarta.annotation.Nullable; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Collections; @@ -41,7 +39,6 @@ import org.apache.polaris.service.auth.AuthenticationRealmConfiguration; import org.apache.polaris.service.auth.AuthenticationType; import org.apache.polaris.service.auth.DecodedToken; -import org.apache.polaris.service.auth.PrincipalAuthInfo; import org.apache.polaris.service.auth.TokenBroker; /** @@ -108,8 +105,7 @@ public Uni authenticate( return identityProviderManager.authenticate( HttpSecurityUtils.setRoutingContextAttribute( - new TokenAuthenticationRequest(new InternalPrincipalAuthInfo(credential, token)), - context)); + new InternalAuthenticationRequest(token), context)); } @Override @@ -130,31 +126,4 @@ public Uni getCredentialTransport(RoutingContext contex return Uni.createFrom() .item(new HttpCredentialTransport(HttpCredentialTransport.Type.AUTHORIZATION, BEARER)); } - - static class InternalPrincipalAuthInfo extends TokenCredential implements PrincipalAuthInfo { - - private final DecodedToken token; - - InternalPrincipalAuthInfo(String credential, DecodedToken token) { - super(credential, "bearer"); - this.token = token; - } - - @Nullable - @Override - public Long getPrincipalId() { - return token.getPrincipalId(); - } - - @Nullable - @Override - public String getPrincipalName() { - return token.getPrincipalName(); - } - - @Override - public Set getPrincipalRoles() { - return token.getPrincipalRoles(); - } - } } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationRequest.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationRequest.java new file mode 100644 index 0000000000..50aad14264 --- /dev/null +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalAuthenticationRequest.java @@ -0,0 +1,36 @@ +/* + * 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.service.auth.internal; + +import io.quarkus.security.identity.request.BaseAuthenticationRequest; +import org.apache.polaris.service.auth.PolarisCredential; + +final class InternalAuthenticationRequest extends BaseAuthenticationRequest { + + private final PolarisCredential credential; + + InternalAuthenticationRequest(PolarisCredential credential) { + this.credential = credential; + } + + PolarisCredential getCredential() { + return credential; + } +} diff --git a/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalIdentityProvider.java b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalIdentityProvider.java index b6d5efd68d..2ed86e9b33 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalIdentityProvider.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/auth/internal/InternalIdentityProvider.java @@ -21,41 +21,33 @@ import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.IdentityProvider; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.identity.request.TokenAuthenticationRequest; +import io.quarkus.security.runtime.QuarkusPrincipal; import io.quarkus.security.runtime.QuarkusSecurityIdentity; import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; import jakarta.enterprise.context.ApplicationScoped; -import java.security.Principal; -/** A custom {@link IdentityProvider} that handles internal token authentication requests. */ +/** A custom {@link IdentityProvider} that handles {@link InternalAuthenticationRequest}s. */ @ApplicationScoped -class InternalIdentityProvider implements IdentityProvider { +class InternalIdentityProvider implements IdentityProvider { @Override - public Class getRequestType() { - return TokenAuthenticationRequest.class; + public Class getRequestType() { + return InternalAuthenticationRequest.class; } @Override public Uni authenticate( - TokenAuthenticationRequest request, AuthenticationRequestContext context) { - if (!(request.getToken() - instanceof InternalAuthenticationMechanism.InternalPrincipalAuthInfo credential)) { - return Uni.createFrom().nullItem(); - } - InternalTokenPrincipal principal = new InternalTokenPrincipal(credential.getPrincipalName()); + InternalAuthenticationRequest request, AuthenticationRequestContext context) { return Uni.createFrom() .item( QuarkusSecurityIdentity.builder() - .setPrincipal(principal) - .addCredential(credential) + .setPrincipal(new QuarkusPrincipal(request.getCredential().getPrincipalName())) + .addCredential(request.getCredential()) .addAttribute( RoutingContext.class.getName(), HttpSecurityUtils.getRoutingContextAttribute(request)) .build()); } - - private record InternalTokenPrincipal(String getName) implements Principal {} } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogAdapter.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogAdapter.java index c2b60fbff2..6fe9dcf48d 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogAdapter.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogAdapter.java @@ -24,7 +24,7 @@ import org.apache.iceberg.catalog.Namespace; import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.iceberg.rest.RESTUtil; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; /** * A common interface for adapters between the REST interface and {@link CatalogHandler} @@ -36,7 +36,7 @@ default Namespace decodeNamespace(String namespace) { } default void validatePrincipal(SecurityContext securityContext) { - var authenticatedPrincipal = (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + var authenticatedPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); if (authenticatedPrincipal == null) { throw new NotAuthorizedException("Failed to find authenticatedPrincipal in SecurityContext"); } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java index 6b2a8ae101..9cc6c93e83 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java @@ -31,9 +31,9 @@ import org.apache.iceberg.exceptions.NoSuchTableException; import org.apache.iceberg.exceptions.NoSuchViewException; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; 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.catalog.PolarisCatalogHelpers; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -60,7 +60,7 @@ public abstract class CatalogHandler { protected final PolarisAuthorizer authorizer; protected final CallContext callContext; - protected final AuthenticatedPolarisPrincipal authenticatedPrincipal; + protected final PolarisPrincipal polarisPrincipal; protected final SecurityContext securityContext; public CatalogHandler( @@ -76,12 +76,11 @@ public CatalogHandler( diagServices.checkNotNull(securityContext, "null_security_context"); diagServices.checkNotNull(securityContext.getUserPrincipal(), "null_user_principal"); diagServices.check( - securityContext.getUserPrincipal() instanceof AuthenticatedPolarisPrincipal, + securityContext.getUserPrincipal() instanceof PolarisPrincipal, "invalid_principal_type", - "Principal must be an AuthenticatedPolarisPrincipal"); + "Principal must be a PolarisPrincipal"); this.securityContext = securityContext; - this.authenticatedPrincipal = - (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); this.authorizer = authorizer; } @@ -142,7 +141,7 @@ protected void authorizeBasicNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -176,7 +175,7 @@ protected void authorizeCreateNamespaceUnderNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", parentNamespace); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -214,7 +213,7 @@ protected void authorizeCreateTableLikeUnderNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -243,7 +242,7 @@ protected void authorizeBasicTableLikeOperationOrThrow( throwNotFoundExceptionForTableLikeEntity(identifier, List.of(subType)); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -294,7 +293,7 @@ protected void authorizeCollectionOfTableLikeOperationOrThrow( "View does not exist: %s", identifier))) .toList(); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, targets, @@ -363,7 +362,7 @@ protected void authorizeRenameTableLikeOperationOrThrow( PolarisResolvedPathWrapper secondary = resolutionManifest.getResolvedPath(dst.namespace(), true); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java index 13767c7942..76401582a9 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogAdapter.java @@ -61,8 +61,8 @@ import org.apache.iceberg.rest.responses.ConfigResponse; import org.apache.iceberg.rest.responses.ImmutableLoadCredentialsResponse; import org.apache.iceberg.rest.responses.LoadTableResponse; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.ExternalCatalogFactory; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -774,8 +774,7 @@ public Response getConfig( // the catalog being accessed. // TODO: Push this down into PolarisCatalogHandlerWrapper for authorizing "any" catalog // role in this catalog. - AuthenticatedPolarisPrincipal authenticatedPrincipal = - (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); + PolarisPrincipal authenticatedPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal(); if (authenticatedPrincipal == null) { throw new NotAuthorizedException("Failed to find authenticatedPrincipal in SecurityContext"); } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java index 4fff89a5c8..c7705934c2 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java @@ -236,7 +236,7 @@ protected void initializeCatalog() { LOGGER.atInfo().log("Initializing non-federated catalog"); this.baseCatalog = catalogFactory.createCallContextCatalog( - callContext, authenticatedPrincipal, securityContext, resolutionManifest); + callContext, polarisPrincipal, securityContext, resolutionManifest); } this.namespaceCatalog = (baseCatalog instanceof SupportsNamespaces) ? (SupportsNamespaces) baseCatalog : null; diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java index 9421735998..54b72589b9 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java @@ -167,7 +167,7 @@ private void authorizeBasicPolicyOperationOrThrow( } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, target, @@ -211,7 +211,7 @@ private void authorizeBasicCatalogOperationOrThrow(PolarisAuthorizableOperation throw new NotFoundException("Catalog not found"); } authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, targetCatalog, @@ -270,7 +270,7 @@ private void authorizePolicyMappingOperationOrThrow( determinePolicyMappingOperation(target, targetWrapper, isAttach); authorizer.authorizeOrThrow( - authenticatedPrincipal, + polarisPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, policyWrapper, diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java index 9580fd5643..900960f08d 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java @@ -37,7 +37,6 @@ import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.config.PolarisConfigurationStore; @@ -62,7 +61,6 @@ import org.apache.polaris.service.auth.AuthenticationRealmConfiguration; import org.apache.polaris.service.auth.AuthenticationType; import org.apache.polaris.service.auth.Authenticator; -import org.apache.polaris.service.auth.PrincipalAuthInfo; import org.apache.polaris.service.auth.TokenBroker; import org.apache.polaris.service.auth.TokenBrokerFactory; import org.apache.polaris.service.auth.external.tenant.OidcTenantResolver; @@ -329,11 +327,8 @@ public TokenBucketFactory tokenBucketFactory( @Produces @RequestScoped - public Authenticator authenticator( - AuthenticationRealmConfiguration config, - @Any - Instance> - authenticators) { + public Authenticator authenticator( + AuthenticationRealmConfiguration config, @Any Instance authenticators) { return authenticators.select(Identifier.Literal.of(config.authenticator().type())).get(); } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/CallContextCatalogFactory.java b/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/CallContextCatalogFactory.java index 270c403c56..9e8f74e2b9 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/CallContextCatalogFactory.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/CallContextCatalogFactory.java @@ -20,7 +20,7 @@ import jakarta.ws.rs.core.SecurityContext; import org.apache.iceberg.catalog.Catalog; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.persistence.resolver.PolarisResolutionManifest; @@ -28,7 +28,7 @@ public interface CallContextCatalogFactory { Catalog createCallContextCatalog( CallContext context, - AuthenticatedPolarisPrincipal authenticatedPrincipal, + PolarisPrincipal polarisPrincipal, SecurityContext securityContext, PolarisResolutionManifest resolvedManifest); } diff --git a/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/PolarisCallContextCatalogFactory.java b/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/PolarisCallContextCatalogFactory.java index 16eb2edd3c..31360ae31d 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/PolarisCallContextCatalogFactory.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/context/catalog/PolarisCallContextCatalogFactory.java @@ -25,7 +25,7 @@ import java.util.Map; import org.apache.iceberg.CatalogProperties; import org.apache.iceberg.catalog.Catalog; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; @@ -71,7 +71,7 @@ public PolarisCallContextCatalogFactory( @Override public Catalog createCallContextCatalog( CallContext context, - AuthenticatedPolarisPrincipal authenticatedPrincipal, + PolarisPrincipal polarisPrincipal, SecurityContext securityContext, final PolarisResolutionManifest resolvedManifest) { PolarisBaseEntity baseCatalogEntity = 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 aa9dd57343..a620b92e4c 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 @@ -38,8 +38,8 @@ import org.apache.polaris.core.admin.model.PolarisCatalog; import org.apache.polaris.core.admin.model.StorageConfigInfo; import org.apache.polaris.core.admin.model.UpdateCatalogRequest; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; @@ -179,7 +179,7 @@ private PolarisAdminService setupPolarisAdminService( new SecurityContext() { @Override public Principal getUserPrincipal() { - return new AuthenticatedPolarisPrincipal( + return PolarisPrincipal.of( new PrincipalEntity.Builder() .setName(PolarisEntityConstants.getRootPrincipalName()) .build(), 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 e6d256eca1..9294d853fc 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,7 +29,7 @@ import org.apache.polaris.core.admin.model.UpdateCatalogRoleRequest; import org.apache.polaris.core.admin.model.UpdatePrincipalRequest; import org.apache.polaris.core.admin.model.UpdatePrincipalRoleRequest; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.CatalogRoleEntity; import org.apache.polaris.core.entity.PolarisPrivilege; @@ -46,14 +46,14 @@ private PolarisAdminService newTestAdminService() { } private PolarisAdminService newTestAdminService(Set activatedPrincipalRoles) { - final AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal(principalEntity, activatedPrincipalRoles); + final PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of(principalEntity, activatedPrincipalRoles); return new PolarisAdminService( callContext, resolutionManifestFactory, metaStoreManager, userSecretsManager, - securityContext(authenticatedPrincipal, activatedPrincipalRoles), + securityContext(authenticatedPrincipal), polarisAuthorizer, reservedProperties); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java index 24a2476bf1..136d6bf251 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisAdminServiceTest.java @@ -30,8 +30,8 @@ import org.apache.iceberg.exceptions.NotFoundException; import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; @@ -62,7 +62,7 @@ public class PolarisAdminServiceTest { @Mock private SecurityContext securityContext; @Mock private PolarisAuthorizer authorizer; @Mock private ReservedProperties reservedProperties; - @Mock private AuthenticatedPolarisPrincipal authenticatedPrincipal; + @Mock private PolarisPrincipal authenticatedPrincipal; @Mock private PolarisResolutionManifest resolutionManifest; @Mock private PolarisResolvedPathWrapper resolvedPathWrapper; 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 6aa54b111f..a714886e85 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 @@ -51,9 +51,9 @@ import org.apache.polaris.core.admin.model.PrincipalWithCredentials; import org.apache.polaris.core.admin.model.PrincipalWithCredentialsCredentials; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; 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.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; import org.apache.polaris.core.context.CallContext; @@ -203,10 +203,10 @@ public Map getConfigOverrides() { protected PrincipalEntity principalEntity; protected CallContext callContext; protected RealmConfig realmConfig; - protected AuthenticatedPolarisPrincipal authenticatedRoot; + protected PolarisPrincipal authenticatedRoot; protected PolarisAuthorizer polarisAuthorizer; - private PolarisCallContext polarisContext; + protected PolarisCallContext polarisContext; @BeforeAll public static void setUpMocks() { @@ -242,7 +242,7 @@ public void before(TestInfo testInfo) { PrincipalEntity rootPrincipal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); - this.authenticatedRoot = new AuthenticatedPolarisPrincipal(rootPrincipal, Set.of()); + this.authenticatedRoot = PolarisPrincipal.of(rootPrincipal, Set.of()); this.adminService = new PolarisAdminService( @@ -250,7 +250,7 @@ public void before(TestInfo testInfo) { resolutionManifestFactory, metaStoreManager, userSecretsManager, - securityContext(authenticatedRoot, Set.of()), + securityContext(authenticatedRoot), polarisAuthorizer, reservedProperties); @@ -383,8 +383,7 @@ protected static void assertSuccess(BaseResult result) { Assertions.assertThat(result.isSuccess()).isTrue(); } - protected @Nonnull SecurityContext securityContext( - AuthenticatedPolarisPrincipal p, Set roles) { + protected @Nonnull SecurityContext securityContext(PolarisPrincipal p) { SecurityContext securityContext = Mockito.mock(SecurityContext.class); Mockito.when(securityContext.getUserPrincipal()).thenReturn(p); Set principalRoleNames = loadPrincipalRolesNames(p); @@ -393,9 +392,14 @@ protected static void assertSuccess(BaseResult result) { return securityContext; } - protected @Nonnull Set loadPrincipalRolesNames(AuthenticatedPolarisPrincipal p) { + protected @Nonnull Set loadPrincipalRolesNames(PolarisPrincipal p) { + PolarisBaseEntity principal = + metaStoreManager + .loadEntity( + callContext.getPolarisCallContext(), 0L, p.getId(), PolarisEntityType.PRINCIPAL) + .getEntity(); return metaStoreManager - .loadGrantsToGrantee(callContext.getPolarisCallContext(), p.getPrincipalEntity()) + .loadGrantsToGrantee(callContext.getPolarisCallContext(), principal) .getGrantRecords() .stream() .filter(gr -> gr.getPrivilegeCode() == PolarisPrivilege.PRINCIPAL_ROLE_USAGE.getCode()) @@ -498,14 +502,14 @@ public TestPolarisCallContextCatalogFactory( @Override public Catalog createCallContextCatalog( CallContext context, - AuthenticatedPolarisPrincipal authenticatedPolarisPrincipal, + PolarisPrincipal polarisPrincipal, SecurityContext securityContext, final PolarisResolutionManifest resolvedManifest) { // This depends on the BasePolarisCatalog allowing calling initialize multiple times // to override the previous config. Catalog catalog = super.createCallContextCatalog( - context, authenticatedPolarisPrincipal, securityContext, resolvedManifest); + context, polarisPrincipal, securityContext, resolvedManifest); catalog.initialize( CATALOG_NAME, ImmutableMap.of( diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/ActiveRolesAugmentorTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/ActiveRolesAugmentorTest.java index a1a7402e66..932a9ff8c4 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/ActiveRolesAugmentorTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/ActiveRolesAugmentorTest.java @@ -29,7 +29,7 @@ import io.smallrye.mutiny.Uni; import java.security.Principal; import java.util.Set; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; @@ -78,7 +78,7 @@ public void testAugmentNonPolarisPrincipal() { public void testAugmentWithValidRoles(String rolesString) { // Given Set roles = Set.of(rolesString.split(",")); - AuthenticatedPolarisPrincipal polarisPrincipal = mock(AuthenticatedPolarisPrincipal.class); + PolarisPrincipal polarisPrincipal = mock(PolarisPrincipal.class); SecurityIdentity identity = QuarkusSecurityIdentity.builder().setPrincipal(polarisPrincipal).build(); @@ -98,7 +98,7 @@ public void testAugmentWithValidRoles(String rolesString) { public void testAugmentWithEmptyRoles() { // Given Set roles = Set.of(); - AuthenticatedPolarisPrincipal polarisPrincipal = mock(AuthenticatedPolarisPrincipal.class); + PolarisPrincipal polarisPrincipal = mock(PolarisPrincipal.class); SecurityIdentity identity = QuarkusSecurityIdentity.builder().setPrincipal(polarisPrincipal).build(); 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 382ad215e7..8166a1302e 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 @@ -28,19 +28,17 @@ import io.quarkus.security.runtime.QuarkusSecurityIdentity; import io.smallrye.mutiny.Uni; import java.security.Principal; -import java.util.Optional; import org.apache.iceberg.exceptions.NotAuthorizedException; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class AuthenticatingAugmentorTest { private AuthenticatingAugmentor augmentor; - private Authenticator authenticator; + private Authenticator authenticator; @BeforeEach - @SuppressWarnings("unchecked") public void setup() { authenticator = mock(Authenticator.class); augmentor = new AuthenticatingAugmentor(authenticator); @@ -77,27 +75,7 @@ public void testAugmentMissingCredential() { public void testAugmentAuthenticationFailure() { // Given Principal nonPolarisPrincipal = mock(Principal.class); - PrincipalAuthInfo credential = mock(PrincipalAuthInfo.class); - SecurityIdentity identity = - QuarkusSecurityIdentity.builder() - .setPrincipal(nonPolarisPrincipal) - .addCredential(credential) - .build(); - - when(authenticator.authenticate(credential)).thenReturn(Optional.empty()); - - // When/Then - assertThatThrownBy( - () -> augmentor.augment(identity, Uni.createFrom()::item).await().indefinitely()) - .isInstanceOf(AuthenticationFailedException.class) - .hasCauseInstanceOf(NotAuthorizedException.class); - } - - @Test - public void testAugmentRuntimeException() { - // Given - Principal nonPolarisPrincipal = mock(Principal.class); - PrincipalAuthInfo credential = mock(PrincipalAuthInfo.class); + PolarisCredential credential = mock(PolarisCredential.class); SecurityIdentity identity = QuarkusSecurityIdentity.builder() .setPrincipal(nonPolarisPrincipal) @@ -117,16 +95,16 @@ public void testAugmentRuntimeException() { @Test public void testAugmentSuccessfulAuthentication() { // Given - AuthenticatedPolarisPrincipal polarisPrincipal = mock(AuthenticatedPolarisPrincipal.class); + PolarisPrincipal polarisPrincipal = mock(PolarisPrincipal.class); when(polarisPrincipal.getName()).thenReturn("user1"); - PrincipalAuthInfo credential = mock(PrincipalAuthInfo.class); + PolarisCredential credential = mock(PolarisCredential.class); SecurityIdentity identity = QuarkusSecurityIdentity.builder() .setPrincipal(polarisPrincipal) .addCredential(credential) .build(); - when(authenticator.authenticate(credential)).thenReturn(Optional.of(polarisPrincipal)); + when(authenticator.authenticate(credential)).thenReturn(polarisPrincipal); // When SecurityIdentity result = diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticationConfigurationTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticationConfigurationTest.java index ffc7fb1893..c84ad6ba32 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticationConfigurationTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/AuthenticationConfigurationTest.java @@ -29,7 +29,7 @@ import jakarta.inject.Inject; import java.util.Map; import org.apache.polaris.service.auth.external.OidcConfiguration; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; import org.junit.jupiter.api.Test; @QuarkusTest @@ -46,19 +46,16 @@ void smokeTest() { assertThat(realms.get(DEFAULT_REALM_KEY).type()).isEqualTo(AuthenticationType.MIXED); assertThat(realms.get(DEFAULT_REALM_KEY).authenticator().type()).isEqualTo("custom"); - assertThat(realms.get(DEFAULT_REALM_KEY).activeRolesProvider().type()).isEqualTo("custom"); assertThat(realms.get(DEFAULT_REALM_KEY).tokenBroker().type()).isEqualTo("custom"); assertThat(realms.get(DEFAULT_REALM_KEY).tokenService().type()).isEqualTo("custom"); assertThat(realms.get("realm1").type()).isEqualTo(AuthenticationType.INTERNAL); assertThat(realms.get("realm1").authenticator().type()).isEqualTo("default"); - assertThat(realms.get("realm1").activeRolesProvider().type()).isEqualTo("default"); assertThat(realms.get("realm1").tokenBroker().type()).isEqualTo("default"); assertThat(realms.get("realm1").tokenService().type()).isEqualTo("default"); assertThat(realms.get("realm2").type()).isEqualTo(AuthenticationType.EXTERNAL); assertThat(realms.get("realm2").authenticator().type()).isEqualTo("default"); - assertThat(realms.get("realm2").activeRolesProvider().type()).isEqualTo("default"); assertThat(realms.get("realm2").tokenBroker().type()).isEqualTo("rsa-key-pair"); assertThat(realms.get("realm2").tokenService().type()).isEqualTo("default"); @@ -82,13 +79,11 @@ public Map getConfigOverrides() { // Default realm: mixed auth with custom impls .put("polaris.authentication.type", "mixed") .put("polaris.authentication.authenticator.type", "custom") - .put("polaris.authentication.active-roles-provider.type", "custom") .put("polaris.authentication.token-broker.type", "custom") .put("polaris.authentication.token-service.type", "custom") // realm1: internal auth with default impls .put("polaris.authentication.realm1.type", "internal") .put("polaris.authentication.realm1.authenticator.type", "default") - .put("polaris.authentication.realm1.active-roles-provider.type", "default") .put("polaris.authentication.realm1.token-broker.type", "default") .put("polaris.authentication.realm1.token-service.type", "default") // realm2: external auth diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentorTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentorTest.java similarity index 86% rename from runtime/service/src/test/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentorTest.java rename to runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentorTest.java index 26cce1ef4d..588ead41ca 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/PrincipalAuthInfoAugmentorTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcPolarisCredentialAugmentorTest.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -32,16 +32,17 @@ import java.util.Optional; import java.util.OptionalLong; import java.util.Set; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration.PrincipalMapper; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration.PrincipalRolesMapper; -import org.apache.polaris.service.auth.external.PrincipalAuthInfoAugmentor.OidcPrincipalAuthInfo; +import org.apache.polaris.service.auth.PolarisCredential; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration.PrincipalMapper; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration.PrincipalRolesMapper; import org.eclipse.microprofile.jwt.JsonWebToken; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -class PrincipalAuthInfoAugmentorTest { +class OidcPolarisCredentialAugmentorTest { - private PrincipalAuthInfoAugmentor augmentor; + private OidcPolarisCredentialAugmentor augmentor; private org.apache.polaris.service.auth.external.mapping.PrincipalMapper principalMapper; private org.apache.polaris.service.auth.external.mapping.PrincipalRolesMapper principalRolesMapper; @@ -68,7 +69,7 @@ public void setup() { when(principalRoleMappers.select(Identifier.Literal.of("default"))) .thenReturn(principalRoleMappers); when(principalRoleMappers.get()).thenReturn(principalRolesMapper); - augmentor = new PrincipalAuthInfoAugmentor(principalMappers, principalRoleMappers); + augmentor = new OidcPolarisCredentialAugmentor(principalMappers, principalRoleMappers); } @Test @@ -120,8 +121,8 @@ public void testAugmentOidcPrincipal() { // Then assertThat(result).isNotNull(); assertThat(result.getPrincipal()).isSameAs(oidcPrincipal); - assertThat(result.getCredential(OidcPrincipalAuthInfo.class)) - .isEqualTo(new OidcPrincipalAuthInfo(123L, "root", Set.of("MAPPED_ROLE1"))); + assertThat(result.getCredential(PolarisCredential.class)) + .isEqualTo(PolarisCredential.of(123L, "root", Set.of("MAPPED_ROLE1"))); // the identity roles should not change, since this is done by the ActiveRolesAugmentor assertThat(result.getRoles()).containsExactlyInAnyOrder("ROLE1"); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentorTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentorTest.java index b9bb336511..1668cc520c 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentorTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/OidcTenantResolvingAugmentorTest.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.getOidcTenantConfig; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.getOidcTenantConfig; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -27,7 +27,9 @@ import io.quarkus.security.runtime.QuarkusSecurityIdentity; import io.smallrye.mutiny.Uni; import java.security.Principal; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; import org.apache.polaris.service.auth.external.tenant.OidcTenantResolver; +import org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor; import org.eclipse.microprofile.jwt.JsonWebToken; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapperTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapperTest.java index ebcb2dc0de..537f2d9488 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapperTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalMapperTest.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external.mapping; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; @@ -30,8 +30,8 @@ import jakarta.enterprise.inject.Instance; import java.util.Optional; import java.util.stream.Stream; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration.PrincipalMapper; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration.PrincipalMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapperTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapperTest.java index 9214a13548..c016ed8eee 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapperTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/mapping/DefaultPrincipalRolesMapperTest.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.auth.external.mapping; -import static org.apache.polaris.service.auth.external.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; +import static org.apache.polaris.service.auth.external.tenant.OidcTenantResolvingAugmentor.TENANT_CONFIG_ATTRIBUTE; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; @@ -29,7 +29,7 @@ import java.util.Optional; import java.util.Set; import java.util.stream.Stream; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; +import org.apache.polaris.service.auth.external.tenant.OidcTenantConfiguration; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolverTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolverTest.java index 99e034e7a4..42c0b81c1d 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolverTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/external/tenant/DefaultOidcTenantResolverTest.java @@ -28,7 +28,6 @@ import java.util.HashMap; import java.util.Map; import org.apache.polaris.service.auth.external.OidcConfiguration; -import org.apache.polaris.service.auth.external.OidcTenantConfiguration; import org.eclipse.microprofile.jwt.JsonWebToken; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanismTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanismTest.java index 6322430283..1a87b38539 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanismTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalAuthenticationMechanismTest.java @@ -29,7 +29,6 @@ import io.quarkus.security.AuthenticationFailedException; import io.quarkus.security.identity.IdentityProviderManager; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.identity.request.TokenAuthenticationRequest; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; import org.apache.iceberg.exceptions.NotAuthorizedException; @@ -128,7 +127,7 @@ public void testAuthenticateWithInvalidTokenInternalAuth() { .isInstanceOf(AuthenticationFailedException.class) .hasCause(cause); verify(tokenBroker).verify("invalidToken"); - verify(identityProviderManager, never()).authenticate(any(TokenAuthenticationRequest.class)); + verify(identityProviderManager, never()).authenticate(any(InternalAuthenticationRequest.class)); } @Test @@ -148,7 +147,7 @@ public void testAuthenticateWithInvalidTokenMixedAuth() { assertThat(result.await().indefinitely()).isNull(); verify(tokenBroker).verify("invalidToken"); - verify(identityProviderManager, never()).authenticate(any(TokenAuthenticationRequest.class)); + verify(identityProviderManager, never()).authenticate(any(InternalAuthenticationRequest.class)); } @Test @@ -168,6 +167,6 @@ public void testAuthenticateWithValidToken() { assertThat(result.await().indefinitely()).isSameAs(securityIdentity); verify(tokenBroker).verify("validToken"); - verify(identityProviderManager).authenticate(any(TokenAuthenticationRequest.class)); + verify(identityProviderManager).authenticate(any(InternalAuthenticationRequest.class)); } } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalIdentityProviderTest.java b/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalIdentityProviderTest.java index 07ab294b55..f6412ec856 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalIdentityProviderTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/auth/internal/InternalIdentityProviderTest.java @@ -20,17 +20,15 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import io.quarkus.security.credential.TokenCredential; import io.quarkus.security.identity.AuthenticationRequestContext; import io.quarkus.security.identity.SecurityIdentity; -import io.quarkus.security.identity.request.TokenAuthenticationRequest; import io.quarkus.vertx.http.runtime.security.HttpSecurityUtils; import io.smallrye.mutiny.Uni; import io.vertx.ext.web.RoutingContext; import java.security.Principal; -import org.apache.polaris.service.auth.internal.InternalAuthenticationMechanism.InternalPrincipalAuthInfo; +import java.util.Set; +import org.apache.polaris.service.auth.PolarisCredential; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -45,25 +43,13 @@ public void setup() { context = mock(AuthenticationRequestContext.class); } - @Test - public void testAuthenticateWithWrongCredential() { - TokenCredential nonInternalCredential = mock(TokenCredential.class); - TokenAuthenticationRequest request = new TokenAuthenticationRequest(nonInternalCredential); - - Uni result = provider.authenticate(request, context); - - assertThat(result.await().indefinitely()).isNull(); - } - @Test public void testAuthenticateWithValidCredential() { - // Create a mock InternalPrincipalAuthInfo - InternalPrincipalAuthInfo credential = mock(InternalPrincipalAuthInfo.class); - when(credential.getPrincipalName()).thenReturn("testUser"); + PolarisCredential credential = PolarisCredential.of(1L, "testUser", Set.of()); + InternalAuthenticationRequest request = new InternalAuthenticationRequest(credential); // Create a request with the credential and a routing context attribute RoutingContext routingContext = mock(RoutingContext.class); - TokenAuthenticationRequest request = new TokenAuthenticationRequest(credential); HttpSecurityUtils.setRoutingContextAttribute(request, routingContext); // Authenticate the request @@ -79,7 +65,7 @@ public void testAuthenticateWithValidCredential() { assertThat(principal.getName()).isEqualTo("testUser"); // Verify the credential is set - assertThat(identity.getCredential(InternalPrincipalAuthInfo.class)).isSameAs(credential); + assertThat(identity.getCredential(PolarisCredential.class)).isSameAs(credential); // Verify the routing context attribute is set assertThat((RoutingContext) identity.getAttribute(RoutingContext.class.getName())) diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogTest.java index 4c69be2d7b..979e12d230 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogTest.java @@ -92,8 +92,8 @@ import org.apache.polaris.core.admin.model.CreateCatalogRequest; import org.apache.polaris.core.admin.model.StorageConfigInfo; import org.apache.polaris.core.admin.model.UpdateCatalogRequest; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; @@ -297,8 +297,7 @@ public void before(TestInfo testInfo) { PrincipalEntity rootPrincipal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); - AuthenticatedPolarisPrincipal authenticatedRoot = - new AuthenticatedPolarisPrincipal(rootPrincipal, Set.of()); + PolarisPrincipal authenticatedRoot = PolarisPrincipal.of(rootPrincipal, Set.of()); securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getUserPrincipal()).thenReturn(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogViewTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogViewTest.java index 260f834fd0..d14ccd84f9 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogViewTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractIcebergCatalogViewTest.java @@ -40,8 +40,8 @@ import org.apache.polaris.core.admin.model.CreateCatalogRequest; import org.apache.polaris.core.admin.model.FileStorageConfigInfo; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; @@ -166,8 +166,7 @@ public void before(TestInfo testInfo) { PrincipalEntity rootPrincipal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); - AuthenticatedPolarisPrincipal authenticatedRoot = - new AuthenticatedPolarisPrincipal(rootPrincipal, Set.of()); + PolarisPrincipal authenticatedRoot = PolarisPrincipal.of(rootPrincipal, Set.of()); SecurityContext securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getUserPrincipal()).thenReturn(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolarisGenericTableCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolarisGenericTableCatalogTest.java index 6f4d7c23d0..c4f3ce5d44 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolarisGenericTableCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolarisGenericTableCatalogTest.java @@ -41,8 +41,8 @@ import org.apache.polaris.core.admin.model.AwsStorageConfigInfo; import org.apache.polaris.core.admin.model.CreateCatalogRequest; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; @@ -114,7 +114,7 @@ public abstract class AbstractPolarisGenericTableCatalogTest { private RealmConfig realmConfig; private PolarisAdminService adminService; private FileIOFactory fileIOFactory; - private AuthenticatedPolarisPrincipal authenticatedRoot; + private PolarisPrincipal authenticatedRoot; private PolarisEntity catalogEntity; private SecurityContext securityContext; private ReservedProperties reservedProperties; @@ -158,7 +158,7 @@ public void before(TestInfo testInfo) { PrincipalEntity rootPrincipal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); - authenticatedRoot = new AuthenticatedPolarisPrincipal(rootPrincipal, Set.of()); + authenticatedRoot = PolarisPrincipal.of(rootPrincipal, Set.of()); securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getUserPrincipal()).thenReturn(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolicyCatalogTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolicyCatalogTest.java index 343ab6ce36..2b0d8d57f8 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolicyCatalogTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/AbstractPolicyCatalogTest.java @@ -48,8 +48,8 @@ import org.apache.polaris.core.admin.model.AwsStorageConfigInfo; import org.apache.polaris.core.admin.model.CreateCatalogRequest; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.config.RealmConfig; @@ -140,7 +140,7 @@ public abstract class AbstractPolicyCatalogTest { private RealmConfig realmConfig; private PolarisAdminService adminService; private FileIOFactory fileIOFactory; - private AuthenticatedPolarisPrincipal authenticatedRoot; + private PolarisPrincipal authenticatedRoot; private PolarisEntity catalogEntity; private SecurityContext securityContext; private ReservedProperties reservedProperties; @@ -179,7 +179,7 @@ public void before(TestInfo testInfo) { PrincipalEntity rootPrincipal = metaStoreManager.findRootPrincipal(polarisContext).orElseThrow(); - authenticatedRoot = new AuthenticatedPolarisPrincipal(rootPrincipal, Set.of()); + authenticatedRoot = PolarisPrincipal.of(rootPrincipal, Set.of()); securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getUserPrincipal()).thenReturn(authenticatedRoot); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/IcebergCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/IcebergCatalogHandlerAuthzTest.java index 3e0465b29f..a3ab18e3f4 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/IcebergCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/IcebergCatalogHandlerAuthzTest.java @@ -55,7 +55,7 @@ import org.apache.polaris.core.admin.model.FileStorageConfigInfo; import org.apache.polaris.core.admin.model.PrincipalWithCredentialsCredentials; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.ExternalCatalogFactory; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.CatalogEntity; @@ -99,14 +99,14 @@ private IcebergCatalogHandler newWrapper(Set activatedPrincipalRoles) { private IcebergCatalogHandler newWrapper( Set activatedPrincipalRoles, String catalogName, CallContextCatalogFactory factory) { - final AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal(principalEntity, activatedPrincipalRoles); + PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of(principalEntity, activatedPrincipalRoles); return new IcebergCatalogHandler( callContext, resolutionManifestFactory, metaStoreManager, userSecretsManager, - securityContext(authenticatedPrincipal, activatedPrincipalRoles), + securityContext(authenticatedPrincipal), factory, catalogName, polarisAuthorizer, @@ -235,8 +235,8 @@ public void testInsufficientPermissionsPriorToSecretRotation() { adminService.assignPrincipalRole(principalName, PRINCIPAL_ROLE1); adminService.assignPrincipalRole(principalName, PRINCIPAL_ROLE2); - final AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal( + PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of( PrincipalEntity.of(newPrincipal.getPrincipal()), Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)); IcebergCatalogHandler wrapper = @@ -245,7 +245,7 @@ public void testInsufficientPermissionsPriorToSecretRotation() { resolutionManifestFactory, metaStoreManager, userSecretsManager, - securityContext(authenticatedPrincipal, Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)), + securityContext(authenticatedPrincipal), callContextCatalogFactory, CATALOG_NAME, polarisAuthorizer, @@ -272,8 +272,8 @@ public void testInsufficientPermissionsPriorToSecretRotation() { PrincipalEntity refreshPrincipal = rotateAndRefreshPrincipal( metaStoreManager, principalName, credentials, callContext.getPolarisCallContext()); - final AuthenticatedPolarisPrincipal authenticatedPrincipal1 = - new AuthenticatedPolarisPrincipal( + PolarisPrincipal authenticatedPrincipal1 = + PolarisPrincipal.of( PrincipalEntity.of(refreshPrincipal), Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)); IcebergCatalogHandler refreshedWrapper = new IcebergCatalogHandler( @@ -281,7 +281,7 @@ public void testInsufficientPermissionsPriorToSecretRotation() { resolutionManifestFactory, metaStoreManager, userSecretsManager, - securityContext(authenticatedPrincipal1, Set.of(PRINCIPAL_ROLE1, PRINCIPAL_ROLE2)), + securityContext(authenticatedPrincipal1), callContextCatalogFactory, CATALOG_NAME, polarisAuthorizer, @@ -1771,12 +1771,12 @@ public void testSendNotificationSufficientPrivileges() { @Override public Catalog createCallContextCatalog( CallContext context, - AuthenticatedPolarisPrincipal authenticatedPolarisPrincipal, + PolarisPrincipal polarisPrincipal, SecurityContext securityContext, PolarisResolutionManifest resolvedManifest) { Catalog catalog = super.createCallContextCatalog( - context, authenticatedPolarisPrincipal, securityContext, resolvedManifest); + context, polarisPrincipal, securityContext, resolvedManifest); String fileIoImpl = "org.apache.iceberg.inmemory.InMemoryFileIO"; catalog.initialize( externalCatalog, ImmutableMap.of(CatalogProperties.FILE_IO_IMPL, fileIoImpl)); diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java index 715d68140d..f09bd1a783 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolarisGenericTableCatalogHandlerAuthzTest.java @@ -24,7 +24,7 @@ import java.util.Map; import java.util.Set; import org.apache.iceberg.catalog.TableIdentifier; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.service.admin.PolarisAuthzTestBase; import org.apache.polaris.service.catalog.generic.GenericTableCatalogHandler; @@ -44,13 +44,13 @@ private GenericTableCatalogHandler newWrapper(Set activatedPrincipalRole private GenericTableCatalogHandler newWrapper( Set activatedPrincipalRoles, String catalogName) { - final AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal(principalEntity, activatedPrincipalRoles); + PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of(principalEntity, activatedPrincipalRoles); return new GenericTableCatalogHandler( callContext, resolutionManifestFactory, metaStoreManager, - securityContext(authenticatedPrincipal, activatedPrincipalRoles), + securityContext(authenticatedPrincipal), catalogName, polarisAuthorizer); } diff --git a/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolicyCatalogHandlerAuthzTest.java b/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolicyCatalogHandlerAuthzTest.java index b955a6ec51..066bd54a36 100644 --- a/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolicyCatalogHandlerAuthzTest.java +++ b/runtime/service/src/test/java/org/apache/polaris/service/catalog/PolicyCatalogHandlerAuthzTest.java @@ -23,7 +23,7 @@ import java.util.Arrays; import java.util.List; import java.util.Set; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; import org.apache.polaris.core.entity.PolarisPrivilege; import org.apache.polaris.core.policy.PredefinedPolicyTypes; @@ -49,13 +49,13 @@ private PolicyCatalogHandler newWrapper(Set activatedPrincipalRoles) { } private PolicyCatalogHandler newWrapper(Set activatedPrincipalRoles, String catalogName) { - final AuthenticatedPolarisPrincipal authenticatedPrincipal = - new AuthenticatedPolarisPrincipal(principalEntity, activatedPrincipalRoles); + PolarisPrincipal authenticatedPrincipal = + PolarisPrincipal.of(principalEntity, activatedPrincipalRoles); return new PolicyCatalogHandler( callContext, resolutionManifestFactory, metaStoreManager, - securityContext(authenticatedPrincipal, activatedPrincipalRoles), + securityContext(authenticatedPrincipal), catalogName, polarisAuthorizer); } 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 e7288c441e..c3fafc7fc5 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 @@ -35,13 +35,12 @@ import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; +import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.catalog.ExternalCatalogFactory; import org.apache.polaris.core.config.PolarisConfigurationStore; import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; -import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PrincipalEntity; import org.apache.polaris.core.persistence.BasePersistence; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; @@ -249,9 +248,8 @@ public TestServices build() { .setCreateTimestamp(Instant.now().toEpochMilli()) .setCredentialRotationRequiredState() .build()); - AuthenticatedPolarisPrincipal principal = - new AuthenticatedPolarisPrincipal( - PolarisEntity.of(createdPrincipal.getPrincipal()), Set.of()); + PolarisPrincipal principal = + PolarisPrincipal.of(PrincipalEntity.of(createdPrincipal.getPrincipal()), Set.of()); SecurityContext securityContext = new SecurityContext() { diff --git a/site/content/in-dev/unreleased/external-idp.md b/site/content/in-dev/unreleased/external-idp.md index 0d969c21d6..4e79a71995 100644 --- a/site/content/in-dev/unreleased/external-idp.md +++ b/site/content/in-dev/unreleased/external-idp.md @@ -50,7 +50,7 @@ polaris.authentication.realm2.type=mixed ### Authenticator -The [`Authenticator`](https://github.com/apache/polaris/blob/main/service/common/src/main/java/org/apache/polaris/service/auth/Authenticator.java) is a component responsible for creating a Polaris principal from the credentials provided by the authentication process. It is common to all authentication types. +The [`Authenticator`](https://github.com/apache/polaris/blob/main/service/common/src/main/java/org/apache/polaris/service/auth/Authenticator.java) is a component responsible for resolving the principal and the principal roles, and for creating a `PolarisPrincipal` from the credentials provided by the authentication process. It is a central component and is invoked for all types of authentication. The `type` property is used to define the `Authenticator` implementation. It is overridable per realm: @@ -59,7 +59,7 @@ polaris.authentication.authenticator.type=default polaris.authentication.realm1.authenticator.type=custom ``` -### Active Roles Provider +### Active Roles Provider The [`ActiveRolesProvider`](https://github.com/apache/polaris/blob/main/service/common/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java) is a component responsible for determining which roles the principal is requesting and should be activated. It is common to all authentication types. @@ -71,7 +71,7 @@ polaris.authentication.active-roles-provider.type=default ## Internal Authentication Configuration -### Token Broker +### Token Broker The [`TokenBroker`](https://github.com/apache/polaris/blob/main/service/common/src/main/java/org/apache/polaris/service/auth/TokenBroker.java) signs and verifies tokens to ensure that they can be validated and remain unaltered. diff --git a/site/content/in-dev/unreleased/helm.md b/site/content/in-dev/unreleased/helm.md index 1cc84f6de0..0fc13f90e7 100644 --- a/site/content/in-dev/unreleased/helm.md +++ b/site/content/in-dev/unreleased/helm.md @@ -189,8 +189,7 @@ ct install --namespace polaris --charts ./helm/polaris |-----|------|---------|-------------| | advancedConfig | object | `{}` | Advanced configuration. You can pass here any valid Polaris or Quarkus configuration property. Any property that is defined here takes precedence over all the other configuration values generated by this chart. Properties can be passed "flattened" or as nested YAML objects (see examples below). Note: values should be strings; avoid using numbers, booleans, or other types. | | affinity | object | `{}` | Affinity and anti-affinity for polaris pods. See https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity. | -| authentication | object | `{"activeRolesProvider":{"type":"default"},"authenticator":{"type":"default"},"realmOverrides":{},"tokenBroker":{"maxTokenGeneration":"PT1H","secret":{"name":null,"privateKey":"private.pem","publicKey":"public.pem","rsaKeyPair":{"privateKey":"private.pem","publicKey":"public.pem"},"secretKey":"symmetric.pem","symmetricKey":{"secretKey":"symmetric.key"}},"type":"rsa-key-pair"},"tokenService":{"type":"default"},"type":"internal"}` | Polaris authentication configuration. | -| authentication.activeRolesProvider | object | `{"type":"default"}` | The `ActiveRolesProvider` implementation to use. Only one built-in type is supported: default. | +| authentication | object | `{"authenticator":{"type":"default"},"realmOverrides":{},"tokenBroker":{"maxTokenGeneration":"PT1H","secret":{"name":null,"privateKey":"private.pem","publicKey":"public.pem","rsaKeyPair":{"privateKey":"private.pem","publicKey":"public.pem"},"secretKey":"symmetric.pem","symmetricKey":{"secretKey":"symmetric.key"}},"type":"rsa-key-pair"},"tokenService":{"type":"default"},"type":"internal"}` | Polaris authentication configuration. | | authentication.authenticator | object | `{"type":"default"}` | The `Authenticator` implementation to use. Only one built-in type is supported: default. | | authentication.realmOverrides | object | `{}` | Authentication configuration overrides per realm. | | authentication.tokenBroker | object | `{"maxTokenGeneration":"PT1H","secret":{"name":null,"privateKey":"private.pem","publicKey":"public.pem","rsaKeyPair":{"privateKey":"private.pem","publicKey":"public.pem"},"secretKey":"symmetric.pem","symmetricKey":{"secretKey":"symmetric.key"}},"type":"rsa-key-pair"}` | The `TokenBroker` implementation to use. Two built-in types are supported: rsa-key-pair and symmetric-key. Only relevant when using internal (or mixed) authentication. When using external authentication, the token broker is not used. | @@ -299,7 +298,7 @@ ct install --namespace polaris --charts ./helm/polaris | oidc.principalMapper.type | string | `"default"` | The `PrincipalMapper` implementation to use. Only one built-in type is supported: default. | | oidc.principalRolesMapper | object | `{"filter":null,"mappings":[],"rolesClaimPath":null,"type":"default"}` | Principal roles mapping configuration. | | oidc.principalRolesMapper.filter | string | `nil` | A regular expression that matches the role names in the identity. Only roles that match this regex will be included in the Polaris-specific roles. | -| oidc.principalRolesMapper.mappings | list | `[]` | A list of regex mappings that will be applied to each role name in the identity. This can be used to transform the role names in the identity into role names as expected by Polaris. The default ActiveRolesProvider expects the security identity to expose role names in the format `POLARIS_ROLE:`. | +| oidc.principalRolesMapper.mappings | list | `[]` | A list of regex mappings that will be applied to each role name in the identity. This can be used to transform the role names in the identity into role names as expected by Polaris. The default Authenticator expects the security identity to expose role names in the format `POLARIS_ROLE:`. | | oidc.principalRolesMapper.rolesClaimPath | string | `nil` | The path to the claim that contains the principal roles. Nested paths can be expressed using "/" as a separator, e.g. "polaris/principal_roles" would look for the "principal_roles" field inside the "polaris" object in the token claims. If not set, Quarkus looks for roles in standard locations. See https://quarkus.io/guides/security-oidc-bearer-token-authentication#token-claims-and-security-identity-roles. | | oidc.principalRolesMapper.type | string | `"default"` | The `PrincipalRolesMapper` implementation to use. Only one built-in type is supported: default. | | persistence | object | `{"relationalJdbc":{"secret":{"jdbcUrl":"jdbcUrl","name":null,"password":"password","username":"username"}},"type":"in-memory"}` | Polaris persistence configuration. |