flyingImer commented on code in PR #3852:
URL: https://github.com/apache/polaris/pull/3852#discussion_r2849916099


##########
polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityConstants.java:
##########
@@ -43,6 +43,9 @@ public class PolarisEntityConstants {
   // the name of the principal role we create to manage the entire Polaris 
service
   private static final String ADMIN_PRINCIPAL_ROLE_NAME = "service_admin";
 
+  // the name of the principal role for catalog admins to list principal roles
+  private static final String CATALOG_ROLE_MANAGER_PRINCIPAL_ROLE_NAME = 
"catalog_role_manager";

Review Comment:
   should it be protected from being droppable?



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -2335,4 +2354,195 @@ private static PolarisEntitySubType 
selectEntitySubType(List<PolarisEntitySubTyp
               subTypes));
     }
   }
+
+  /**
+   * Grants the catalog_role_manager principal role to all principals assigned 
to the specified
+   * principal role. This allows catalog admins to list principal roles.
+   */
+  private void grantCatalogRoleManagerIfNeeded(PrincipalRoleEntity 
principalRoleEntity) {
+    // Load catalog_role_manager directly from metastore
+    EntityResult catalogRoleManagerResult =
+        metaStoreManager.readEntityByName(
+            getCurrentPolarisContext(),
+            null,
+            PolarisEntityType.PRINCIPAL_ROLE,
+            PolarisEntitySubType.NULL_SUBTYPE,
+            PolarisEntityConstants.getNameOfCatalogRoleManagerPrincipalRole());
+
+    if (!catalogRoleManagerResult.isSuccess() || 
catalogRoleManagerResult.getEntity() == null) {
+      return;

Review Comment:
   2 cents: log something when nothing found. This should be helpful to capture 
the missing catalog_role_manager role, as currently it is created only during 
bootstrap IIUC, i.e., we don't have upgrade/backfill path for already 
bootstrapped realms. I'm fine with temporary gaps for upgrade and backfill, but 
strongly recommend at least to warn on role not found



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -1534,8 +1544,17 @@ public PrivilegeResult 
revokeCatalogRoleFromPrincipalRole(
         getPrincipalRoleByName(resolutionManifest, principalRoleName);
     CatalogEntity catalogEntity = getCatalogByName(resolutionManifest, 
catalogName);
     CatalogRoleEntity catalogRoleEntity = 
getCatalogRoleByName(resolutionManifest, catalogRoleName);
-    return metaStoreManager.revokeUsageOnRoleFromGrantee(
-        getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+    PrivilegeResult result =
+        metaStoreManager.revokeUsageOnRoleFromGrantee(
+            getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+
+    // if revoking catalog_admin, check if principal still has catalog_admin 
on other catalogs
+    if (result.isSuccess()
+        && 
PolarisEntityConstants.getNameOfCatalogAdminRole().equals(catalogRoleName)) {
+      revokeCatalogRoleManagerIfNeeded(principalRoleEntity);

Review Comment:
   I see the `catalog_role_manager` is revoked only via 
`revokeCatalogRoleFromPrincipalRole`, but `catalog_role_manager` is granted to 
principal (not principal roles). Say when a principal is unassigned from a 
principal role (i.e., revoke principal role from principal API), 
`catalog_role_manager` granted to the principal would be never revoked.
   
   I feel it should be cleaned up as well to avoid leaking PRINCIPAL_ROLE_LIST 
privilege



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -2335,4 +2354,195 @@ private static PolarisEntitySubType 
selectEntitySubType(List<PolarisEntitySubTyp
               subTypes));
     }
   }
+
+  /**
+   * Grants the catalog_role_manager principal role to all principals assigned 
to the specified
+   * principal role. This allows catalog admins to list principal roles.
+   */
+  private void grantCatalogRoleManagerIfNeeded(PrincipalRoleEntity 
principalRoleEntity) {
+    // Load catalog_role_manager directly from metastore
+    EntityResult catalogRoleManagerResult =
+        metaStoreManager.readEntityByName(
+            getCurrentPolarisContext(),
+            null,
+            PolarisEntityType.PRINCIPAL_ROLE,
+            PolarisEntitySubType.NULL_SUBTYPE,
+            PolarisEntityConstants.getNameOfCatalogRoleManagerPrincipalRole());
+
+    if (!catalogRoleManagerResult.isSuccess() || 
catalogRoleManagerResult.getEntity() == null) {
+      return;
+    }
+
+    PrincipalRoleEntity catalogRoleManagerEntity =
+        PrincipalRoleEntity.of(catalogRoleManagerResult.getEntity());
+
+    // Find all principals that have this principal role and grant 
catalog_role_manager to them
+    LoadGrantsResult grantsResult =
+        metaStoreManager.loadGrantsOnSecurable(getCurrentPolarisContext(), 
principalRoleEntity);
+
+    if (grantsResult.isSuccess()) {
+      for (PolarisGrantRecord grant : grantsResult.getGrantRecords()) {
+        // Check if this is a PRINCIPAL_ROLE_USAGE grant (principal using this 
role)
+        if (grant.getPrivilegeCode() == 
PolarisPrivilege.PRINCIPAL_ROLE_USAGE.getCode()) {
+          // Load the principal (grantee)
+          EntityResult principalResult =
+              metaStoreManager.loadEntity(
+                  getCurrentPolarisContext(),
+                  grant.getGranteeCatalogId(),
+                  grant.getGranteeId(),
+                  PolarisEntityType.PRINCIPAL);
+
+          if (principalResult.isSuccess() && principalResult.getEntity() != 
null) {
+            PrincipalEntity principal = 
PrincipalEntity.of(principalResult.getEntity());
+
+            // Check if the principal already has catalog_role_manager
+            LoadGrantsResult principalGrantsResult =
+                
metaStoreManager.loadGrantsToGrantee(getCurrentPolarisContext(), principal);
+
+            boolean alreadyHasCatalogRoleManager = false;
+            if (principalGrantsResult.isSuccess()) {
+              for (PolarisGrantRecord existingGrant : 
principalGrantsResult.getGrantRecords()) {
+                if (existingGrant.getSecurableId() == 
catalogRoleManagerEntity.getId()
+                    && existingGrant.getPrivilegeCode()
+                        == PolarisPrivilege.PRINCIPAL_ROLE_USAGE.getCode()) {
+                  alreadyHasCatalogRoleManager = true;
+                  break;
+                }
+              }
+            }
+
+            // Only grant if not already granted
+            if (!alreadyHasCatalogRoleManager) {
+              metaStoreManager.grantUsageOnRoleToGrantee(
+                  getCurrentPolarisContext(), null, catalogRoleManagerEntity, 
principal);
+            }
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * Revokes the catalog_role_manager principal role from all principals 
assigned to the specified
+   * principal role if they no longer have any catalog_admin grants.
+   */
+  private void revokeCatalogRoleManagerIfNeeded(PrincipalRoleEntity 
principalRoleEntity) {
+    // Load catalog_role_manager directly from metastore
+    EntityResult catalogRoleManagerResult =
+        metaStoreManager.readEntityByName(
+            getCurrentPolarisContext(),
+            null,
+            PolarisEntityType.PRINCIPAL_ROLE,
+            PolarisEntitySubType.NULL_SUBTYPE,
+            PolarisEntityConstants.getNameOfCatalogRoleManagerPrincipalRole());
+
+    if (!catalogRoleManagerResult.isSuccess() || 
catalogRoleManagerResult.getEntity() == null) {
+      return;

Review Comment:
   2 cents: log something when nothing found



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -1518,8 +1519,17 @@ public PrivilegeResult assignCatalogRoleToPrincipalRole(
     CatalogEntity catalogEntity = getCatalogByName(resolutionManifest, 
catalogName);
     CatalogRoleEntity catalogRoleEntity = 
getCatalogRoleByName(resolutionManifest, catalogRoleName);
 
-    return metaStoreManager.grantUsageOnRoleToGrantee(
-        getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+    PrivilegeResult result =
+        metaStoreManager.grantUsageOnRoleToGrantee(
+            getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+
+    // if granting catalog_admin, also grant catalog_role_manager to allow 
listing principal roles
+    if (result.isSuccess()
+        && 
PolarisEntityConstants.getNameOfCatalogAdminRole().equals(catalogRoleName)) {
+      grantCatalogRoleManagerIfNeeded(principalRoleEntity);

Review Comment:
   catalog role manager should be granted on catalog_admin grant to a role as 
well, i.e., inside `assignPrincipalRole`



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -1534,8 +1544,17 @@ public PrivilegeResult 
revokeCatalogRoleFromPrincipalRole(
         getPrincipalRoleByName(resolutionManifest, principalRoleName);
     CatalogEntity catalogEntity = getCatalogByName(resolutionManifest, 
catalogName);
     CatalogRoleEntity catalogRoleEntity = 
getCatalogRoleByName(resolutionManifest, catalogRoleName);
-    return metaStoreManager.revokeUsageOnRoleFromGrantee(
-        getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+    PrivilegeResult result =
+        metaStoreManager.revokeUsageOnRoleFromGrantee(
+            getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+
+    // if revoking catalog_admin, check if principal still has catalog_admin 
on other catalogs
+    if (result.isSuccess()
+        && 
PolarisEntityConstants.getNameOfCatalogAdminRole().equals(catalogRoleName)) {
+      revokeCatalogRoleManagerIfNeeded(principalRoleEntity);

Review Comment:
   Similarly, when catalog drops, it should be cleaned up



##########
runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java:
##########
@@ -1534,8 +1544,17 @@ public PrivilegeResult 
revokeCatalogRoleFromPrincipalRole(
         getPrincipalRoleByName(resolutionManifest, principalRoleName);
     CatalogEntity catalogEntity = getCatalogByName(resolutionManifest, 
catalogName);
     CatalogRoleEntity catalogRoleEntity = 
getCatalogRoleByName(resolutionManifest, catalogRoleName);
-    return metaStoreManager.revokeUsageOnRoleFromGrantee(
-        getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+    PrivilegeResult result =
+        metaStoreManager.revokeUsageOnRoleFromGrantee(
+            getCurrentPolarisContext(), catalogEntity, catalogRoleEntity, 
principalRoleEntity);
+
+    // if revoking catalog_admin, check if principal still has catalog_admin 
on other catalogs
+    if (result.isSuccess()
+        && 
PolarisEntityConstants.getNameOfCatalogAdminRole().equals(catalogRoleName)) {
+      revokeCatalogRoleManagerIfNeeded(principalRoleEntity);

Review Comment:
   we should have tests capturing them



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to