Skip to content

Support for external identity providers #1397

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
May 1, 2025
Merged

Conversation

adutra
Copy link
Contributor

@adutra adutra commented Apr 18, 2025

Fixes #336 and #976.

Overview

This change is globally backwards-compatible, there are no configuration breaking changes and the default server behavior is the same as today (internal authentication only).

The main interfaces are also unchanged – but some implementations changed.

TODOs

Things to do in follow-up PRs:

  • Integration tests (these will be tricky to implement, therefore I prefer to tackle that later)
  • Helm chart changes
  • Documentation

Authentication Types

To support external identity providers, the notion of "authentication type" has been introduced. The following values are supported:

  • internal: Polaris internal authentication only (default)
  • external: Polaris external authentication only (using Quarkus OIDC)
    • When this type is enabled globally or for a realm, the realm's internal token endpoint is disabled and returns 501.
  • mixed: Polaris internal and external authentication (using Quarkus OIDC)
    • internal is tried first, then external

The default authentication type is internal, but can be either changed globally or overridden per realm.

The authentication type is configured in the application.properties file as below:

# Default authentication type
polaris.authentication.type=internal

# Authentication type overrides per realm
polaris.authentication.realm1.type=external
polaris.authentication.realm2.type=mixed

See below for details of each authentication type.

Configuration Changes

The polaris.authentication configuration section remains backwards-compatible, but everything now can be overridden per realm.

The following options are effective only when using internal auth (can be overridden in per realm):

  • polaris.authentication.token-service.*
  • polaris.authentication.token-broker.*

The polaris.oidc configuration section is new and is used to further configure the OIDC tenants. See below for examples.

Authenticator Changes

The Authenticator interface remains the same, but BasePolarisAuthenticator has been modified and became DefaultAuthenticator.

This is because Quarkus Security distinguishes two phases of authentication:

  1. Extract the credentials (i.e., decode the token)
  2. Authenticate the credentials

Therefore, the Authenticator is not responsible anymore for decoding the token, but only for authenticating it.

A new PrincipalCredential interface has been introduced to hold the principal ID, name and roles, and pass it to the authenticator. The existing DecodedToken interface, which is used for internal authentication, is now a sub-type of PrincipalCredential.

The default logic for authenticating the principal, i.e. by a metastore lookup by ID or by name, remains the same.

Token Broker Changes

Token brokers now need to be injected directly, instead of injecting their factories. They are now request-scoped beans.

As a bonus, token brokers can now be configured per realm, i.e., one realm could use RSA key pairs, and another one shared secrets.

Authentication Workflows in Detail

Internal

  • InternalAuthenticationMechanism handles the authentication header
    • Calls TokenBroker to decode the token and creates a PrincipalCredential
  • InternalIdentityProvider creates a first SecurityIdentity with the PrincipalCredential
  • AuthenticatingAugmentor authenticates the PrincipalCredential
    • Calls Authenticator.authenticate()
    • Sets AuthenticatedPolarisPrincipal as the SecurityIdentity's principal
  • ActiveRolesAugmentor sets the active roles in the SecurityIdentity
    • Calls ActiveRolesProvider

External

  • [Quarkus OIDC] OidcAuthenticationMechanism handles the authentication header
  • [Quarkus OIDC] OidcIdentityProvider creates a first SecurityIdentity with the token
  • OidcTenantResolvingAugmentor resolves the Polaris OIDC tenant configuration to use and store it as an attribute in the SecurityIdentity
  • PrincipalAuthInfoAugmentor maps the JWT claims to a PrincipalAuthInfo and adds it to the SecurityIdentity
  • AuthenticatingAugmentor authenticates the PrincipalAuthInfo
    • Calls Authenticator.authenticate()
    • Sets AuthenticatedPolarisPrincipal as the SecurityIdentity's principal
  • ActiveRolesAugmentor sets the active roles in the SecurityIdentity
    • Calls ActiveRolesProvider

Mixed

  • InternalAuthenticationMechanism handles the authentication header
    • Calls TokenBroker to decode the token
  • If the TokenBroker validates the token:
    • InternalIdentityProvider creates a first SecurityIdentity with the token as a PolarisCredential
  • Otherwise:
    • InternalAuthenticationMechanism yields to the OIDC mechanism
    • [Quarkus OIDC] OidcAuthenticationMechanism handles the authentication header
    • [Quarkus OIDC] OidcIdentityProvider creates a first SecurityIdentity with the token
    • OidcTenantResolvingAugmentor resolves the Polaris OIDC tenant configuration to use and store it as an attribute in the SecurityIdentity
    • PrincipalAuthInfoAugmentor maps the JWT claims to a PrincipalAuthInfo and adds it to the SecurityIdentity
  • For both cases: AuthenticatingAugmentor authenticates the PrincipalAuthInfo
    • Calls Authenticator.authenticate()
    • Sets AuthenticatedPolarisPrincipal as the SecurityIdentity's principal
  • ActiveRolesAugmentor sets the active roles in the SecurityIdentity
    • Calls ActiveRolesProvider

OIDC Principal Mapping

Main interface: org.apache.polaris.service.quarkus.auth.external.mapping.PrincipalMapper.

Currently, the following polaris.oidc options are available:

  • polaris.oidc.principal-mapper.type: the Principal mapper implementation to use
  • polaris.oidc.principal-mapper.id-claim-path: the claim path to the principal id in the JWT token, e.g. polaris/principal_id
  • polaris.oidc.principal-mapper.name-claim-path: the claim path to the principal name in the JWT token, e.g. polaris/principal_name

They can be overridden per OIDC tenant (this is different from the realm!). The OIDC tenants will be looked up in the quarkus.oidc configuration section, e.g.:

quarkus.oidc.oidc-tenant1.auth-server-url=http://localhost:8080/realms/polaris
quarkus.oidc.oidc-tenant1.client-id=client1
quarkus.oidc.oidc-tenant1.application-type=service

polaris.oidc.oidc-tenant1.principal-mapper.id-claim-path=polaris/principal_id
polaris.oidc.oidc-tenant1.principal-mapper.name-claim-path=polaris/principal_name

If a tenant is not found in the quarkus.oidc or in the polaris.oidc section, the default OIDC tenant will be used.

Important: the default OIDC tenant is disabled by default, it must be enabled if you intend to use external auth.

OIDC Principal Roles Mapping

Main interface: org.apache.polaris.service.quarkus.auth.external.mapping.PrincipalRolesMapper.

Roles will be mapped from the JWT by Quarkus OIDC. This is done by setting the quarkus.oidc.roles.role-claim-path property:

quarkus.oidc.roles.role-claim-path=polaris/roles

See https://quarkus.io/guides/security-oidc-bearer-token-authentication#token-claims-and-security-identity-roles for more information.

The roles mapped by Quarkus will be available in SecurityIdentity.getRoles(). On the Polaris side, the PrincipalRolesMapper interface converts those roles to Polaris roles; the following properties are available:

  • polaris.oidc.principal-roles-mapper.type: the Principal Roles mapper implementation to use.
  • polaris.oidc.principal-roles-mapper.filter: optional regex to filter out roles.
  • polaris.oidc.principal-roles-mapper.mappings[0].regex: optional regex to modify role names.
  • polaris.oidc.principal-roles-mapper.mappings[0].replacement: replacement string.

These properties can be overridden per realm.

Mapping Examples

Example: for a JWT token like this (some claims omitted for brevity):

{
  "sub": "a663c299-1118-4b35-ac85-f9a4f38d0699",
  "typ": "Bearer",
  "azp": "client1",
  "scope": "profile email",
  "polaris": {
    "roles": [ "PRINCIPAL_ROLE:ALL" ],
    "principal_name": "root",
    "principal_id": 1
  },
  "preferred_username": "service-account-client1",
  "clientAddress": "172.18.0.1",
  "client_id": "client1"
}

You can configure Polaris like this:

quarkus.oidc.roles.role-claim-path=polaris/roles
polaris.oidc.principal-mapper.id-claim-path=polaris/principal_id
polaris.oidc.principal-mapper.name-claim-path=polaris/principal_name

Another example; if the JWT token is like this:

{
  "sub": "1"
  "typ": "Bearer",
  "azp": "client1",
  "scope": "service_admin catalog_admin profile email",
  "preferred_username": "root",
  "clientAddress": "172.18.0.1",
  "client_id": "client1"
}

You can configure Polaris like this:

quarkus.oidc.roles.role-claim-path=scope
polaris.oidc.principal-mapper.id-claim-path=sub
polaris.oidc.principal-mapper.name-claim-path=preferred_username
polaris.oidc.principal-roles-mapper.filter=^(?!profile$|email$).*
polaris.oidc.principal-roles-mapper.mappings[0].regex=^.*$
polaris.oidc.principal-roles-mapper.mappings[0].replacement=PRINCIPAL_ROLE:$0

The resulting Polaris principal roles will be: PRINCIPAL_ROLE:service_admin and PRINCIPAL_ROLE:catalog_admin.

@adutra
Copy link
Contributor Author

adutra commented Apr 18, 2025

For those willing to test out integration with Keycloak:

  1. Start a Keycloak server on port 8080 e.g.: http://localhost:8080/realms/master

  2. With keycloak admin UI, configure a client to return the following claims (e.g. using hard-coded claim mappers):
    a. polaris/principal_id
    b. polaris/principal_name
    c. polaris/roles

  3. Run Polaris with:

./gradlew :polaris-quarkus-service:quarkusDev \
  -Dpolaris.realm-context.realms=realm1,realm2,realm3 \
  -Dpolaris.bootstrap.credentials="realm1,root,secret;realm2,root,secret;realm3,root,secret" \
  -Dpolaris.authentication.realm2.type=external \
  -Dpolaris.authentication.realm3.type=mixed \
  -Dquarkus.oidc.tenant-enabled=true \
  -Dquarkus.oidc.auth-server-url=http://localhost:8080/realms/master \
  -Dquarkus.oidc.roles.role-claim-path=polaris/roles \
  -Dpolaris.oidc.principal-mapper.id-claim-path=polaris/principal_id \
  -Dpolaris.oidc.principal-mapper.name-claim-path=polaris/principal_name
  1. 3 realms will be bootstrapped:
    a. realm1 internal auth
    b. realm2 external auth
    c; realm3 mixed auth

  2. Obtain a token from Keycloak:

token=$(curl -v http://localhost:8080/realms/master/protocol/openid-connect/token \
  -d client_id=client1 \
  -d client_secret=s3cr3t \
  -d grant_type=client_credentials | jq -r .access_token)
  1. Obtain a token from Polaris (realm1 or realm3 – realm2 will return 501):
token=$(curl -v http://localhost:8181/api/catalog/v1/oauth/tokens \
  -H "Polaris-Realm: realm1" \
  -d client_id=root \
  -d client_secret=secret \
  -d grant_type=client_credentials \
  -d scope=PRINCIPAL_ROLE:ALL | jq -r .access_token)
  1. Using the token:
curl -v http://localhost:8181/api/catalog/v1/config\?warehouse\=default \
  -H "Polaris-Realm: realm1"  \
  -H "Authorization: Bearer $token"

*
* <p>By convention, this method returns an empty set when the principal is requesting all
* available principal roles.
*/
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm trying to document things as I go. This is the best knowledge I can get for this principal attribute.

quarkus.oidc.application-type=service
quarkus.oidc.resolve-tenants-with-issuer=true
# Default tenant (disabled by default, set this to true if you use external authentication)
quarkus.oidc.tenant-enabled=false
Copy link
Contributor Author

@adutra adutra Apr 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could enable this conditionally with a ConfigSourceInterceptor that would check if any realm is using external or mixed auth, in which case it makes sense to enable the default OIDC tenant, especially if no other tenant is configured. But I'm leaving this as a follow-up improvement.


/** A custom {@link IdentityProvider} that handles Polaris token authentication requests. */
@ApplicationScoped
public class PolarisIdentityProvider implements IdentityProvider<TokenAuthenticationRequest> {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI this was just moved to org.apache.polaris.service.quarkus.auth.internal.InternalIdentityProvider but git lost track of it.

@@ -61,10 +58,6 @@ public Map<String, String> getConfigOverrides() {
}
}

@Inject
@Identifier("rsa-key-pair")
TokenBrokerFactory tokenBrokerFactory;
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused.

@@ -77,7 +77,11 @@ public Map<String, String> getConfigOverrides() {
"polaris.metrics.tags.environment",
"prod",
"polaris.realm-context.type",
"test");
"test",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test would fail without this change because each realm would have a different pair of RSA keys. So switching to shared secret.

/**
* Authenticates the given credentials and returns an optional principal.
*
* <p>If the credentials are not valid or if the authentication fails, implementations may choose
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again trying to document things a posteriori.

I don't really see the value of returning an Optional here, as the semantics of returning "empty" conflict with that of throwing an error. But I didn't want to change the method signature without knowing what others think.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to prefer exceptions here (for follow-up)

@adutra adutra force-pushed the oidc-multi-auth branch 6 times, most recently from 0fff0f7 to abb7483 Compare April 19, 2025 17:22
@adutra adutra force-pushed the oidc-multi-auth branch 3 times, most recently from dd3d992 to 42befb1 Compare April 19, 2025 22:45
* available principal roles. When this is true, {@link #getActivatedPrincipalRoleNames()} returns
* an empty set.
*/
public boolean allRoles() {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we need this method to distinguish the case when the principal is requesting all roles from the case where no roles were found in the token – since both cases translate into an empty set of role names.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But a better solution imho would be to remove roles from this class completely, and let SecurityIdentity.getRoles() expose the roles.

I checked the call sites for getActivatedPrincipalRoles() and it's in fact never used. So it seems that setActivatedPrincipalRoles() could be removed as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the method because it doesn't work well with the Resolver. TLDR we can't distinguish "no roles" from "all roles" for now.

activatedPrincipalRoles.addAll(
Arrays.stream(tokenInfo.getScope().split(" "))
credentials.getPrincipalRoles().stream()
.map(
s -> // strip the principal_role prefix, if present
s.startsWith(PRINCIPAL_ROLE_PREFIX)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is dangerous and should be changed. For example, if the roles were initially:
["PRINCIPAL_ROLE:catalog-admin', "service-admin"], this would be transformed into: ["catalog-admin', "service-admin"], potentially requesting the service-admin role, but that likely wasn't intentional.

I think it would be safer to discard all roles that don't have the PRINCIPAL_ROLE: prefix.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree, this should be stream().filter(s -> s.startsWith(PRINCIPAL_ROLE_PREFIX)).map(...)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to filter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, let's change this.

Copy link
Contributor Author

@adutra adutra Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW I also think that the responsibility of stripping off the prefix should be transferred from this class to DefaultPrincipalRolesMapper. But we can do this in a follow-up PR.

Copy link
Contributor

@dimas-b dimas-b left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice improvement 👍


interface AuthenticatorConfiguration {}
Map<String, ? extends AuthenticationRealmConfiguration> realms();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe make ? a declared type parameter to avoid type casts in sub-types?

|| authenticationType == AuthenticationType.MIXED) {
errors.add(
Error.of(
"Internal authentication is deprecated since Iceberg 1.6.0.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the statement is true, Polaris still supports the /token endpoint. Should we really flag this as a problem for production deployments? Resolving this requires using Iceberg 1.9 clients plus an Auth Manager implementation, which may be too much of a burden to roll out ATM.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't need Iceberg 1.9 and auth manager, client credentials should still work, although without token refreshes. But OK to remove this.

/**
* Authenticates the given credentials and returns an optional principal.
*
* <p>If the credentials are not valid or if the authentication fails, implementations may choose
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to prefer exceptions here (for follow-up)

@@ -82,7 +82,9 @@ protected List<PrincipalRoleEntity> loadActivePrincipalRoles(
principal.getId());
throw new NotAuthorizedException("Unable to authenticate");
}
boolean allRoles = tokenRoles.contains(BasePolarisAuthenticator.PRINCIPAL_ROLE_ALL);

// FIXME how to distinguish allRoles from no roles at all?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this is a non-issue. My understanding is that tokens need to support restricting to specific roles. Restricting to no roles at all is hardly useful in Polaris since Principals cannot have access rights granted directly to them.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But that's the issue, if no roles are found in the token, Polaris will assume that the token has ALL of the principal roles available at the time of the request. That's imho concerning.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I always thought assuming all roles by default was normal in Polaris. I do not see any enforced restrictions for roles in Polaris-owned tokens. So roles in Polaris tokens always looked like a self-imposed restriction to me.

External tokens would have roles / scopes injected by the IdP, I assume.

@collado-mike : WDYT?

activatedPrincipalRoles.addAll(
Arrays.stream(tokenInfo.getScope().split(" "))
credentials.getPrincipalRoles().stream()
.map(
s -> // strip the principal_role prefix, if present
s.startsWith(PRINCIPAL_ROLE_PREFIX)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1 to filter

*
* @see DefaultAuthenticator
*/
public interface PrincipalCredential {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What makes this class a "Credential"? How about PrincipalAuthInfo?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I inferred from Quarkus Security parlance, a "principal" defines an identity, but a "credential" is any concrete form of principal identity proof, and could be: a string, a JWT token, a TLS certificate, etc. So "credential" seemed appropriate here since this is basically a view of a JWT token.

But OK to change to PrincipalAuthInfo 👍

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, but this interface does not have anything that requires it to be backed by JWT and the methods are only informative, they do not provide any "proof" of identity 🤔

try {
token = decodeToken(credential);
} catch (Exception e) {
return configuration.type() == AuthenticationType.MIXED
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be more correct to only parse the token here and delegate validation to InternalIdentityProvider?

With that approach, I guess we will not have condition failure logic... All nuance should be handled by Quarkus, WDYT?

We could have a Polaris-specific JWT claim to identify Polaris tokens, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, the issue is that "parse" and "validate" are basically tied together.

Auth0 has two methods: JWT.decode and JWT.require: the former decodes without validating the signature, the latter decodes and validates.

Currently, JWTBroker.verify() uses JWT.require. We could introduce a new method in TokenBroker, e.g. parse() or decode(), and call JWT.decode there. But I don't know if it's worth the hassle: I bet that decoding the token is just slightly faster than decoding and verifying.

BTW that's why I introduced the MIXED authentication type: so that the extra penalty of decoding the token twice is only paid by realms that opt for that authentication type.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Performance-wise current approach is probably easiest and fastest. However, InternalIdentityProvider looks like a no-op in the current state of the code, which looks a bit odd to me. Also, the if in exception handling seems to be coming exactly from the uncertainty of the token's origin.

If we parsed the token first, we could delegate to the right validation code and then, if an validation fails it will always be a certain failure.

Regarding parsing penalty, if the token is external, the first attempt will parse and cause an exception on all call, which would be bigger overhead for the lower priority authenticators, I guess.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

However, InternalIdentityProvider looks like a no-op in the current state of the code, which looks a bit odd to me

That is very true, but is intentional: we could short-circuit in InternalAuthenticationMechanism and return a SecurityIdentity straight away, rather than invoking identityProviderManager.authenticate(). In that case, an IdentityProvider is not necessary.

BUT: when the identity provider manager is not invoked, no augmentors are invoked either. I discovered this while testing.

So the TLDR is: we need InternalIdentityProvider, even if it's a passthrough impl for now, because there are augmentors that must be invoked.

Regarding parsing penalty, if the token is external, the first attempt will parse and cause an exception on all call, which would be bigger overhead for the lower priority authenticators, I guess.

True. Let me try to introduce the new TokenBroker method and see if that looks better.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After playing with this a bit, I'd be in favor of leaving this for a follow-up PR. The changes to the TokenBroker interface become a bit invasive imho.

I was trying something like this:

interface TokenBroker {
  DecodedToken decode(String token); // done in InternalAuthenticationMechanism
  void verify(DecodedToken token); // done in InternalIdentityProvider
...
}

But:

  1. decode would still throw an exception on every request with an external token;
  2. We'd need to leak some auth0 types in the DecodedToken interface in order to avoid re-parsing the token. e.g. DecodedJWT getAuth0Token();

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm actually my idea won't even work. We need to verify the signature as well in InternalAuthenticationMechanism. That's the only way to be sure that the token is or isn't internal to Polaris.

Copy link
Contributor

@dimas-b dimas-b Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking that we could parse without validation in InternalAuthenticationMechanism, check a Polaris-specific property and trust it to choose the internal validation logic. Then, InternalIdentityProvider would finish the validation by checking the signature. If the internal property is not present, validation will be done by OIDC.

In any case, I think it makes sense to defer this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was able to achieve something that I think will improve the efficiency of token decoding:

adutra#4

Let me know if I should merge those changes into this PR or wait until this one is merged.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's do that separately.. This PR is already big and reviewed :)

@adutra adutra force-pushed the oidc-multi-auth branch 2 times, most recently from b062ecd to 935448f Compare April 25, 2025 14:08
@github-project-automation github-project-automation bot moved this from PRs In Progress to Ready to merge in Basic Kanban Board Apr 25, 2025
dimas-b
dimas-b previously approved these changes Apr 30, 2025
@dimas-b
Copy link
Contributor

dimas-b commented Apr 30, 2025

@collado-mike : any more comments from your side? :)


/** A utility class for locating claims in a JWT token by path. */
@ApplicationScoped
public class ClaimsLocator {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

package private?

return identity.getAttribute(TENANT_CONFIG_ATTRIBUTE);
}

@Inject OidcTenantResolver resolver;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why no constructor injection?

* given identity and adds it as an attribute to the {@link SecurityIdentity}.
*/
@ApplicationScoped
public class OidcTenantResolvingAugmentor implements SecurityIdentityAugmentor {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a lot of these classes can be package private. Any reason to expose them outside of this package?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one specifically is referenced outside the package, but OK for the others 👍

@Identifier("default")
public class DefaultPrincipalMapper implements PrincipalMapper {

@Inject ClaimsLocator claimsLocator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

constructor inject?

Comment on lines +64 to 66
AuthenticatedPolarisPrincipal polarisPrincipal =
identity.getPrincipal(AuthenticatedPolarisPrincipal.class);
Set<String> validRoleNames = activeRolesProvider.getActiveRoles(polarisPrincipal);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the ActiveRolesProvider doesn't need more context about the roles the principal should have. E.g., in the external auth case, the token indicates the roles, whereas in the internal case, we have to look up grant records. How does the role provider know whether to accept the tokens contents or to validate the grants?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very good question. You'll notice that for now only one impl of ActiveRolesProvider exists, which will invariably lookup the grants in the database.

When we have federated principals, my suggestion would be to change this Augmentor by either:

  1. Creating one different Augmentor for each authentication type (internal/external)
  2. Make the Augmentor use a different impl of ActiveRolesProvider depending on the authentication type.

My preference would be for 2, but in any case, my intention was to tackle that once we have federated principals. Wdyt? Does this plan work for you?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We also need a more robust story around roles mapping. For now, most of the work is done by PrincipalRolesMapper, but some further processing is being done in DefaultAuthenticator, for historical reasons. And AuthenticatedPolarisPrincipal wouldn't need to expose roles anymore. But again I'm leaving that for later, as this PR is already big.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deferring this improvement makes sense to me. Also +1 to option 2 above.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This plan sounds ok to me. Using the different ActiveRolesProvider for different authn methods sounds like the right way to go.

Copy link
Contributor

@collado-mike collado-mike left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! thanks for the changes

@adutra adutra merged commit e1c0a1c into apache:main May 1, 2025
6 checks passed
@github-project-automation github-project-automation bot moved this from Ready to merge to Done in Basic Kanban Board May 1, 2025
snazy added a commit to snazy/polaris that referenced this pull request May 22, 2025
* Policy Store: PolicyMappingRecord with Persistence Impl (apache#1104)

* Spark: Setup repository code structure and build (apache#1190)

* Added freshness aware table loading using metadata file location for ETag (apache#1037)

* Pulled in iceberg 1.8.0 spec changes for freshness aware table loading and added feature to Polaris

* Changed etag support to use entityId:version tuple

* fixed getresponse call

* Changed etagged response to record and gave default implementation to ETaggableEntity

* Made iceberg rest spec docs clearer

* Added HTTP Compliant ETag and IfNoneMatch representations and separated persistence from etag logic

* Changed ETag to be a record and improved semantics of IfNoneMatch

* Fixed semantics of if none match

* Removed ETag representation, consolidated in IfNoneMatch

* fixed if none match parsing

* Added table entity retrieval method to table operations

* removed accidental commit of pycache folders

* Fixed formatting

* Changed to use metadata location hash

* Ran formatting

* use sha256

* Moved out ETag functions to utility class and removed ETaggedLoadTableResponse

* Addressed comments

* Fixed IcebergTableLikeEntity package rename

* main: Update dependency io.opentelemetry.semconv:opentelemetry-semconv to v1.31.0 (apache#1288)

* Update LICENSE and NOTICE in the distributions (admin and server) (apache#1258)

* Gradle/Quarkus: make imageBuild task depend on jandex (apache#1290)

* Core: Clarify the atomicity of BasePersistence methods (apache#1274)

* Implement GenericTableCatalogAdapter (apache#1264)

* rebase

* more fixes

* autolint

* working on tests

* stable test

* autolint

* polish

* changes per review

* some changes per review

* grants

* autolint

* changes per review

* changes per review

* typofix

* Improve code-containment and efficiency of etag-aware loading (apache#1296)

* Improve code-containment and efficiency of etag-aware loading

-Make the hash generation resilient against null metadataLocation
-Use getResolvedPath instead of getPassthroughResolvedPath to avoid redundant persistence round-trip
-Only try to calculate the etag for comparison against ifNoneMatch if the ifNoneMatch is actually provided

* Add strict null-checking at callsites to generateETag, disallow passing null to generator

* Add TODO to refactor shared logic for etag generation

* Core: Add Endpoints and resource paths for Generic Table (apache#1286)

* main: Update dependency com.nimbusds:nimbus-jose-jwt to v10.1 (apache#1299)

* [JDBC] Part1 : ADD SQL script for Polaris setup (apache#1276)

* main: Update registry.access.redhat.com/ubi9/openjdk-21-runtime Docker tag to v1.22-1.1743605859 (apache#1300)

* done (apache#1297)

* Add Polaris Community Meeting for April 3, 2025 (apache#1304)

* Use config-file to define errorprone rule (apache#1233)

Also enabled a couple more simple rules, and adding suppressions/fixes for/to the code.

The two rules `EqualsGetClass` and `UnusedMethod`, which I think are useful, are not enabled yet, because that would mean actual code changes, which I do not want to do in this PR.

The rule `PatternMatchingInstanceof`, introduced in apache#393, is disabled in this PR. It does not work before errorrpone 2.37.0 (via apache#1213) - requires additional changes to enable the rule (see apache#1215).

* Add Yun as a contributor (apache#1310)

* Refactor CatalogHandler to comply with ErrorProne rules (apache#1312)

Fix the CI error after apache#1233

* Implement PolicyCatalog Stage 1: CRUD + ListPolicies (apache#1294)

* main: Update dependency io.opentelemetry:opentelemetry-bom to v1.49.0 (apache#1316)

* main: Update docker.io/jaegertracing/all-in-one Docker tag to v1.68.0 (apache#1317)

* main: Update dependency boto3 to v1.37.28 (apache#1328)

* main: Update dependency software.amazon.awssdk:bom to v2.31.16 (apache#1329)

* Make `BasePolaritsMetaStoreManagerTest` and `(Base)ResolverTest` reusable (apache#1308)

Moves the test cases into the `Base*` classes and make sure the classes can be reused by other persistence implementations.

* main: Update dependency io.opentelemetry.semconv:opentelemetry-semconv to v1.32.0 (apache#1293)

* main: Update mockito monorepo to v5.17.0 (apache#1311)

* PySpark Update AWS Region (apache#1302)

Co-authored-by: Travis Bowen <travis.bowen@snowflake.com>

* main: Update dependency com.nimbusds:nimbus-jose-jwt to v10.2 (apache#1334)

* main: Update dependency com.diffplug.spotless:spotless-plugin-gradle to v7.0.3 (apache#1335)

* Maven publication: Produce correct `<scm><tag>` in `pom.xml` (apache#1330)

`project.scm.tag` in a Maven pom is intended to refer to the SCM (Git) tag. We currently publish `main`, which is incorrect.

This change omits the SCM tag for snapshot builds, but emits the Git tag for releases.

* Remove `@StaticInitSafe` annotation (apache#1331)

There was an issue around mapped configurations having the `@StaticInitSafe` annotation that led to _two_ instances (a "static" one and a "somewhet application-scoped" one) - this was fixed in Quarkus 3.21. One bug in smallrye-config is fixed for Quarkus > 3.21.0, another issue however remains.

Since `@StaticInitSafe` annotated configs seem to cause some weird issues, it seems legit to remote that annotation altogether. This approach was [taken in Nessie](projectnessie/nessie#10606) as well. Investigations (via practical experiments) have proven that there's no measurable impact (runtime + heap) when doing this - and that's also been confirmed by Quarkus + Smallrye-config maintainers.

Hence this change remotes that annotation from the code base.

* Build/Release: Add a "generate digest" task and use for source tarball and Quarkus distributables (apache#1271)

* Ensure that digest and signature are generated for both Polaris-Server and admin tar/zip distribution
* Move "generate digest" functionality to a Gradle task

* main: Update dependency com.google.errorprone:error_prone_core to v2.37.0 (apache#1213)

* main: Update Quarkus Platform and Group to v3.21.1 (apache#1291)

* main: Update dependency io.netty:netty-codec-http2 to v4.2.0.Final (apache#1301)

* Remove unnecessary `clean` and `--no-build-cache` from Gradle invocations (apache#1338)

`quarkusAppPartsBuild --rerun` is the right way to force a Docker image build.

* Generalize bootstrapping in servers (apache#1313)

* Remove `instanceof` checks from `QuarkusProducers`.

* Remove the now unused `onStartup` method from `InMemoryPolarisMetaStoreManagerFactory`.

* Instead, call the good old `bootstrapRealms` method from `QuarkusProducers`.

* Add new config property to control which MetaStore types are bootstrapped automatically (defaults to `in-memory` as before).

* There is no bootstrap behaviour change in this PR, only refactorings to simplify code.

* Add info log message to indicate when a realm is bootstrapped in runtime using preset credentials.

Future enhancements may include pulling preset credentials from a secret manager like Vault for bootstrapping (s discussed in comments on apache#1228).

* main: Update actions/stale digest to 816d9db (apache#1341)

* main: Update dependency com.adobe.testing:s3mock-testcontainers to v4 (apache#1342)

* main: Update dependency org.eclipse.persistence:eclipselink to v4.0.6 (apache#1343)

* main: Update dependency io.quarkus to v3.21.2 (apache#1344)

* main: Update dependency com.google.guava:guava to v33.4.7-jre (apache#1340)

Co-authored-by: Robert Stupp <snazy@snazy.de>

* Spark: Add Namespaces and View support for SparkCatalog (apache#1332)

* Demote technical log messages to DEBUG in PolarisCallContextCatalogFactory (apache#1346)

These messages appear to be logging low-level technical details
about what is going on in the factory and are not likely to be
of interest to most users on a daily basis.

* Core/Service: Implement PolicyCatalog Stage 2: detach/attach/getApplicablePolicies (apache#1314)

* Spec: Add 'inherited' and 'namespace' Fields to GetApplicablePolicies API Response (apache#1277)

* Properly track bootstrappedRealms in InMemoryPolarisMetaStoreManagerFactory (apache#1352)

Fixes apache#1351

* Implement GenericTableCatalogAdapter; admin-related fixes (apache#1298)

* initial commit:

* debugging

* some polish

* autolint

* spec change

* bugfix

* bugfix

* various fixes

* another missing admin location

* autolint

* false by default

* fixes per review

* autolint

* more fixes

* DRY

* revert small change for a better error

* integration test

* extra test

* autolint

* stable

* wip

* rework subtypes a bit

* stable again

* autolint

* apply new lint rule

* errorprone again

* adjustments per review

* update golden files

* add another test

* clean up logic in PolarisAdminService

* autolint

* more fixes per review

* format

* Update versions in distribution LICENSE and NOTICE (apache#1350)

* Spark: Add CreateTable and LoadTable implementation for SparkCatalog (apache#1303)

* Add a weigher to the EntityCache based on approximate entity size (apache#490)

* initial commit

* autolint

* resolve conflicts

* autolint

* pull main

* Add multiplier

* account for name, too

* adjust multiplier

* add config

* autolint

* remove old cast

* more tests, fixes per review

* add precise weight test

* autolint

* populate credentials field for loadTableResponse (apache#1225)

* populate credentials field for loadTableResponse

* spotless

* spotless

* remove unused hashset

* fix merge

* fix empty credential case

* spotlessApply

---------

Co-authored-by: David Lu <dalu@hubspot.com>

* main: Update dependency io.smallrye.common:smallrye-common-annotation to v2.12.0 (apache#1355)

* Build: Avoid adding duplicated projects for Intelij IDE usage (apache#1333)

* main: Update dependency org.junit:junit-bom to v5.12.2 (apache#1354)

* main: Update dependency org.apache.commons:commons-text to v1.13.1 (apache#1358)

* main: Update dependency boto3 to v1.37.33 (apache#1360)

* main: Update dependency software.amazon.awssdk:bom to v2.31.21 (apache#1361)

* main: Update dependency io.micrometer:micrometer-bom to v1.14.6 (apache#1362)

* main: Update dependency com.google.guava:guava to v33.4.8-jre (apache#1366)

* Update LICENSE/NOTICE with latest versions (apache#1364)

* Use "clean" LICENSE and NOTICE in published jar artifacts (apache#1292)

* main: Update dependency io.projectreactor.netty:reactor-netty-http to v1.2.5 (apache#1372)

* Add `Varint` type for variable-length integer encoding (apache#1229)

* main: Update docker.io/prom/prometheus Docker tag to v3.3.0 (apache#1375)

* Set version to 0.10.0-beta in prepaaration for the next release (apache#1370)

* Update the link to OpenAPI in the documentation (apache#1379)

* Integration test for Spark Client (apache#1349)

* add integration test

* add change

* add comments

* rebase main

* update class comments

* add base integration

* clean up comments

* main: Update dependency net.ltgt.gradle:gradle-errorprone-plugin to v4.2.0 (apache#1392)

* Add generic table documentations (apache#1374)

* add generic table documentation (incomplete)

* fix table and spacing

* remove documentation in client api since there is no implementation yet

* remove spacing

* minor fix - proof read

* review fix, wording

* add generic table documentation (incomplete)

* fix table and spacing

* remove documentation in client api since there is no implementation yet

* remove spacing

* minor fix - proof read

* review fix, wording

* proof read - punctuation fix

* change table privilege reference

* Unblock test `listNamespacesWithEmptyNamespace` (apache#1289)

* Unblock test `listNamespacesWithEmptyNamespace`

* Use `containsExactly` to simplify the test

* Fix empty namespace behavior

* Address comments

* Block dropping empty namespace

* Improve error messages

* Revamp the Quick Start page (apache#1367)

* First Draft with AWS

* try again

* try again

* try again

* try again

* try again

* try now

* should work

* AWS First Draft Complete

* ensure file changed

* Azure First Draft Complete

* Azure First Draft, pt. 2

* Azure Completed

* GCP First Draft

* GCP Verified

* File structure fixed

* Remove Trino-specific tutorial

* Restructured Quick Start

* Addresses minor comments from @eric-maynard

* Added reference to Deploying Polaris in Production

* Fix MD Link Checker

---------

Co-authored-by: Adnan Hemani <adnan.hemani@snowflake.com>

* Update README with links to new Quickstart experience (apache#1393)

* Update the StorageConfiguration to invoke singleton client objects, a… (apache#1386)

* Update the StorageConfiguration to invoke singleton client objects, and add a test

* Fix formatting

* using guava suppliers

* Add aws region

* Cleanup and mock test

* Spark: Add rest table operations (drop, list, purge and rename etc) for Spark Client (apache#1368)

* Initial MVP implementation of Catalog Federation to remote Iceberg REST Catalogs (apache#1305)

* Initial prototype of catalog federation just passing special properties into internal properties.

Make Resolver federation-aware to properly handle "best-effort" resolution of
passthrough facade entities.

Targets will automatically reflect the longest-path that we happen to have stored
locally and resolve grants against that path (including the degenerate case
where the longest-path is just the catalog itself).

This provides Catalog-level RBAC for passthrough federation.

Sketch out persistence-layer flow for how connection secrets might be pushed
down into a secrets-management layer.

* Defined internal representation classes for connection config

* Construct and initialize federated iceberg catalog based on connection config

* Apply the same spec renames to the internal ConnectionConfiguration representations.

* Manually pick @XJDKC fixes for integration tests and omittign secrets in response objects

* Fix internal connection structs with updated naming from spec PR

* Push CreateCatalogRequest down to PolarisAdminService::createCatalog just like UpdateCatalogRequest in updateCatalog.

This is needed if we're going to make PolarisAdminService handle secrets management without ever putting the secrets
into a CatalogEntity.

* Add new interface UserSecretsManager along with a default implementation

The default UnsafeInMemorySecretsManager just uses an inmemory ConcurrentHashMap
to store secrets, but structurally illustrates the full flow of intended
implementations.

For mutual protection against a compromise of a secret store or the core
persistence store, the default implementation demonstrates storing only
an encrypted secret in the secret store, and a one-time-pad key in the
returned referencePayload; other implementations using standard crypto
protocols may choose to instead only utilize the remote secret store as
the encryption keystore while storing the ciphertext in the referencePayload
(e.g. using a KMS engine with Vault vs using a KV engine).

Additionally, it demonstrates the use of an integrity check by storing a
basic hashCode in the referencePayload as well.

* Wire in UserSecretsManager to createCatalog and federated Iceberg API handlers

Update the internal DPOs corresponding to the various ConnectionConfigInfo API objects
to no longer contain any possible fields for inline secrets, instead holding the
JSON-serializable UserSecretReference corresponding to external/offloaded secrets.

CreateCatalog for federated catalogs containing secrets will now first extract
UserSecretReferences from the CreateCatalogRequest, and the CatalogEntity will
populate the DPOs corresponding to ConnectionConfigInfos in a secondary pass
by pulling out the relevant extracted UserSecretReferences.

For federated catalog requests, when reconstituting the actual sensitive
secret configs, the UserSecretsManager will be used to obtain the secrets
by using the stored UserSecretReferences.

Remove vestigial internal properties from earlier prototypes.

* Since we already use commons-codec DigestUtils.sha256Hex, use that for the hash in UnsafeInMemorySecretsManager
just for consistency and to illustrate a typical scenario using a cryptographic hash.

* Rename the persistence-objects corresponding to API model objects with a new naming
convention that just takes the API model object name and appends "Dpo" as a suffix;

* Use UserSecretsManagerFactory to Produce the UserSecretsManager (#1)

* Move PolarisAuthenticationParameters to a top-level property according to the latest spec

* Create a Factory for UserSecretsManager

* Fix a typo in UnsafeInMemorySecretsManagerFactory

* Gate all federation logic behind a new FeatureConfiguration - ENABLE_CATALOG_FEDERATION

* Also rename some variables and method names to be consistent with prior rename to ConnectionConfigInfoDpo

* Change ConnectionType and AuthenticationType to be stored as int codes in persistence objects.

Address PR feedback for various nits and javadoc comments.

* Add javadoc comment to IcebergCatalogPropertiesProvider

* Add some constraints on the expected format of the URN in UserSecretReference and placeholders
for next steps where we'd provide a ResolvingUserSecretsManager for example if the runtime ever
needs to delegate to two different implementations of UserSecretsManager for different entities.

Reduce the `forEntity` argument to just PolarisEntityCore to make it more clear that the
implementation is supposed to extract the necessary identifier info from forEntity for
backend cleanup and tracking purposes.

---------

Co-authored-by: Rulin Xing <rulin.xing+oss@snowflake.com>
Co-authored-by: Rulin Xing <xjdkcsq3@gmail.com>

* Add Adnan and Neelesh to collaborators list (apache#1396)

* Replace authentication filters with Quarkus Security (apache#1373)

* Implement PolicyCatalogHandler and Add Policy Privileges Stage 1: CRUD + ListPolicies (apache#1357)

* Add PolicyCatalogHandler and tests

* Fix style

* Address review comments

* Address review comments 2

* fix nit

* Remove CallContext.getAuthenticatedPrincipal() (apache#1400)

* main: Update dependency info.picocli:picocli-codegen to v4.7.7 (apache#1408)

* main: Update dependency com.google.errorprone:error_prone_core to v2.38.0 (apache#1404)

* Add Polaris Community Meeting 2025-04-17 (apache#1409)

* main: Update dependency boto3 to v1.37.37 (apache#1412)

* EclipseLink: add PrimaryKey to policy mapping records JPA model (apache#1403)

* Re-instate dependencies between Docker Compose services (apache#1407)

* Do not rotate bootstrapped root credentials (apache#1414)

* Add Getting Started Button to the Apache Polaris Webshite Homepage (apache#1406)

* Core: change to return ApplicablePolicies (apache#1415)

* Rename the Snapshot Retention policy (apache#1284)

* Rename the Snapshot Retention policy

* Resolve comments

* Resolve comments

---------

Co-authored-by: Yufei Gu <yufei.apache.org>

* main: Update dependency com.adobe.testing:s3mock-testcontainers to v4.1.0 (apache#1419)

* rename snapshotRetention to snashotExpiry (apache#1420)

* main: Update registry.access.redhat.com/ubi9/openjdk-21-runtime Docker tag to v1.22-1.1744796716 (apache#1394)

* main: Update dependency software.amazon.awssdk:bom to v2.31.26 (apache#1413)

* main: Update dependency com.adobe.testing:s3mock-testcontainers to v4.1.1 (apache#1425)

* Fix releaseEmailTemplate task (apache#1384)

* Update distributions LICENSE and NOTICE with AWS SDK 2.31.26 update (apache#1423)

* Support snapshots=refs (apache#1405)

* initial commit

* autolint

* small revert

* rebase

* autolint

* simpler

* autolint

* tests

* autolint

* stable

* fix leak

* ready for review

* improved test

* autolint

* logic flip again

* Update service/common/src/main/java/org/apache/polaris/service/catalog/iceberg/IcebergCatalogHandler.java

Co-authored-by: Alexandre Dutra <adutra@users.noreply.github.com>

* Update integration-tests/src/main/java/org/apache/polaris/service/it/env/CatalogApi.java

Co-authored-by: Alexandre Dutra <adutra@users.noreply.github.com>

* adjustments for committed suggestions

* autolint

---------

Co-authored-by: Alexandre Dutra <adutra@users.noreply.github.com>

* Remove activatedPrincipalRoles property from AuthenticatedPolarisPrincipal (apache#1410)

This seems to be a leftover from when ActiveRolesProvider was introduced. The setter was still used, but the getter wasn't, which hints at the fact that this property can be safely removed.

As a bonus, AuthenticatedPolarisPrincipal now becomes immutable, which is imho a very good thing.

* Implement PolicyCatalogHandler and Add Policy Privileges Stage 2: AttachPolicy + DetachPolicy (apache#1416)

* add auth test for attach/detach

* apply formatter

* refactor authorizePolicyAttachmentOperation

* address comment

* better naming

* Ship eclipselink and PostgreSQL JDBC driver by default in Polaris distribution (apache#1411)

* Fix Connection Config DPOs (apache#1422)

* Fix connection config dpos

* Run spotlessApply

* Doc: Fix the issue that html tags are not working in Hugo (apache#1382)

* Implement PolicyCatalogHandler Stage 3: GetApplicablePolicies (apache#1421)

* [JDBC] Part2: Add Relational JDBC module (apache#1287)

* Bump version to 0.11.0-beta-incubating-SNAPSHOT (apache#1429)

* Make entity lookups by id honor the specified entity type (apache#1401)

* Make entity lookups by id honor the specified entity type

All implementations of `TransactionalPersistence.lookupEntityInCurrentTxn()` are currently ignoring the `typeCode` parameter completely and could potentially return an entity of the wrong type.

This can become very concerning during authentication, since a principal lookup could return some entity that is not a principal, and that would be considered a successful authentication.

* review

* Remove "test" Authenticator (apache#1399)

* Propagate SQLException as "caused by" (apache#1430)

* Remove logging for DbOps (apache#1433)

* Spark: Add regtests for Spark client to test built jars (apache#1402)

* main: Update dependency com.google.cloud:google-cloud-storage-bom to v2.51.0 (apache#1436)

* main: Update dependency org.testcontainers:testcontainers-bom to v1.21.0 (apache#1437)

* main: Update actions/setup-python digest to a26af69 (apache#1440)

* Spark-IT: use correct configurations (apache#1444)

... do not let Spark leak into Quarkus

* PolarisRestCatalogIntegrationTest: Always purge generic tables (apache#1443)

* Add missing Postgresql dependency (apache#1447)

* Add Request Timeouts  (apache#1431)

* add timeout

* add iceberg exception mapping

* dont use quarkus bom, disable timeout

* nits

* Fix sparks sql regtests with up to date config (apache#1454)

* Refactor BasePolarisTableOperations & BasePolarisViewOperations (apache#1426)

* initial copy paste

* Reorder

* view copy paste

* fixes, polish

* stable

* yank

* CODE_COPIED_TO_POLARIS comments

* autolint

* update license

* typofix

* update comments

* autolint

* Use .sha512 extension instead of -sha512 (apache#1449)

* main: Update dependency org.eclipse.microprofile.fault-tolerance:microprofile-fault-tolerance-api to v4.1.2 (apache#1451)

* Doc: Update Local Root Principal Credentials in Quickstart (apache#1452)

* Update the Getting Started Workflow with each Cloud Provider's Blob Storage (apache#1435)

* AWS First Draft

* Debug

* revert typo

* Add JQ to docker runtime

* Debug, pt2

* debug

* debug

* Allow Instance Profile Roles

* change random suffix

* change instance profile to regular IAM roles

* AWS Final Draft

* Azure First Draft

* debug

* Azure First Draft

* debug

* typo

* GCP First Try

* GCP Complete

* GCP Final

* add all jars to Spark

* refactor

* Implement PolicyCatalogAdapter (apache#1438)

* Generic Table/Policy Store: Move feature config check to Adapter and some small refactoring (apache#1465)

* update refs (apache#1464)

* [JDBC] Part3: Plumb JDBC module to Quarkus (apache#1371)

* Allow BasePolarisTableOperations to skip refreshing metadata after a commit (apache#1456)

* initial commit

* fix another test

* changes per comments

* visibility

* changes per review

* autolint

* oops

* main: Update dependency com.fasterxml.jackson:jackson-bom to v2.19.0 (apache#1455)

* Doc: Added set custom credentials instruction in README (apache#1461)

* Doc: Add policy documentation (apache#1460)

* main: Update dependency software.amazon.awssdk:bom to v2.31.30 (apache#1475)

* main: Update dependency gradle to v8.14 (apache#1459)

* main: Update dependency gradle to v8.14

* fix PR

---------

Co-authored-by: Robert Stupp <snazy@snazy.de>

* Remove unused class TokenInfoExchangeResponse (apache#1479)

This is an oversight from apache#1399.

* Upgrade Polaris to Iceberg 1.9.0 (apache#1309)

* Doc: Update on access-control policy docs (apache#1472)

* main: Update Quarkus Platform and Group (apache#1381)

* Added link to the Spark-Jupyter Notebook Getting Started from the main Getting Started Page (apache#1453)

* Added link to the Spark-Jupyter Notebook Getting Started from the main Quickstart page

* Typo

Co-authored-by: Eric Maynard <emaynard@apache.org>

* Suggestions as per @eric-maynard's review

* Fix Typo

---------

Co-authored-by: Eric Maynard <emaynard@apache.org>

* [JDBC] Support Policy (apache#1468)

* Refactor EntityCache into an interface (apache#1193)

* Refactor EntityCache to an interface

* fix

* spotless

* Remove unused PolarisCredentialVendor.validateAccessToLocations() (apache#1480)

* Remove unused PolarisCredentialVendor.validateAccessToLocations()

* review: remove ValidateAccessResult and comments

* Policy Store: Check whether Policy is in use before dropping and support `detach-all` flag (apache#1467)

* fix error (apache#1492)

* Ensure writeToPolicyMappingRecord update existing record if primary key equals in EclipseLink Persistence Impl (apache#1469)

* update PolicyMappingRecord if not exists

* update test

* add TODO

* Eliminate getCurrentContext() call in PolarisAuthorizerImpl (apache#1494)

* Add getting-started for Polaris Spark Client with Delta tables (apache#1488)

* Fix: Pull Postgres image automatically (apache#1495)

* Fix Outdated Information and add Information regarding `docker compose down` to Quickstart  (apache#1497)

* Fix Outdated Information and Add Information regarding docker compose down to Quickstart

* Revision 2

* Remove shutdown from README

* typo

* Upgrade Iceberg REST Spec to match Iceberg 1.8 (apache#1283)

* prep for review

* reset

* more changes

* fixes

* github action change

* another build change

* try api revert

* re-all

* custom type mappings, rebuild

* autolint

* polish

* yank custom types

* update

* autolint

* wip

* Revert build changes

* example

* autolint

* Fix FileIOExceptionsTest to conform to new Iceberg 1.8 API (apache#1501)

It looks like after apache#1283, this test no longer compiles as the Iceberg API has changed. I'm not sure how this wasn't caught by CI on that PR itself.

* JDBC: Optimize writeEntity calls (apache#1496)

* Remove transaction from atomic writes

* remove if-else

* main: Update registry.access.redhat.com/ubi9/openjdk-21-runtime Docker tag to v1.22-1.1745840590 (apache#1499)

* Support for external identity providers (apache#1397)

* JDBC: create objects without reflection (apache#1434)

* Include quarkus-container-image and README in the binary distributions (apache#1493)

* Site: Fix Management and Catalog Spec links (apache#1507)

* Lazy iteration over JDBC ResultSet (apache#1487)

* refactor

* autolint

* polish

* autolint

* changes per review

* autolint

* unwrapping caller

* changes per review

* Update distributions LICENSE and NOTICE with artifacts and versions sync (apache#1509)

* Avoid using deprecated `NestedField.of()` (apache#1514)

* Fix compile warning: unknown enum constant Id.NAME (apache#1513)

* Doc: Add getting started with JDBC source (apache#1470)

* Site: Add Polaris Spark client webpage under unreleased (apache#1503)

* Add new committers (apache#1518)

* Docs: Fix the wrong catalog name in `using polaris` page (apache#1471)

* fix

Signed-off-by: owenowenisme <mses010108@gmail.com>

* update docker compose

Signed-off-by: owenowenisme <mses010108@gmail.com>

---------

Signed-off-by: owenowenisme <mses010108@gmail.com>

* main: Update dependency org.apache.commons:commons-configuration2 to v2.12.0 (apache#1481)

* main: Update dependency com.google.cloud:google-cloud-storage-bom to v2.52.1 (apache#1485)

* main: Update dependency com.azure:azure-sdk-bom to v1.2.34 (apache#1490)

* main: Update docker.io/prom/prometheus Docker tag to v3.3.1 (apache#1510)

* Add new committers on website (apache#1521)

* main: Update dependency software.amazon.awssdk:bom to v2.31.35 (apache#1524)

* fix overlapping menu item on the nav bar (apache#1520)

* fix overlapping menu item on the nav bar

* prevent dropdowns expanding inside the navbar

* Additional refs update for iceberg 1.9.0 (apache#1491)

* Additional refs update for iceberg 1.9.0

* Additional refs update for iceberg 1.9.0

* Additional refs update for iceberg 1.9.0

* Fix typo on Pierre's github URL (apache#1527)

* Refactor storage access configuration handling (apache#1504)

* Refactor storage access configuration handling

This is a step towards supporting non-AWS S3 storage, but this
refactoring is relevant to all storage backends.

There is no change to existing behaviours.

* Rename PolarisCredentialProperty to StorageAccessProperty
  and introduce non-credential properties (as an example for now)

* StorageAccessProperty values are ultimately meant to be
  produced by PolarisStorageIntegration implementations

* Some previous entries in StorageAccessProperty are not really
  credential properties, but their treatment is not changed in this
  PR to maintain exactly the same bahaviour as before.

* Add AccessConfig to represent both credential and non-credential
  properties related to storage access.

* [JDBC] : Deprecate EclipseLink (apache#1515)

* Auto-bootstrap: add verbose logging (apache#1376)

Log explicit messages around auto-bootstrapping and unnecessary/left-over secrets that are (still) available.

* Add nightly build GH action to publish SNAPSHOT on Nexus (apache#1383)

* Add nightly build GH action to publish SNAPSHOT on Nexus (apache#1383)

* Build: Fix `fetchAsfProjectName` and make the publishing extension more flexible (apache#1442)

The added flexibility is intended to be ported to the multiple project in the polaris-tools repository.

(Follow up of apache#1384)

* Poetry v2 (apache#898)

* PEP 621 and Poetry v2

* PEP 621 and Poetry v2

* Update min python to 3.9

* Add back flask8 for apache#1096

* Add Integration tests for Delta tables for Spark Client (apache#1500)

* main: Update dependency com.google.cloud:google-cloud-storage-bom to v2.52.2 (apache#1536)

* main: Update dependency poetry to v2.1.3 (apache#1534)

* main: Update dependency io.netty:netty-codec-http2 to v4.2.1.Final (apache#1533)

* main: Update dependency boto3 to v1.38.10 (apache#1525)

* Fix test failure (apache#1541)

* Fix the URL of the KEYS file in the release vote email template (apache#1538)

* Event Listeners (apache#922)

Implementation of event listeners discussed [here](https://lists.apache.org/thread/03yz5wolkvy8l7rbcwjnqdq1bl8p065v).

I decided to keep this implementation generic and not take a dependency on Jakarta Events nor Vertx busses. It's easy to extend this, either within Polaris or in an external PolarisEventListener, and handle events however one wishes.

Some high level notes:
- PolarisEventListener is the main interface with all the event methods such as `onBeforeRequestRateLimited`
- DefaultPolarisEventListener is an empty implementation which allows users to only partially implement event handlers
- `polaris.events.type` is the config that lets you specify your event listener implementation

* Update metastores.md (apache#1537)

* Update metastores.md

* Resolve comment.

* Resolve comment.

---------

Co-authored-by: Yufei Gu <yufei.apache.org>

* Doc: Document the Concept of realm (apache#1478)

* main: Update dependency boto3 to v1.38.11 (apache#1542)

* Fix compile warning: [unchecked] unchecked cast (apache#1544)

Use `Class.cast()` instead of implicit cast.

* NoSQL: Adopt to "Make entity lookups by id honor the specified entity type (apache#1401)"

* NoSQL: Filter on correct subtype

* NoSQL: merge/rebase 2025/04/30

* additional merge-relaged changes

---------

Signed-off-by: owenowenisme <mses010108@gmail.com>
Co-authored-by: Honah (Jonas) J. <honahx@apache.org>
Co-authored-by: gh-yzou <167037035+gh-yzou@users.noreply.github.com>
Co-authored-by: Mansehaj Singh <msehajs@gmail.com>
Co-authored-by: Mend Renovate <bot@renovateapp.com>
Co-authored-by: JB Onofré <jbonofre@apache.org>
Co-authored-by: Alexandre Dutra <adutra@users.noreply.github.com>
Co-authored-by: Yufei Gu <yufei@apache.org>
Co-authored-by: Eric Maynard <eric.maynard+oss@snowflake.com>
Co-authored-by: Dennis Huo <7410123+dennishuo@users.noreply.github.com>
Co-authored-by: Prashant Singh <35593236+singhpk234@users.noreply.github.com>
Co-authored-by: Travis Bowen <122238243+travis-bowen@users.noreply.github.com>
Co-authored-by: Travis Bowen <travis.bowen@snowflake.com>
Co-authored-by: Dmitri Bourlatchkov <dmitri.bourlatchkov@dremio.com>
Co-authored-by: Juichang Lu <wolflex888@gmail.com>
Co-authored-by: David Lu <dalu@hubspot.com>
Co-authored-by: gfakbar20 <gfakbar20@gmail.com>
Co-authored-by: Liam Bao <90495036+liamzwbao@users.noreply.github.com>
Co-authored-by: Adnan Hemani <adnan.h@berkeley.edu>
Co-authored-by: Adnan Hemani <adnan.hemani@snowflake.com>
Co-authored-by: Neelesh Salian <nssalian@users.noreply.github.com>
Co-authored-by: Rulin Xing <rulin.xing+oss@snowflake.com>
Co-authored-by: Rulin Xing <xjdkcsq3@gmail.com>
Co-authored-by: fabio-rizzo-01 <fabio.rizzocascio@jpmorgan.com>
Co-authored-by: Pierre Laporte <pierre@pingtimeout.fr>
Co-authored-by: Richard Liu <35082658+RichardLiu2001@users.noreply.github.com>
Co-authored-by: Michael Collado <40346148+collado-mike@users.noreply.github.com>
Co-authored-by: Owen Lin (You-Cheng Lin) <106612301+owenowenisme@users.noreply.github.com>
Co-authored-by: Eric Maynard <emaynard@apache.org>
Co-authored-by: Andrew Guterman <andrew.guterman1@gmail.com>
Co-authored-by: MonkeyCanCode <yongzheng0809@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FEATURE REQUEST] External IdP / Token Service
3 participants