Skip to content

Commit 43ab107

Browse files
authored
Refactor PolarisPrincipal to contain generic attributes (#5085)
1 parent 65e27f0 commit 43ab107

29 files changed

Lines changed: 444 additions & 503 deletions

File tree

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,19 @@ request adding CHANGELOG notes for breaking (!) changes and possibly other secti
4848
are now always bootstrapped with the latest available schema version.
4949
- The `MaintenanceService.performMaintenance()` signature now requires an explicit `OptionalLong overrideRunId` argument to supersede the latest unfinished maintenance run.
5050
- Admin grant APIs now reject table-like privilege targets with an empty namespace. A table-like target without a namespace is considered invalid input.
51+
- `PolarisPrincipal` now carries a generic `Map<String, Object> attributes` bag instead of the
52+
previous `Map<String, String> properties` and `Optional<String> token` fields. Three well-known
53+
attribute keys are defined as constants on the interface:
54+
55+
- `PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY`: holds the `PrincipalEntity`;
56+
- `PolarisPrincipal.PRINCIPAL_ROLE_ALL_ATTRIBUTE_KEY`: holds a boolean indicating if the
57+
`PRINCIPAL_ROLE:ALL` pseudo-role is present;
58+
- `PolarisPrincipal.JWT_ATTRIBUTE_KEY`: holds the raw JWT string.
59+
60+
Use `PolarisPrincipal.getAttribute(key, type)` for type-safe access. The `Authenticator` interface
61+
now accepts a Quarkus `SecurityIdentity` instead of a `PolarisCredential`, and throws
62+
`AuthenticationFailedException` (mapped to HTTP 401) instead of Iceberg's
63+
`NotAuthorizedException`.
5164

5265
### New Features
5366
- Added GCS principal attribution for vended credentials (the GCP counterpart of AWS STS session tags). Set `GCS_PRINCIPAL_ATTRIBUTION_ENABLED=true` to activate; the feature flags `GCS_PRINCIPAL_ATTRIBUTION_WIF_AUDIENCE`, `GCS_PRINCIPAL_ATTRIBUTION_TOKEN_ISSUER`, and `GCS_PRINCIPAL_ATTRIBUTION_SIGNING_KEY_FILE` are then required (a missing value is a fatal configuration error). Also requires a `gcpServiceAccount` on the catalog StorageConfiguration. When enabled, credential vending chains a catalog-signed JWT through a Workload Identity Federation token exchange and service-account impersonation, so the Polaris principal appears in GCS Data Access audit logs (`serviceAccountDelegationInfo.principalSubject`) for any client. `GCS_PRINCIPAL_ATTRIBUTION_SIGNING_KEY_ID` sets the JWT `kid` for JWKS key rotation. Attribution is keyed per-principal in the credential cache; when disabled (default), GCP vending behaviour is unchanged.

extensions/auth/ranger/src/main/java/org/apache/polaris/extension/auth/ranger/utils/RangerUtils.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import org.apache.polaris.core.auth.PolarisPrincipal;
3131
import org.apache.polaris.core.entity.PolarisEntity;
3232
import org.apache.polaris.core.entity.PolarisEntityType;
33+
import org.apache.polaris.core.entity.PrincipalEntity;
3334
import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper;
3435
import org.apache.polaris.core.persistence.ResolvedPolarisEntity;
3536
import org.apache.ranger.authz.model.RangerAccessInfo;
@@ -127,10 +128,12 @@ private static Map<String, Object> getResourceAttributes(
127128
}
128129

129130
private static Map<String, Object> getUserAttributes(PolarisPrincipal principal) {
130-
Map<String, String> properties = principal.getProperties();
131+
Map<String, String> properties =
132+
principal
133+
.getAttribute(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, PrincipalEntity.class)
134+
.map(PrincipalEntity::getInternalPropertiesAsMap)
135+
.orElse(Collections.emptyMap());
131136

132-
return (properties == null || properties.isEmpty())
133-
? Collections.emptyMap()
134-
: new HashMap<>(properties);
137+
return properties.isEmpty() ? Collections.emptyMap() : new HashMap<>(properties);
135138
}
136139
}

integration-tests/src/main/java/org/apache/polaris/service/it/test/PolarisManagementServiceIntegrationTest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1028,7 +1028,7 @@ public void testCreatePrincipalAndRotateCredentials() {
10281028
.isNotNull()
10291029
.extracting(ErrorResponse::message)
10301030
.asString()
1031-
.contains("PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE");
1031+
.contains("must rotate credentials first");
10321032
}
10331033

10341034
// Now try to rotate using the principal's token.

polaris-core/src/main/java/org/apache/polaris/core/auth/AuthorizationPreConditions.java

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.apache.polaris.core.config.FeatureConfiguration;
2323
import org.apache.polaris.core.config.RealmConfig;
2424
import org.apache.polaris.core.entity.PolarisEntityConstants;
25+
import org.apache.polaris.core.entity.PrincipalEntity;
2526

2627
/**
2728
* Common pre-condition checks shared across authorizer implementations for credential-related
@@ -48,12 +49,21 @@ public static void checkCredentialRotationRequired(
4849
if (realmConfig.getConfig(
4950
FeatureConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING)
5051
&& authzOp != PolarisAuthorizableOperation.ROTATE_CREDENTIALS
51-
&& polarisPrincipal
52-
.getProperties()
53-
.containsKey(PolarisEntityConstants.PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE)) {
52+
&& mustRotateCredentials(polarisPrincipal)) {
5453
throw new ForbiddenException(
55-
"Principal '%s' is not authorized for op %s due to PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE",
54+
"Principal '%s' is not authorized for op %s because it must rotate credentials first",
5655
polarisPrincipal.getName(), authzOp);
5756
}
5857
}
58+
59+
private static boolean mustRotateCredentials(PolarisPrincipal principal) {
60+
return principal
61+
.getAttribute(PolarisPrincipal.PRINCIPAL_ENTITY_ATTRIBUTE_KEY, PrincipalEntity.class)
62+
.map(PrincipalEntity::getInternalPropertiesAsMap)
63+
.map(
64+
map ->
65+
map.containsKey(
66+
PolarisEntityConstants.PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_STATE))
67+
.orElse(false);
68+
}
5969
}

0 commit comments

Comments
 (0)