Skip to content

Commit

Permalink
Adding roles & department name to JWT (#50)
Browse files Browse the repository at this point in the history
Co-authored-by: Dominic West <[email protected]>
  • Loading branch information
dominicwest and DomWestAnd authored Jul 17, 2023
1 parent 00c0c1a commit c393bab
Show file tree
Hide file tree
Showing 4 changed files with 84 additions and 14 deletions.
4 changes: 4 additions & 0 deletions src/main/java/gov/cabinetofice/gapuserservice/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ public boolean hasEmail() {
return this.email != null;
}

public boolean hasDepartment() {
return this.department != null;
}

public boolean isApplicant() {
return !isAdmin() && this.roles.stream().anyMatch((role) -> role.getName().equals(RoleEnum.APPLICANT));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gov.cabinetofice.gapuserservice.config.ApplicationConfigProperties;
import gov.cabinetofice.gapuserservice.dto.OneLoginUserInfoDto;
import gov.cabinetofice.gapuserservice.model.Role;
import gov.cabinetofice.gapuserservice.model.User;
import gov.cabinetofice.gapuserservice.service.OneLoginService;
import gov.cabinetofice.gapuserservice.service.jwt.impl.CustomJwtServiceImpl;
Expand Down Expand Up @@ -31,6 +32,7 @@
@Controller
@RequestMapping("v2")
@ConditionalOnProperty(value = "feature.onelogin.enabled", havingValue = "true")
@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
public class LoginControllerV2 {

private final OneLoginService oneLoginService;
Expand All @@ -46,7 +48,6 @@ public class LoginControllerV2 {
@Value("${admin-base-url}")
private String adminBaseUrl;

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@GetMapping("/login")
public RedirectView login(final @RequestParam Optional<String> redirectUrl,
final HttpServletRequest request,
Expand All @@ -73,7 +74,6 @@ public RedirectView login(final @RequestParam Optional<String> redirectUrl,
return new RedirectView(redirectUrl.orElse(configProperties.getDefaultRedirectUrl()));
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
@GetMapping("/redirect-after-login")
public RedirectView redirectAfterLogin(final @CookieValue(name = REDIRECT_URL_COOKIE) Optional<String> redirectUrl,
final HttpServletResponse response,
Expand All @@ -83,7 +83,7 @@ public RedirectView redirectAfterLogin(final @CookieValue(name = REDIRECT_URL_CO
final OneLoginUserInfoDto userInfo = oneLoginService.getUserInfo(authToken);
final Optional<User> userOptional = oneLoginService.getUser(userInfo.getEmail(), userInfo.getSub());

final Cookie customJwt = generateCustomJwtCookie(userInfo);
final Cookie customJwt = generateCustomJwtCookie(userInfo, userOptional);
response.addCookie(customJwt);

if (userOptional.isPresent()) {
Expand All @@ -103,12 +103,20 @@ public RedirectView redirectAfterLogin(final @CookieValue(name = REDIRECT_URL_CO
return getRedirectView(user, redirectUrl);
}

private Cookie generateCustomJwtCookie(final OneLoginUserInfoDto userInfo) {
private Cookie generateCustomJwtCookie(final OneLoginUserInfoDto userInfo, final Optional<User> userOptional) {
final Map<String, String> claims = new HashMap<>();
claims.put("email", userInfo.getEmail());
claims.put("sub", userInfo.getSub());
// TODO Add roles to claims
// TODO Add department details to claims

if(userOptional.isPresent()) {
final User user = userOptional.get();
claims.put("roles", user.getRoles().stream().map(Role::getName).toList().toString());
if (user.hasDepartment()) {
claims.put("department", user.getDepartment().getName());
}
} else {
claims.put("roles", oneLoginService.getNewUserRoles().toString());
}

return WebUtil.buildCookie(
new Cookie(userServiceCookieName, customJwtService.generateToken(claims)),
Expand All @@ -118,7 +126,6 @@ private Cookie generateCustomJwtCookie(final OneLoginUserInfoDto userInfo) {
);
}

@SuppressWarnings("OptionalUsedAsFieldOrParameterType")
private RedirectView getRedirectView(final User user, final Optional<String> redirectUrl) {
if(user.isSuperAdmin()) return new RedirectView(adminBaseUrl + "/super-admin/dashboard");
if(user.isAdmin()) return new RedirectView(adminBaseUrl + "/dashboard");
Expand Down
21 changes: 21 additions & 0 deletions src/test/java/gov/cabinetofice/gapuserservice/model/UserTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,25 @@ void shouldReturnFalseWhenUserDoesNotHaveSub() {
Assertions.assertFalse(response);
}
}

@Nested
class hasDepartment {
@Test
void shouldReturnTrueWhenUserHasDepartment() {
final User user = User.builder().department(Department.builder().build()).build();

final boolean response = user.hasDepartment();

Assertions.assertTrue(response);
}

@Test
void shouldReturnFalseWhenUserDoesNotHaveDepartment() {
final User user = User.builder().build();

final boolean response = user.hasDepartment();

Assertions.assertFalse(response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import gov.cabinetofice.gapuserservice.config.ApplicationConfigProperties;
import gov.cabinetofice.gapuserservice.dto.OneLoginUserInfoDto;
import gov.cabinetofice.gapuserservice.model.Department;
import gov.cabinetofice.gapuserservice.model.Role;
import gov.cabinetofice.gapuserservice.model.RoleEnum;
import gov.cabinetofice.gapuserservice.model.User;
Expand All @@ -21,7 +22,9 @@
import org.springframework.web.servlet.view.RedirectView;
import org.springframework.web.util.WebUtils;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

import static org.assertj.core.api.AssertionsForClassTypes.assertThat;
Expand Down Expand Up @@ -149,29 +152,45 @@ void shouldCreateNewUser_WhenNoUserFound() {
.thenReturn(Optional.empty());
when(oneLoginService.createUser("sub", "email"))
.thenReturn(User.builder().build());
when(oneLoginService.getNewUserRoles())
.thenReturn(List.of(RoleEnum.APPLICANT, RoleEnum.FIND));

final RedirectView methodResponse = loginController.redirectAfterLogin(redirectUrl, response, "a-custom-valid-token");

verify(response).addCookie(customJwtCookie);
verify(oneLoginService).createUser("sub", "email");
assertThat(methodResponse.getUrl()).isEqualTo(redirectUrl.get());

verify(response).addCookie(customJwtCookie);
final Map<String, String> claims = new HashMap<>();
claims.put("sub", "sub");
claims.put("email", "email");
claims.put("roles", "[APPLICANT, FIND]");
verify(customJwtService).generateToken(claims);
}

@Test
void shouldDoNothing_WhenUserFoundWithSub() {
final HttpServletResponse response = Mockito.spy(new MockHttpServletResponse());
final Optional<String> redirectUrl = Optional.of("redirectUrl");
final User user = User.builder().sub("sub").email("email").build();
final User user = User.builder().sub("sub").email("email").roles(List.of(
Role.builder().name(RoleEnum.APPLICANT).build()
)).build();

when(oneLoginService.getUser("email", "sub"))
.thenReturn(Optional.of(user));

final RedirectView methodResponse = loginController.redirectAfterLogin(redirectUrl, response, "a-custom-valid-token");

verify(response).addCookie(customJwtCookie);
verify(oneLoginService, times(0)).createUser(anyString(), anyString());
verify(oneLoginService, times(0)).addSubToUser(anyString(), anyString());
assertThat(methodResponse.getUrl()).isEqualTo(redirectUrl.get());

verify(response).addCookie(customJwtCookie);
final Map<String, String> claims = new HashMap<>();
claims.put("sub", "sub");
claims.put("email", "email");
claims.put("roles", "[APPLICANT]");
verify(customJwtService).generateToken(claims);
}

@Test
Expand All @@ -182,16 +201,23 @@ void shouldUpdateUser_WhenUserFoundWithoutSub_AndIsAdmin() {
Role.builder().name(RoleEnum.ADMIN).build(),
Role.builder().name(RoleEnum.APPLICANT).build(),
Role.builder().name(RoleEnum.FIND).build()
)).build();
)).department(Department.builder().name("department").build()).build();

when(oneLoginService.getUser("email", "sub"))
.thenReturn(Optional.of(user));

final RedirectView methodResponse = loginController.redirectAfterLogin(redirectUrl, response, "a-custom-valid-token");

verify(response).addCookie(customJwtCookie);
verify(oneLoginService).addSubToUser("sub", "email");
assertThat(methodResponse.getUrl()).isEqualTo("adminBaseUrl/dashboard");

verify(response).addCookie(customJwtCookie);
final Map<String, String> claims = new HashMap<>();
claims.put("sub", "sub");
claims.put("email", "email");
claims.put("roles", "[ADMIN, APPLICANT, FIND]");
claims.put("department", "department");
verify(customJwtService).generateToken(claims);
}

@Test
Expand All @@ -210,9 +236,15 @@ void shouldUpdateUser_WhenUserFoundWithoutSub_AndIsSuperAdmin() {

final RedirectView methodResponse = loginController.redirectAfterLogin(redirectUrl, response, "a-custom-valid-token");

verify(response).addCookie(customJwtCookie);
verify(oneLoginService).addSubToUser("sub", "email");
assertThat(methodResponse.getUrl()).isEqualTo("adminBaseUrl/super-admin/dashboard");

verify(response).addCookie(customJwtCookie);
final Map<String, String> claims = new HashMap<>();
claims.put("sub", "sub");
claims.put("email", "email");
claims.put("roles", "[SUPER_ADMIN, ADMIN, APPLICANT, FIND]");
verify(customJwtService).generateToken(claims);
}

@Test
Expand All @@ -229,8 +261,14 @@ void shouldGoToMigrateDataPage_WhenUserFoundWithoutSub_AndIsAnApplicant() {

final RedirectView methodResponse = loginController.redirectAfterLogin(redirectUrl, response, "a-custom-valid-token");

verify(response).addCookie(customJwtCookie);
assertThat(methodResponse.getUrl()).isEqualTo("/should-migrate-data");

verify(response).addCookie(customJwtCookie);
final Map<String, String> claims = new HashMap<>();
claims.put("sub", "sub");
claims.put("email", "email");
claims.put("roles", "[APPLICANT, FIND]");
verify(customJwtService).generateToken(claims);
}
}
}

0 comments on commit c393bab

Please sign in to comment.