feat(oidc): email claim configuration

pull/324/head
Piotr Karatkevich 2022-06-29 17:33:54 +03:00
parent 4e1b90a5ae
commit 6d2907e226
4 changed files with 58 additions and 9 deletions

View File

@ -110,6 +110,10 @@ che.oidc.allowed_clock_skew_sec=3
# `name` in Dex installations.
che.oidc.username_claim=NULL
# Email claim to be used when parsing JWT token
# if not defined the fallback value is 'email'.
che.oidc.email_claim=NULL
# Base URL of an alternate OIDC provider that provides
# a discovery endpoint as detailed in the following specification
# link:https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig[Obtaining OpenID Provider Configuration Information]

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -43,6 +43,7 @@ public class OIDCInfoProvider implements Provider<OIDCInfo> {
OIDC_SETTING_PREFIX + "auth_internal_server_url";
public static final String OIDC_PROVIDER_SETTING = OIDC_SETTING_PREFIX + "oidc_provider";
public static final String OIDC_USERNAME_CLAIM_SETTING = OIDC_SETTING_PREFIX + "username_claim";
public static final String OIDC_EMAIL_CLAIM_SETTING = OIDC_SETTING_PREFIX + "email_claim";
public static final String OIDC_ALLOWED_CLOCK_SKEW_SEC =
OIDC_SETTING_PREFIX + "allowed_clock_skew_sec";

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -12,6 +12,7 @@
package org.eclipse.che.multiuser.oidc.filter;
import static com.google.common.base.Strings.isNullOrEmpty;
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_EMAIL_CLAIM_SETTING;
import static org.eclipse.che.multiuser.oidc.OIDCInfoProvider.OIDC_USERNAME_CLAIM_SETTING;
import io.jsonwebtoken.Claims;
@ -40,7 +41,8 @@ import org.eclipse.che.multiuser.api.permission.server.PermissionChecker;
*
* <p>It also makes sure that User is present in Che database. If not, it will create the User from
* JWT token claims. The username claim is configured with {@link
* org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_USERNAME_CLAIM_SETTING}.
* org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_USERNAME_CLAIM_SETTING}. The email claim is
* configured with {@link org.eclipse.che.multiuser.oidc.OIDCInfoProvider#OIDC_EMAIL_CLAIM_SETTING}.
*/
@Singleton
public class OidcTokenInitializationFilter
@ -48,11 +50,13 @@ public class OidcTokenInitializationFilter
private static final String EMAIL_CLAIM = "email";
private static final String NAME_CLAIM = "name";
protected static final String DEFAULT_USERNAME_CLAIM = NAME_CLAIM;
protected static final String DEFAULT_EMAIL_CLAIM = EMAIL_CLAIM;
private final JwtParser jwtParser;
private final PermissionChecker permissionChecker;
private final UserManager userManager;
private final String usernameClaim;
private final String emailClaim;
@Inject
public OidcTokenInitializationFilter(
@ -61,12 +65,14 @@ public class OidcTokenInitializationFilter
SessionStore sessionStore,
RequestTokenExtractor tokenExtractor,
UserManager userManager,
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim) {
@Nullable @Named(OIDC_USERNAME_CLAIM_SETTING) String usernameClaim,
@Nullable @Named(OIDC_EMAIL_CLAIM_SETTING) String emailClaim) {
super(sessionStore, tokenExtractor);
this.permissionChecker = permissionChecker;
this.jwtParser = jwtParser;
this.userManager = userManager;
this.usernameClaim = isNullOrEmpty(usernameClaim) ? DEFAULT_USERNAME_CLAIM : usernameClaim;
this.emailClaim = isNullOrEmpty(emailClaim) ? DEFAULT_EMAIL_CLAIM : emailClaim;
}
@Override
@ -86,7 +92,7 @@ public class OidcTokenInitializationFilter
User user =
userManager.getOrCreateUser(
claims.getSubject(),
claims.get(EMAIL_CLAIM, String.class),
claims.get(emailClaim, String.class),
claims.get(usernameClaim, String.class));
return new AuthorizedSubject(
new SubjectImpl(user.getName(), user.getId(), token, false), permissionChecker);

View File

@ -1,5 +1,5 @@
/*
* Copyright (c) 2012-2021 Red Hat, Inc.
* Copyright (c) 2012-2022 Red Hat, Inc.
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
@ -11,6 +11,7 @@
*/
package org.eclipse.che.multiuser.oidc.filter;
import static org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_EMAIL_CLAIM;
import static org.eclipse.che.multiuser.oidc.filter.OidcTokenInitializationFilter.DEFAULT_USERNAME_CLAIM;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.mock;
@ -46,6 +47,7 @@ public class OidcTokenInitializationFilterTest {
@Mock private RequestTokenExtractor tokenExtractor;
@Mock private UserManager userManager;
private final String usernameClaim = "blabolClaim";
private final String emailClaim = "pafturClaim";
private final String TEST_USERNAME = "jondoe";
private final String TEST_USER_EMAIL = "jon@doe";
private final String TEST_USERID = "jon1337";
@ -66,11 +68,12 @@ public class OidcTokenInitializationFilterTest {
sessionStore,
tokenExtractor,
userManager,
usernameClaim);
usernameClaim,
emailClaim);
lenient().when(jwsClaims.getBody()).thenReturn(claims);
lenient().when(claims.getSubject()).thenReturn(TEST_USERID);
lenient().when(claims.get("email", String.class)).thenReturn(TEST_USER_EMAIL);
lenient().when(claims.get(emailClaim, String.class)).thenReturn(TEST_USER_EMAIL);
lenient().when(claims.get(usernameClaim, String.class)).thenReturn(TEST_USERNAME);
}
@ -124,7 +127,8 @@ public class OidcTokenInitializationFilterTest {
sessionStore,
tokenExtractor,
userManager,
customUsernameClaim);
customUsernameClaim,
emailClaim);
User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME);
@ -142,8 +146,42 @@ public class OidcTokenInitializationFilterTest {
verify(claims, never()).get(usernameClaim, String.class);
}
@Test(dataProvider = "emailClaims")
public void testDefaultEmailClaimWhenEmpty(String customEmailClaim)
throws ServerException, ConflictException {
tokenInitializationFilter =
new OidcTokenInitializationFilter(
permissionsChecker,
jwtParser,
sessionStore,
tokenExtractor,
userManager,
usernameClaim,
customEmailClaim);
User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME);
when(userManager.getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME))
.thenReturn(createdUser);
when(claims.get(DEFAULT_EMAIL_CLAIM, String.class)).thenReturn(TEST_USER_EMAIL);
var subject = tokenInitializationFilter.extractSubject(TEST_TOKEN, jwsClaims);
assertEquals(subject.getUserId(), TEST_USERID);
assertEquals(subject.getUserName(), TEST_USERNAME);
assertEquals(subject.getToken(), TEST_TOKEN);
verify(userManager).getOrCreateUser(TEST_USERID, TEST_USER_EMAIL, TEST_USERNAME);
verify(claims).get(DEFAULT_EMAIL_CLAIM, String.class);
verify(claims, never()).get(emailClaim, String.class);
}
@DataProvider
public static Object[][] usernameClaims() {
return new Object[][] {{""}, {null}};
}
@DataProvider
public static Object[][] emailClaims() {
return new Object[][] {{""}, {null}};
}
}