Skip to content
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

feat(oidc): email claim configuration #324

Merged
merged 3 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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/
Expand Down Expand Up @@ -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]
Expand Down
Original file line number Diff line number Diff line change
@@ -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/
Expand Down Expand Up @@ -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";

Expand Down
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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;
Expand Down Expand Up @@ -40,19 +41,22 @@
*
* <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
extends MultiUserEnvironmentInitializationFilter<Jws<Claims>> {
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(
Expand All @@ -61,12 +65,14 @@ public 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
Expand All @@ -86,7 +92,7 @@ protected Subject extractSubject(String token, Jws<Claims> processedToken) {
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);
Expand Down
Original file line number Diff line number Diff line change
@@ -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/
Expand All @@ -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;
Expand Down Expand Up @@ -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";
Expand All @@ -66,11 +68,12 @@ public void setUp() {
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);
}

Expand Down Expand Up @@ -124,7 +127,8 @@ public void testDefaultUsernameClaimWhenEmpty(String customUsernameClaim)
sessionStore,
tokenExtractor,
userManager,
customUsernameClaim);
customUsernameClaim,
emailClaim);
User createdUser = mock(User.class);
when(createdUser.getId()).thenReturn(TEST_USERID);
when(createdUser.getName()).thenReturn(TEST_USERNAME);
Expand All @@ -142,8 +146,42 @@ public void testDefaultUsernameClaimWhenEmpty(String customUsernameClaim)
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}};
}
}