Skip to content

Commit f934443

Browse files
authored
Prefer PolarisPrincipal over SecurityContext (#2932)
The general idea is that `SecurityContext` comes from `jakarta.ws.rs` and there is no reason for non-REST related classes to rely on those details. Instead, once preprocessing of a REST-request has inferred the `PolarisPrincipal` all inner/core code should rely on only that. Note that this simplifies a bunch of tests that had to create their own `SecurityContext` around the principal that they wanted to use, thus having to decide how to implement `isUserInRole` and the other methods.
1 parent 74d4198 commit f934443

35 files changed

+125
-251
lines changed

polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/PolarisResolutionManifest.java

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import com.google.common.collect.HashMultimap;
2222
import com.google.common.collect.Multimap;
2323
import jakarta.annotation.Nullable;
24-
import jakarta.ws.rs.core.SecurityContext;
2524
import java.util.ArrayList;
2625
import java.util.HashMap;
2726
import java.util.HashSet;
@@ -52,7 +51,7 @@ public class PolarisResolutionManifest implements PolarisResolutionManifestCatal
5251
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisResolutionManifest.class);
5352

5453
private final ResolverFactory resolverFactory;
55-
private final SecurityContext securityContext;
54+
private final PolarisPrincipal principal;
5655
private final RealmContext realmContext;
5756
private final String catalogName;
5857
private final Resolver primaryResolver;
@@ -73,20 +72,15 @@ public PolarisResolutionManifest(
7372
PolarisDiagnostics diagnostics,
7473
RealmContext realmContext,
7574
ResolverFactory resolverFactory,
76-
SecurityContext securityContext,
75+
PolarisPrincipal principal,
7776
String catalogName) {
7877
this.realmContext = realmContext;
7978
this.resolverFactory = resolverFactory;
8079
this.catalogName = catalogName;
81-
this.primaryResolver = resolverFactory.createResolver(securityContext, catalogName);
8280
this.diagnostics = diagnostics;
83-
this.diagnostics.checkNotNull(securityContext, "null_security_context_for_resolution_manifest");
84-
this.securityContext = securityContext;
85-
diagnostics.check(
86-
securityContext.getUserPrincipal() instanceof PolarisPrincipal,
87-
"invalid_principal_type_for_resolution_manifest",
88-
"principal={}",
89-
securityContext.getUserPrincipal());
81+
this.diagnostics.checkNotNull(principal, "null_principal_for_resolution_manifest");
82+
this.principal = principal;
83+
this.primaryResolver = resolverFactory.createResolver(principal, catalogName);
9084

9185
// TODO: Make the rootContainer lookup no longer optional in the persistence store.
9286
// For now, we'll try to resolve the rootContainer as "optional", and only if we fail to find
@@ -186,7 +180,7 @@ public PolarisResolvedPathWrapper getPassthroughResolvedPath(Object key) {
186180
ResolverPath requestedPath = passthroughPaths.get(key);
187181

188182
// Run a single-use Resolver for this path.
189-
Resolver passthroughResolver = resolverFactory.createResolver(securityContext, catalogName);
183+
Resolver passthroughResolver = resolverFactory.createResolver(principal, catalogName);
190184
passthroughResolver.addPath(requestedPath);
191185
ResolverStatus status = passthroughResolver.resolveAll();
192186

polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/ResolutionManifestFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@
2121

2222
import jakarta.annotation.Nonnull;
2323
import jakarta.annotation.Nullable;
24-
import jakarta.ws.rs.core.SecurityContext;
24+
import org.apache.polaris.core.auth.PolarisPrincipal;
2525

2626
public interface ResolutionManifestFactory {
2727

2828
@Nonnull
2929
PolarisResolutionManifest createResolutionManifest(
30-
@Nonnull SecurityContext securityContext, @Nullable String referenceCatalogName);
30+
@Nonnull PolarisPrincipal principal, @Nullable String referenceCatalogName);
3131
}

polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/ResolutionManifestFactoryImpl.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@
2121

2222
import jakarta.annotation.Nonnull;
2323
import jakarta.annotation.Nullable;
24-
import jakarta.ws.rs.core.SecurityContext;
2524
import org.apache.polaris.core.PolarisDiagnostics;
25+
import org.apache.polaris.core.auth.PolarisPrincipal;
2626
import org.apache.polaris.core.context.RealmContext;
2727

2828
public class ResolutionManifestFactoryImpl implements ResolutionManifestFactory {
@@ -43,8 +43,8 @@ public ResolutionManifestFactoryImpl(
4343
@Nonnull
4444
@Override
4545
public PolarisResolutionManifest createResolutionManifest(
46-
@Nonnull SecurityContext securityContext, @Nullable String referenceCatalogName) {
46+
@Nonnull PolarisPrincipal principal, @Nullable String referenceCatalogName) {
4747
return new PolarisResolutionManifest(
48-
diagnostics, realmContext, resolverFactory, securityContext, referenceCatalogName);
48+
diagnostics, realmContext, resolverFactory, principal, referenceCatalogName);
4949
}
5050
}

polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/Resolver.java

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@
2020

2121
import jakarta.annotation.Nonnull;
2222
import jakarta.annotation.Nullable;
23-
import jakarta.ws.rs.core.SecurityContext;
2423
import java.util.AbstractSet;
2524
import java.util.ArrayList;
2625
import java.util.HashMap;
@@ -116,7 +115,7 @@ public class Resolver {
116115
*
117116
* @param polarisCallContext the polaris call context
118117
* @param polarisMetaStoreManager meta store manager
119-
* @param securityContext The {@link SecurityContext} for the current request
118+
* @param principal The {@link PolarisPrincipal} for the current request
120119
* @param cache shared entity cache
121120
* @param referenceCatalogName if not null, specifies the name of the reference catalog. The
122121
* reference catalog is the catalog used to resolve catalog roles and catalog path. Also, if a
@@ -130,7 +129,7 @@ public Resolver(
130129
@Nonnull PolarisDiagnostics diagnostics,
131130
@Nonnull PolarisCallContext polarisCallContext,
132131
@Nonnull PolarisMetaStoreManager polarisMetaStoreManager,
133-
@Nonnull SecurityContext securityContext,
132+
@Nonnull PolarisPrincipal principal,
134133
@Nullable EntityCache cache,
135134
@Nullable String referenceCatalogName) {
136135
this.polarisCallContext = polarisCallContext;
@@ -143,16 +142,9 @@ public Resolver(
143142
this.diagnostics.checkNotNull(polarisCallContext, "unexpected_null_polarisCallContext");
144143
this.diagnostics.checkNotNull(
145144
polarisMetaStoreManager, "unexpected_null_polarisMetaStoreManager");
146-
this.diagnostics.checkNotNull(securityContext, "security_context_must_be_specified");
147-
this.diagnostics.checkNotNull(
148-
securityContext.getUserPrincipal(), "principal_must_be_specified");
149-
this.diagnostics.check(
150-
securityContext.getUserPrincipal() instanceof PolarisPrincipal,
151-
"unexpected_principal_type",
152-
"class={}",
153-
securityContext.getUserPrincipal().getClass().getName());
145+
this.diagnostics.checkNotNull(principal, "principal_must_be_specified");
154146

155-
this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal();
147+
this.polarisPrincipal = principal;
156148
// paths to resolve
157149
this.pathsToResolve = new ArrayList<>();
158150
this.resolvedPaths = new ArrayList<>();

polaris-core/src/main/java/org/apache/polaris/core/persistence/resolver/ResolverFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,9 @@
2121

2222
import jakarta.annotation.Nonnull;
2323
import jakarta.annotation.Nullable;
24-
import jakarta.ws.rs.core.SecurityContext;
24+
import org.apache.polaris.core.auth.PolarisPrincipal;
2525

2626
public interface ResolverFactory {
2727
Resolver createResolver(
28-
@Nonnull SecurityContext securityContext, @Nullable String referenceCatalogName);
28+
@Nonnull PolarisPrincipal principal, @Nullable String referenceCatalogName);
2929
}

polaris-core/src/testFixtures/java/org/apache/polaris/core/persistence/BaseResolverTest.java

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@
2020

2121
import jakarta.annotation.Nonnull;
2222
import jakarta.annotation.Nullable;
23-
import jakarta.ws.rs.core.SecurityContext;
24-
import java.security.Principal;
2523
import java.util.ArrayList;
2624
import java.util.Comparator;
2725
import java.util.Iterator;
@@ -442,29 +440,7 @@ private Resolver allocateResolver(
442440
diagServices,
443441
callCtx(),
444442
metaStoreManager(),
445-
new SecurityContext() {
446-
@Override
447-
public Principal getUserPrincipal() {
448-
return authenticatedPrincipal;
449-
}
450-
451-
@Override
452-
public boolean isUserInRole(String role) {
453-
return roleEntities
454-
.map(l -> l.stream().map(PrincipalRoleEntity::getName).anyMatch(role::equals))
455-
.orElse(allRoles);
456-
}
457-
458-
@Override
459-
public boolean isSecure() {
460-
return false;
461-
}
462-
463-
@Override
464-
public String getAuthenticationScheme() {
465-
return "";
466-
}
467-
},
443+
authenticatedPrincipal,
468444
this.shouldUseCache ? this.cache : null,
469445
referenceCatalogName);
470446
}

runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import jakarta.annotation.Nullable;
2727
import jakarta.enterprise.context.RequestScoped;
2828
import jakarta.inject.Inject;
29-
import jakarta.ws.rs.core.SecurityContext;
3029
import java.util.ArrayList;
3130
import java.util.Arrays;
3231
import java.util.HashMap;
@@ -47,7 +46,6 @@
4746
import org.apache.iceberg.exceptions.NotFoundException;
4847
import org.apache.iceberg.exceptions.ValidationException;
4948
import org.apache.polaris.core.PolarisCallContext;
50-
import org.apache.polaris.core.PolarisDiagnostics;
5149
import org.apache.polaris.core.admin.model.AuthenticationParameters;
5250
import org.apache.polaris.core.admin.model.BearerAuthenticationParameters;
5351
import org.apache.polaris.core.admin.model.Catalog;
@@ -146,7 +144,6 @@ public class PolarisAdminService {
146144
private final CallContext callContext;
147145
private final RealmConfig realmConfig;
148146
private final ResolutionManifestFactory resolutionManifestFactory;
149-
private final SecurityContext securityContext;
150147
private final PolarisPrincipal polarisPrincipal;
151148
private final PolarisAuthorizer authorizer;
152149
private final PolarisMetaStoreManager metaStoreManager;
@@ -156,28 +153,19 @@ public class PolarisAdminService {
156153

157154
@Inject
158155
public PolarisAdminService(
159-
@Nonnull PolarisDiagnostics diagnostics,
160156
@Nonnull CallContext callContext,
161157
@Nonnull ResolutionManifestFactory resolutionManifestFactory,
162158
@Nonnull PolarisMetaStoreManager metaStoreManager,
163159
@Nonnull UserSecretsManager userSecretsManager,
164160
@Nonnull ServiceIdentityProvider serviceIdentityProvider,
165-
@Nonnull SecurityContext securityContext,
161+
@Nonnull PolarisPrincipal principal,
166162
@Nonnull PolarisAuthorizer authorizer,
167163
@Nonnull ReservedProperties reservedProperties) {
168164
this.callContext = callContext;
169165
this.realmConfig = callContext.getRealmConfig();
170166
this.resolutionManifestFactory = resolutionManifestFactory;
171167
this.metaStoreManager = metaStoreManager;
172-
this.securityContext = securityContext;
173-
diagnostics.checkNotNull(securityContext, "null_security_context");
174-
diagnostics.checkNotNull(securityContext.getUserPrincipal(), "null_security_context");
175-
diagnostics.check(
176-
securityContext.getUserPrincipal() instanceof PolarisPrincipal,
177-
"unexpected_principal_type",
178-
"class={}",
179-
securityContext.getUserPrincipal().getClass().getName());
180-
this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal();
168+
this.polarisPrincipal = principal;
181169
this.authorizer = authorizer;
182170
this.userSecretsManager = userSecretsManager;
183171
this.serviceIdentityProvider = serviceIdentityProvider;
@@ -197,7 +185,7 @@ private ServiceIdentityProvider getServiceIdentityProvider() {
197185
}
198186

199187
private PolarisResolutionManifest newResolutionManifest(@Nullable String catalogName) {
200-
return resolutionManifestFactory.createResolutionManifest(securityContext, catalogName);
188+
return resolutionManifestFactory.createResolutionManifest(polarisPrincipal, catalogName);
201189
}
202190

203191
private static PrincipalEntity getPrincipalByName(

runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogAdapter.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,11 @@ default Namespace decodeNamespace(String namespace) {
3535
return RESTUtil.decodeNamespace(URLEncoder.encode(namespace, Charset.defaultCharset()));
3636
}
3737

38-
default void validatePrincipal(SecurityContext securityContext) {
39-
var authenticatedPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal();
40-
if (authenticatedPrincipal == null) {
41-
throw new NotAuthorizedException("Failed to find authenticatedPrincipal in SecurityContext");
38+
default PolarisPrincipal validatePrincipal(SecurityContext securityContext) {
39+
var authenticatedPrincipal = securityContext.getUserPrincipal();
40+
if (authenticatedPrincipal instanceof PolarisPrincipal polarisPrincipal) {
41+
return polarisPrincipal;
4242
}
43+
throw new NotAuthorizedException("Failed to find authenticatedPrincipal in SecurityContext");
4344
}
4445
}

runtime/service/src/main/java/org/apache/polaris/service/catalog/common/CatalogHandler.java

Lines changed: 3 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
import static org.apache.polaris.core.entity.PolarisEntitySubType.ICEBERG_TABLE;
2222

2323
import jakarta.enterprise.inject.Instance;
24-
import jakarta.ws.rs.core.SecurityContext;
2524
import java.util.Arrays;
2625
import java.util.EnumSet;
2726
import java.util.List;
@@ -70,13 +69,12 @@ public abstract class CatalogHandler {
7069
protected final CallContext callContext;
7170
protected final RealmConfig realmConfig;
7271
protected final PolarisPrincipal polarisPrincipal;
73-
protected final SecurityContext securityContext;
7472

7573
public CatalogHandler(
7674
PolarisDiagnostics diagnostics,
7775
CallContext callContext,
7876
ResolutionManifestFactory resolutionManifestFactory,
79-
SecurityContext securityContext,
77+
PolarisPrincipal principal,
8078
String catalogName,
8179
PolarisAuthorizer authorizer,
8280
PolarisCredentialManager credentialManager,
@@ -86,14 +84,7 @@ public CatalogHandler(
8684
this.realmConfig = callContext.getRealmConfig();
8785
this.resolutionManifestFactory = resolutionManifestFactory;
8886
this.catalogName = catalogName;
89-
diagnostics.checkNotNull(securityContext, "null_security_context");
90-
diagnostics.checkNotNull(securityContext.getUserPrincipal(), "null_user_principal");
91-
diagnostics.check(
92-
securityContext.getUserPrincipal() instanceof PolarisPrincipal,
93-
"invalid_principal_type",
94-
"Principal must be a PolarisPrincipal");
95-
this.securityContext = securityContext;
96-
this.polarisPrincipal = (PolarisPrincipal) securityContext.getUserPrincipal();
87+
this.polarisPrincipal = principal;
9788
this.authorizer = authorizer;
9889
this.credentialManager = credentialManager;
9990
this.externalCatalogFactories = externalCatalogFactories;
@@ -104,7 +95,7 @@ protected PolarisCredentialManager getPolarisCredentialManager() {
10495
}
10596

10697
protected PolarisResolutionManifest newResolutionManifest() {
107-
return resolutionManifestFactory.createResolutionManifest(securityContext, catalogName);
98+
return resolutionManifestFactory.createResolutionManifest(polarisPrincipal, catalogName);
10899
}
109100

110101
/** Initialize the catalog once authorized. Called after all `authorize...` methods. */

runtime/service/src/main/java/org/apache/polaris/service/catalog/generic/GenericTableCatalogAdapter.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.apache.iceberg.catalog.TableIdentifier;
2828
import org.apache.polaris.core.PolarisDiagnostics;
2929
import org.apache.polaris.core.auth.PolarisAuthorizer;
30+
import org.apache.polaris.core.auth.PolarisPrincipal;
3031
import org.apache.polaris.core.catalog.ExternalCatalogFactory;
3132
import org.apache.polaris.core.config.FeatureConfiguration;
3233
import org.apache.polaris.core.config.RealmConfig;
@@ -92,14 +93,14 @@ private GenericTableCatalogHandler newHandlerWrapper(
9293
SecurityContext securityContext, String prefix) {
9394
FeatureConfiguration.enforceFeatureEnabledOrThrow(
9495
realmConfig, FeatureConfiguration.ENABLE_GENERIC_TABLES);
95-
validatePrincipal(securityContext);
96+
PolarisPrincipal principal = validatePrincipal(securityContext);
9697

9798
return new GenericTableCatalogHandler(
9899
diagnostics,
99100
callContext,
100101
resolutionManifestFactory,
101102
metaStoreManager,
102-
securityContext,
103+
principal,
103104
prefixParser.prefixToCatalogName(realmContext, prefix),
104105
polarisAuthorizer,
105106
polarisCredentialManager,

0 commit comments

Comments
 (0)