This is an automated email from the ASF dual-hosted git repository. collado pushed a commit to branch mcollado-authn-resolve-roles in repository https://gitbox.apache.org/repos/asf/polaris.git
commit 172e36cd6a50973b11fd098f32974f33ed4a792a Author: Michael Collado <[email protected]> AuthorDate: Tue Jan 7 14:40:02 2025 -0800 Refactored active role lookup into new interface --- .../service/dropwizard/PolarisApplication.java | 3 + ...olarisPrincipalRoleSecurityContextProvider.java | 70 +--------------------- .../polaris/service/auth/ActiveRolesProvider.java | 36 +++++++++++ .../service/auth/DefaultActiveRolesProvider.java | 65 ++++---------------- 4 files changed, 55 insertions(+), 119 deletions(-) diff --git a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/PolarisApplication.java b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/PolarisApplication.java index 90b46fcb..6e0bb298 100644 --- a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/PolarisApplication.java +++ b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/PolarisApplication.java @@ -107,7 +107,9 @@ import org.apache.polaris.service.admin.api.PolarisPrincipalRolesApi; import org.apache.polaris.service.admin.api.PolarisPrincipalRolesApiService; import org.apache.polaris.service.admin.api.PolarisPrincipalsApi; import org.apache.polaris.service.admin.api.PolarisPrincipalsApiService; +import org.apache.polaris.service.auth.ActiveRolesProvider; import org.apache.polaris.service.auth.Authenticator; +import org.apache.polaris.service.auth.DefaultActiveRolesProvider; import org.apache.polaris.service.catalog.IcebergCatalogAdapter; import org.apache.polaris.service.catalog.api.IcebergRestCatalogApi; import org.apache.polaris.service.catalog.api.IcebergRestCatalogApiService; @@ -314,6 +316,7 @@ public class PolarisApplication extends Application<PolarisApplicationConfig> { PolarisCatalogsApi.class, PolarisPrincipalsApi.class, PolarisPrincipalRolesApi.class); + bind(DefaultActiveRolesProvider.class).to(ActiveRolesProvider.class); bindAsContract(RealmEntityManagerFactory.class).in(Singleton.class); bind(PolarisCallContextCatalogFactory.class) .to(CallContextCatalogFactory.class) diff --git a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java index 9e288bf8..35d6517d 100644 --- a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java +++ b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java @@ -27,21 +27,9 @@ import jakarta.ws.rs.container.ContainerRequestFilter; import jakarta.ws.rs.core.SecurityContext; import java.io.IOException; import java.security.Principal; -import java.util.List; import java.util.Set; -import java.util.function.Predicate; -import java.util.stream.Collectors; -import org.apache.iceberg.exceptions.NotAuthorizedException; -import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; -import org.apache.polaris.core.auth.PolarisGrantManager; -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.PrincipalRoleEntity; -import org.apache.polaris.core.persistence.MetaStoreManagerFactory; -import org.apache.polaris.core.persistence.PolarisMetaStoreManager; -import org.apache.polaris.service.auth.BasePolarisAuthenticator; +import org.apache.polaris.service.auth.ActiveRolesProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,8 +38,7 @@ public class PolarisPrincipalRoleSecurityContextProvider implements ContainerReq private static final Logger LOGGER = LoggerFactory.getLogger(PolarisPrincipalRoleSecurityContextProvider.class); @Inject Provider<SecurityContext> securityContextProvider; - @Inject MetaStoreManagerFactory metaStoreManager; - @Inject Provider<PolarisGrantManager> polarisGrantManagerProvider; + @Inject ActiveRolesProvider activeRolesProvider; @Override public void filter(ContainerRequestContext requestContext) throws IOException { @@ -67,14 +54,7 @@ public class PolarisPrincipalRoleSecurityContextProvider implements ContainerReq public SecurityContext createSecurityContext( SecurityContext ctx, AuthenticatedPolarisPrincipal principal) { - RealmContext realmContext = CallContext.getCurrentContext().getRealmContext(); - List<PrincipalRoleEntity> activeRoles = - loadActivePrincipalRoles( - principal.getActivatedPrincipalRoleNames(), - principal.getPrincipalEntity(), - metaStoreManager.getOrCreateMetaStoreManager(realmContext)); - Set<String> validRoleNames = - activeRoles.stream().map(PrincipalRoleEntity::getName).collect(Collectors.toSet()); + Set<String> validRoleNames = activeRolesProvider.getActiveRoles(principal); return new SecurityContext() { @Override public Principal getUserPrincipal() { @@ -97,48 +77,4 @@ public class PolarisPrincipalRoleSecurityContextProvider implements ContainerReq } }; } - - protected List<PrincipalRoleEntity> loadActivePrincipalRoles( - Set<String> tokenRoles, PolarisEntity principal, PolarisMetaStoreManager metaStoreManager) { - PolarisCallContext polarisContext = CallContext.getCurrentContext().getPolarisCallContext(); - PolarisGrantManager.LoadGrantsResult principalGrantResults = - polarisGrantManagerProvider.get().loadGrantsToGrantee(polarisContext, principal); - polarisContext - .getDiagServices() - .check( - principalGrantResults.isSuccess(), - "Failed to resolve principal roles for principal name={} id={}", - principal.getName(), - principal.getId()); - if (!principalGrantResults.isSuccess()) { - LOGGER.warn( - "Failed to resolve principal roles for principal name={} id={}", - principal.getName(), - principal.getId()); - throw new NotAuthorizedException("Unable to authenticate"); - } - boolean allRoles = tokenRoles.contains(BasePolarisAuthenticator.PRINCIPAL_ROLE_ALL); - Predicate<PrincipalRoleEntity> includeRoleFilter = - allRoles ? r -> true : r -> tokenRoles.contains(r.getName()); - List<PrincipalRoleEntity> activeRoles = - principalGrantResults.getGrantRecords().stream() - .map( - gr -> - metaStoreManager.loadEntity( - polarisContext, gr.getSecurableCatalogId(), gr.getSecurableId())) - .filter(PolarisMetaStoreManager.EntityResult::isSuccess) - .map(PolarisMetaStoreManager.EntityResult::getEntity) - .map(PrincipalRoleEntity::of) - .filter(includeRoleFilter) - .toList(); - if (activeRoles.size() != principalGrantResults.getGrantRecords().size()) { - LOGGER - .atWarn() - .addKeyValue("principal", principal.getName()) - .addKeyValue("scopes", tokenRoles) - .addKeyValue("roles", activeRoles) - .log("Some principal roles were not found in the principal's grants"); - } - return activeRoles; - } } diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java b/service/common/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.java new file mode 100644 index 00000000..0da63e62 --- /dev/null +++ b/service/common/src/main/java/org/apache/polaris/service/auth/ActiveRolesProvider.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; + +import java.util.Set; +import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; + +/** + * Provides the active roles for a given principal. Implementations may rely on the active request + * or SecurityContext to determine the active roles. + */ +public interface ActiveRolesProvider { + /** + * Returns the active roles for the given principal. + * + * @param principal the currently authenticated principal + * @return the active roles + */ + Set<String> getActiveRoles(AuthenticatedPolarisPrincipal principal); +} diff --git a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java similarity index 67% copy from dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java copy to service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java index 9e288bf8..8706a7fd 100644 --- a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/auth/PolarisPrincipalRoleSecurityContextProvider.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java @@ -16,17 +16,10 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.polaris.service.dropwizard.auth; +package org.apache.polaris.service.auth; -import jakarta.annotation.Priority; import jakarta.inject.Inject; import jakarta.inject.Provider; -import jakarta.ws.rs.Priorities; -import jakarta.ws.rs.container.ContainerRequestContext; -import jakarta.ws.rs.container.ContainerRequestFilter; -import jakarta.ws.rs.core.SecurityContext; -import java.io.IOException; -import java.security.Principal; import java.util.List; import java.util.Set; import java.util.function.Predicate; @@ -41,61 +34,29 @@ import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PrincipalRoleEntity; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; -import org.apache.polaris.service.auth.BasePolarisAuthenticator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -@Priority(Priorities.AUTHENTICATION + 1) -public class PolarisPrincipalRoleSecurityContextProvider implements ContainerRequestFilter { - private static final Logger LOGGER = - LoggerFactory.getLogger(PolarisPrincipalRoleSecurityContextProvider.class); - @Inject Provider<SecurityContext> securityContextProvider; +/** + * 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. + */ +public class DefaultActiveRolesProvider implements ActiveRolesProvider { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultActiveRolesProvider.class); + @Inject Provider<RealmContext> realmContextProvider; @Inject MetaStoreManagerFactory metaStoreManager; @Inject Provider<PolarisGrantManager> polarisGrantManagerProvider; @Override - public void filter(ContainerRequestContext requestContext) throws IOException { - AuthenticatedPolarisPrincipal polarisPrincipal = - (AuthenticatedPolarisPrincipal) securityContextProvider.get().getUserPrincipal(); - if (polarisPrincipal == null) { - return; - } - SecurityContext securityContext = - createSecurityContext(requestContext.getSecurityContext(), polarisPrincipal); - requestContext.setSecurityContext(securityContext); - } - - public SecurityContext createSecurityContext( - SecurityContext ctx, AuthenticatedPolarisPrincipal principal) { - RealmContext realmContext = CallContext.getCurrentContext().getRealmContext(); + public Set<String> getActiveRoles(AuthenticatedPolarisPrincipal principal) { List<PrincipalRoleEntity> activeRoles = loadActivePrincipalRoles( principal.getActivatedPrincipalRoleNames(), principal.getPrincipalEntity(), - metaStoreManager.getOrCreateMetaStoreManager(realmContext)); - Set<String> validRoleNames = - activeRoles.stream().map(PrincipalRoleEntity::getName).collect(Collectors.toSet()); - return new SecurityContext() { - @Override - public Principal getUserPrincipal() { - return principal; - } - - @Override - public boolean isUserInRole(String role) { - return validRoleNames.contains(role); - } - - @Override - public boolean isSecure() { - return ctx.isSecure(); - } - - @Override - public String getAuthenticationScheme() { - return ctx.getAuthenticationScheme(); - } - }; + metaStoreManager.getOrCreateMetaStoreManager(realmContextProvider.get())); + return activeRoles.stream().map(PrincipalRoleEntity::getName).collect(Collectors.toSet()); } protected List<PrincipalRoleEntity> loadActivePrincipalRoles(
