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

[ALS-8262] Test User Role Updates On Empty DBGap Passport #239

Merged
merged 3 commits into from
Jan 29, 2025
Merged
Show file tree
Hide file tree
Changes from 2 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
Expand Up @@ -197,10 +197,7 @@ private void setSecurityContextForUser(HttpServletRequest request, HttpServletRe
}
}

// Get the user's roles
Set<Role> userRoles = authenticatedUser.getUser().getRoles();

// Check if the user has any roles and privileges associated with them
if (userRoles == null || userRoles.isEmpty() || userRoles.stream().allMatch(role -> CollectionUtils.isEmpty(role.getPrivileges()))) {
logger.error("User doesn't have any roles or privileges.");
try {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import edu.harvard.hms.dbmi.avillach.auth.model.ras.Passport;
import edu.harvard.hms.dbmi.avillach.auth.model.ras.RasDbgapPermission;
Expand Down Expand Up @@ -105,46 +104,52 @@ public HashMap<String, String> authenticate(Map<String, String> authRequest, Str
}

User user = initializedUser.get();
Optional<Passport> rasPassport = extractAndVerifyPassport(authRequest, introspectResponse, user);
if (rasPassport.isEmpty()) return null;
user = updateRasUserRoles(authRequest.get("code"), user, rasPassport.get());
setUserPassport(authRequest, introspectResponse, user);
HashMap<String, String> responseMap = createUserClaims(user, idToken);

responseMap.put("oktaIdToken", idToken);
logger.info("LOGIN SUCCESS ___ USER {}:{} ___ WITH ROLES ___ {} ___ AUTHORIZATION WILL EXPIRE AT ___ {} ___ CODE {}",
user.getSubject(), user.getUuid().toString(),
user.getRoles().stream().map(role -> role.getName().replace("MANAGED_", "")).collect(Collectors.joining(",")),
responseMap.get("expirationDate"), authRequest.get("code"));

return responseMap;
}

private Optional<Passport> extractAndVerifyPassport(Map<String, String> authRequest, JsonNode introspectResponse, User user) {
Optional<Passport> rasPassport = this.rasPassPortService.extractPassport(introspectResponse);
if (rasPassport.isEmpty()) {
logger.info("LOGIN FAILED ___ NO RAS PASSPORT FOUND ___ USER: {} ___ CODE {}", user.getSubject(), authRequest.get("code"));
return null;
return Optional.empty();
}

if (rasPassPortService.isExpired(rasPassport.get())) {
logger.error("validateRASPassport() LOGIN FAILED ___ PASSPORT IS EXPIRED ___ USER: {} ___ CODE {}", user.getSubject(), authRequest.get("code"));
return null;
return Optional.empty();
}

if (!rasPassport.get().getIss().equals(this.rasPassportIssuer)) {
logger.error("validateRASPassport() LOGIN FAILED ___ PASSPORT ISSUER IS NOT CORRECT ___ USER: {} ___ " +
"EXPECTED ISSUER {} ___ ACTUAL ISSUER {} ___ CODE {}",
user.getSubject(), this.rasPassportIssuer, rasPassport.get().getIss(), authRequest.get("code"));
return null;
return Optional.empty();
}
return rasPassport;
}

logger.info("RAS PASSPORT FOUND ___ USER: {} ___ PASSPORT: {} ___ CODE {}", user.getSubject(), rasPassport.get(), authRequest.get("code"));

Set<RasDbgapPermission> dbgapPermissions = this.rasPassPortService.ga4gpPassportToRasDbgapPermissions(rasPassport.get().getGa4ghPassportV1());
protected User updateRasUserRoles(String code, User user, Passport rasPassport) {
logger.info("RAS PASSPORT FOUND ___ USER: {} ___ PASSPORT: {} ___ CODE {}", user.getSubject(), rasPassport, code);
Set<RasDbgapPermission> dbgapPermissions = this.rasPassPortService.ga4gpPassportToRasDbgapPermissions(rasPassport.getGa4ghPassportV1());
Set<String> dbgapRoleNames = this.roleService.getRoleNamesForDbgapPermissions(dbgapPermissions);
user = userService.updateUserRoles(user, dbgapRoleNames);
logger.debug("USER {} ROLES UPDATED {} ___ CODE {}",
user.getSubject(),
user.getRoles().stream().map(role -> role.getName().replace("MANAGED_", "")).toArray(),
authRequest.get("code"));

String passport = introspectResponse.get("passport_jwt_v11").toString();
user.setPassport(passport);
logger.info("RAS PASSPORT SUCCESSFULLY ADDED TO USER: {} ___ CODE {}", user.getSubject(), authRequest.get("code"));
userService.save(user);
HashMap<String, String> responseMap = createUserClaims(user, idToken);
responseMap.put("oktaIdToken", idToken);
logger.info("LOGIN SUCCESS ___ USER {}:{} ___ WITH ROLES ___ {} ___ AUTHORIZATION WILL EXPIRE AT ___ {} ___ CODE {}",
user.getSubject(), user.getUuid().toString(),
user.getRoles().stream().map(role -> role.getName().replace("MANAGED_", "")).collect(Collectors.joining(",")),
responseMap.get("expirationDate"), authRequest.get("code"));

return responseMap;
code);
return user;
}

private Optional<User> initializeUser(JsonNode introspectResponse) {
Expand Down Expand Up @@ -197,7 +202,13 @@ protected ObjectNode generateRasUserMetadata(User user) {

return objectNode;
}


private void setUserPassport(Map<String, String> authRequest, JsonNode introspectResponse, User user) {
String passport = introspectResponse.get("passport_jwt_v11").toString();
user.setPassport(passport);
userService.save(user);
logger.info("RAS PASSPORT SUCCESSFULLY ADDED TO USER: {} ___ CODE {}", user.getSubject(), authRequest.get("code"));
}

@Override
public String getProvider() {
Expand All @@ -213,4 +224,5 @@ public void setRasConnection(Connection rasConnection) {
this.rasConnection = rasConnection;
}


}
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
package edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import edu.harvard.hms.dbmi.avillach.auth.entity.Connection;
import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege;
import edu.harvard.hms.dbmi.avillach.auth.entity.Role;
import edu.harvard.hms.dbmi.avillach.auth.entity.User;
import edu.harvard.hms.dbmi.avillach.auth.model.ras.Passport;
import edu.harvard.hms.dbmi.avillach.auth.model.ras.RasDbgapPermission;
import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository;
import edu.harvard.hms.dbmi.avillach.auth.service.impl.*;
import edu.harvard.hms.dbmi.avillach.auth.utils.FenceMappingUtility;
Expand All @@ -19,6 +24,7 @@
import java.util.*;

import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.*;

@SpringBootTest
Expand Down Expand Up @@ -90,7 +96,6 @@ public void testAuthorizationCodeFlow_Successful() {

// token exchange
when(restClientUtil.retrievePostResponse(anyString(), any(), eq(queryString))).thenReturn(ResponseEntity.ok(data));

// introspect
when(restClientUtil.retrievePostResponse(anyString(), any(), eq(payload))).thenReturn(ResponseEntity.ok(introspectionResponse));

Expand All @@ -104,6 +109,30 @@ public void testAuthorizationCodeFlow_Successful() {
assertNotNull(authenticate);
}

@Test
public void testUpdateUserRoles_withEmptyDBGapPermissions() throws JsonProcessingException {
String introspectionResponse =
"{\"active\":true,\"sub\":\"example_email@test.com\",\"client_id\":\"test_client_id\",\"passport_jwt_v11\":\""+ exampleRasPassport +"\"}";
JsonNode introspectionResponseParsed = new ObjectMapper().readTree(introspectionResponse);
String code = "AlphaNumericCode";
User user = createTestUser();
Optional<Passport> passport = this.rasPassPortService.extractPassport(introspectionResponseParsed);
assertTrue(passport.isPresent());

Set<RasDbgapPermission> dbgapPermissions = new HashSet<>();
Set<String> dbgapRoleNames = new HashSet<>();

when(rasPassPortService.ga4gpPassportToRasDbgapPermissions(any())).thenReturn(dbgapPermissions);
when(roleService.getRoleNamesForDbgapPermissions(any())).thenReturn(dbgapRoleNames);
when(userService.updateUserRoles(any(), any())).thenReturn(user);

user = this.rasAuthenticationService.updateRasUserRoles(code, user, passport.get());
assertNotNull(user);

// We are verifying that we attempt to update a users roles even if no dbgap roles are present.
verify(userService, times(1)).updateUserRoles(user, dbgapRoleNames);
}

private User createTestUser() {
User user = new User();
user.setUuid(UUID.randomUUID());
Expand Down