Add support for configurable JWT principal fields (#3599)

This commit is contained in:
mosiac1 2022-03-24 19:49:53 +00:00 committed by GitHub
parent f74b5d77bc
commit beaeb41022
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 43 additions and 23 deletions

View File

@ -13,6 +13,7 @@
package org.openmetadata.catalog.security;
import java.util.List;
import lombok.Getter;
import lombok.Setter;
@ -22,7 +23,7 @@ public class AuthenticationConfiguration {
@Getter @Setter private String authority;
@Getter @Setter private String clientId;
@Getter @Setter private String callbackUrl;
@Getter @Setter private String jwtEmail = "email";
@Getter @Setter private List<String> jwtPrincipalClaims = List.of("email", "preferred_username", "sub");
@Override
public String toString() {
@ -42,6 +43,8 @@ public class AuthenticationConfiguration {
+ ", callbackUrl='"
+ callbackUrl
+ '\''
+ ", jwtPrincipalClaims="
+ jwtPrincipalClaims
+ '}';
}
}

View File

@ -23,6 +23,7 @@ import io.dropwizard.util.Strings;
import java.net.URI;
import java.security.interfaces.RSAPublicKey;
import java.util.Calendar;
import java.util.List;
import java.util.TimeZone;
import javax.ws.rs.container.ContainerRequestContext;
import javax.ws.rs.container.ContainerRequestFilter;
@ -40,14 +41,14 @@ public class JwtFilter implements ContainerRequestFilter {
public static final String AUTHORIZATION_HEADER = "Authorization";
public static final String TOKEN_PREFIX = "Bearer";
private String publicKeyUri;
private String jwtEmail;
private List<String> jwtPrincipalClaims;
@SuppressWarnings("unused")
private JwtFilter() {}
public JwtFilter(AuthenticationConfiguration authenticationConfiguration) {
this.publicKeyUri = authenticationConfiguration.getPublicKey();
this.jwtEmail = authenticationConfiguration.getJwtEmail();
this.jwtPrincipalClaims = authenticationConfiguration.getJwtPrincipalClaims();
}
@SneakyThrows
@ -70,6 +71,7 @@ public class JwtFilter implements ContainerRequestFilter {
if (jwt.getExpiresAt().before(Calendar.getInstance(TimeZone.getTimeZone("UTC")).getTime())) {
throw new AuthenticationException("Expired token!");
}
// Validate JWT with public key
final URI uri = new URI(publicKeyUri).normalize();
UrlJwkProvider urlJwkProvider = new UrlJwkProvider(uri.toURL());
@ -80,24 +82,27 @@ public class JwtFilter implements ContainerRequestFilter {
} catch (RuntimeException runtimeException) {
throw new AuthenticationException("Invalid token");
}
String authorizedEmail;
if (jwt.getClaims().get(jwtEmail) != null) {
authorizedEmail = jwt.getClaim(jwtEmail).as(TextNode.class).asText();
} else if (jwt.getClaims().get("email") != null) {
authorizedEmail = jwt.getClaim("email").as(TextNode.class).asText();
} else if (jwt.getClaims().get("preferred_username") != null) {
authorizedEmail = jwt.getClaim("preferred_username").as(TextNode.class).asText();
} else if (jwt.getClaim("sub") != null) {
authorizedEmail = jwt.getClaim("sub").as(TextNode.class).asText();
} else {
throw new AuthenticationException("Invalid JWT token, \"email\" or \"subject\" not present.");
}
String userName;
if (authorizedEmail.contains("@")) {
userName = authorizedEmail.split("@")[0];
} else {
userName = authorizedEmail;
}
// Get username from JWT token
String userName =
jwtPrincipalClaims.stream()
.filter(jwt.getClaims()::containsKey)
.findFirst()
.map(jwt::getClaim)
.map(claim -> claim.as(TextNode.class).asText())
.map(
authorizedClaim -> {
if (authorizedClaim.contains("@")) {
return authorizedClaim.split("@")[0];
} else {
return authorizedClaim;
}
})
.orElseThrow(
() ->
new AuthenticationException(
"Invalid JWT token, none of the following claims are present " + jwtPrincipalClaims));
// Setting Security Context
CatalogPrincipal catalogPrincipal = new CatalogPrincipal(userName);
String scheme = requestContext.getUriInfo().getRequestUri().getScheme();

View File

@ -15,6 +15,10 @@ authenticationConfiguration:
callbackUrl: "http://localhost:8585/callback"
```
* (Optional) Set the user principal fields
`authenticationConfiguration.jwtPrincipalClaims` - Sets fields of the access token used for the OpenMetadata user. First of these fields that is present in the token is used. Default is `['email', 'preferred_username', 'sub']`.
* Update authorizerConfiguration to add adminPrincipals
```

View File

@ -2,7 +2,7 @@
## Update conf/openmetadata-security.yaml
Once the `client id` and `client secret` are generated, add `client id` as the value of the `clientId` field in the openmetadata-security.yaml file. See the snippet below for an example of where to place the `client id` value.
* Once the `client id` and `client secret` are generated, add `client id` as the value of the `clientId` field in the openmetadata-security.yaml file. See the snippet below for an example of where to place the `client id` value.
```
authenticationConfiguration:
@ -13,7 +13,11 @@ authenticationConfiguration:
callbackUrl: "http://localhost:8585/callback"
```
Then, update authorizerConfiguration to add adminPrincipals.
* (Optional) Set the user principal fields
`authenticationConfiguration.jwtPrincipalClaims` - Sets fields of the access token used for the OpenMetadata user. First of these fields that is present in the token is used. Default is `['email', 'preferred_username', 'sub']`.
* Then, update authorizerConfiguration to add adminPrincipals.
```
authorizerConfiguration:

View File

@ -13,6 +13,10 @@ authenticationConfiguration:
callbackUrl: "http://localhost:8585/callback"
```
* (Optional) Set the user principal fields
`authenticationConfiguration.jwtPrincipalClaims` - Sets fields of the access token used for the OpenMetadata user. First of these fields that is present in the token is used. Default is `['email', 'preferred_username', 'sub']`.
* Update `authorizerConfiguration` to add `adminPrincipals`
```