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(

Reply via email to