Skip to content

Commit

Permalink
Support user creation for Oicd authentication
Browse files Browse the repository at this point in the history
This is similar to UserDetailImplOAuth2UserService though in that case it is simpler and just reuses the parent class to get an OicdUser and then convert it into a UserDetailsImpl.

Add a username-from-email config. In case the principal ID form the auth provider is a UUID, it is of little use on the Mojito side for tasks such as sending notifications, associating with Git commit, PRs, etc. With this option, the username is extracted from the email. This assumes a 1:1 mapping between the email and the Unix username

Considered saving the email in the user, but for simplicity we just need a username that is unix like for now.

Note: UserDetailImplOAuth2UserService probably duplicated the code to support the "unwrapping" of the user attribute. The logic was probably failing before being able to do the conversion
  • Loading branch information
ja-openai committed Sep 13, 2024
1 parent 5a062e3 commit 410ef5e
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.box.l10n.mojito.security;

import com.box.l10n.mojito.entity.security.user.User;
import java.util.Map;
import org.springframework.security.oauth2.core.oidc.OidcIdToken;
import org.springframework.security.oauth2.core.oidc.OidcUserInfo;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;

public class OidcUserDetailsImpl extends UserDetailsImpl implements OidcUser {

private final OidcUser oidcUser;

public OidcUserDetailsImpl(User user, OidcUser oidcUser) {
super(user);
this.oidcUser = oidcUser;
}

@Override
public Map<String, Object> getClaims() {
return oidcUser.getClaims();
}

@Override
public OidcUserInfo getUserInfo() {
return oidcUser.getUserInfo();
}

@Override
public OidcIdToken getIdToken() {
return oidcUser.getIdToken();
}

@Override
public Map<String, Object> getAttributes() {
return oidcUser.getAttributes();
}

@Override
public String getName() {
return oidcUser.getName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ public static class OAuth2 {

String unwrapUserAttributes;

/**
* In case the principal ID form the auth provider is a UUID, it is of little use on the Mojito
* side for tasks such as sending notifications, associating with Git commit, PRs, etc.
*
* <p>With this option, the username is extracted from the email. This assumes a 1:1 mapping
* between the email and the Unix username
*
* <p>Currently only implement in {@link UserDetailImplOidcUserService}
*/
boolean usernameFromEmail = false;

String givenNameAttribute = "first_name";
String surnameAttribute = "last_name";
String commonNameAttribute = "name";
Expand Down Expand Up @@ -115,5 +126,13 @@ public String getUnwrapUserAttributes() {
public void setUnwrapUserAttributes(String unwrapUserAttributes) {
this.unwrapUserAttributes = unwrapUserAttributes;
}

public boolean isUsernameFromEmail() {
return usernameFromEmail;
}

public void setUsernameFromEmail(boolean usernameFromEmail) {
this.usernameFromEmail = usernameFromEmail;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.box.l10n.mojito.security;

import com.box.l10n.mojito.entity.security.user.User;
import com.box.l10n.mojito.service.security.user.UserService;
import java.util.Objects;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserRequest;
import org.springframework.security.oauth2.client.oidc.userinfo.OidcUserService;
import org.springframework.security.oauth2.core.OAuth2AuthenticationException;
import org.springframework.security.oauth2.core.oidc.user.OidcUser;

class UserDetailImplOidcUserService extends OidcUserService {

SecurityConfig securityConfig;
UserService userService;

public UserDetailImplOidcUserService(SecurityConfig securityConfig, UserService userService) {
this.securityConfig = Objects.requireNonNull(securityConfig);
this.userService = Objects.requireNonNull(userService);
}

@Override
public OidcUser loadUser(OidcUserRequest userRequest) throws OAuth2AuthenticationException {
OidcUser oidcUser = super.loadUser(userRequest);

SecurityConfig.OAuth2 securityConfigOAuth2 =
securityConfig
.getoAuth2()
.getOrDefault(
userRequest.getClientRegistration().getRegistrationId(),
new SecurityConfig.OAuth2());

String username;
if (securityConfigOAuth2.usernameFromEmail) {
String email = oidcUser.getEmail();
if (email == null) {
throw new RuntimeException(
"OidcUser's email must not be null since it used to extract username");
}
username = email.split("@")[0];
} else {
username = oidcUser.getName();
}

User user =
userService.getOrCreateOrUpdateBasicUser(
username, oidcUser.getGivenName(), oidcUser.getFamilyName(), oidcUser.getFullName());

return new OidcUserDetailsImpl(user, oidcUser);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,9 @@ public SecurityFilterChain configure(HttpSecurity http) throws Exception {
userInfoEndpoint -> {
userInfoEndpoint.userService(
new UserDetailImplOAuth2UserService(securityConfig, userService));

userInfoEndpoint.oidcUserService(
new UserDetailImplOidcUserService(securityConfig, userService));
});
});
break;
Expand Down

0 comments on commit 410ef5e

Please sign in to comment.