adutra opened a new pull request, #1397:
URL: https://github.com/apache/polaris/pull/1397

   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 following authentication type is also present, but I would be in favor 
of removing it:
   
   - `test`: The old `TestInlineBearerTokenAuthenticator`, currently unused in 
Polaris
   
   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:
   
   ```properties
   # 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 `PolarisCredential` interface has been introduced to hold the decoded 
JWT and pass it to the authenticator. The existing `DecodedToken` interface, 
which is used for internal authentication, is now a sub-type of 
`PolarisCredential`.
   
   **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 `JWTBroker` to decode the token
     - `InternalIdentityProvider` creates a first SecurityIdentity with the 
token as a `PolarisCredential`
     - `AuthenticatingAugmentor` authenticates the `PolarisCredential`
       - 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
     - `ClaimsMappingAugmentor` maps the JWT claims to a `PolarisCredential` 
and adds it to the SecurityIdentity
     - `AuthenticatingAugmentor` authenticates the `PolarisCredential`
       - 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 `JWTBroker` to decode the token
     - If the token is issued by Polaris:
       - `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
       - `ClaimsMappingAugmentor` maps the JWT claims to a `PolarisCredential` 
and adds it to the SecurityIdentity
     - For both cases: `AuthenticatingAugmentor` authenticates the 
`PolarisCredential`
       - Calls `Authenticator.authenticate()`
       - Sets `AuthenticatedPolarisPrincipal` as the SecurityIdentity's 
principal
     - `ActiveRolesAugmentor` sets the active roles in the SecurityIdentity
       - Calls `ActiveRolesProvider`
   
   ### OIDC Claims Mapping
   
   Currently, the following `polaris.oidc` options are available:
   
   * `polaris.oidc.claims.principal-id-claim-path`: the claim path to the 
principal id in the JWT token, e.g. `polaris/principal_id`
   * `polaris.oidc.claims.principal-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.:
   
   ```properties
   
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.claims.principal-id-claim-path=polaris/principal_id
   
polaris.oidc.oidc-tenant1.claims.principal-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.
   
   ### OIDC Roles Mapping
   
   Roles will be mapped from the JWT by Quarkus OIDC. This is done by setting 
the `quarkus.oidc.roles.role-claim-path` property:
   
   ```properties
   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.
   
   ### Mapping Examples 
   
   Example: for a JWT token like this (some claims omitted for brevity):
   
   ```json
   {
     "sub": "a663c299-1118-4b35-ac85-f9a4f38d0699",
     "typ": "Bearer",
     "azp": "client1",
     "scope": "profile email",
     "polaris": {
       "roles": [ "PRINCIPAL_ROLE:ALL" ],
       "principal_name": "root",
       "principal_id": 0
     },
     "preferred_username": "service-account-client1",
     "clientAddress": "172.18.0.1",
     "client_id": "client1"
   }
   ```
   
   You can configure Polaris like this:
   
   ```properties
   quarkus.oidc.roles.role-claim-path=polaris/roles
   polaris.oidc.claims.principal-id-claim-path=polaris/principal_id
   polaris.oidc.claims.principal-name-claim-path=polaris/principal_name
   ```
   
   Another example; if the JWT token is like this:
   
   ```json
   {
     "sub": "0", // principal id
     "typ": "Bearer",
     "azp": "client1",
     "scope": "PRINCIPAL_ROLE:service_admin PRINCIPAL_ROLE:catalog_admin 
profile email",
     "preferred_username": "root",
     "clientAddress": "172.18.0.1",
     "client_id": "client1"
   }
   ```
   
   You can configure Polaris like this:
   
   ```properties
   quarkus.oidc.roles.role-claim-path=scope
   polaris.oidc.claims.principal-id-claim-path=sub
   polaris.oidc.claims.principal-name-claim-path=preferred_username
   ```
   


-- 
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