Skip to content

Commit

Permalink
Add StudyAccessService
Browse files Browse the repository at this point in the history
  • Loading branch information
JamesPeck committed Jan 16, 2024
1 parent 0849736 commit 19339d3
Show file tree
Hide file tree
Showing 4 changed files with 163 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,6 @@ public void init() {

mailSession.getProperties().put("mail.smtp.ssl.trust", "smtp.gmail.com");

logger.info("Auth micro app has been successfully started");

//Set info for the swagger.json
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.0");
Expand All @@ -161,6 +159,7 @@ public void init() {
beanConfig.setBasePath("/psama");
beanConfig.setResourcePackage(TokenService.class.getPackage().getName());
beanConfig.setScan(true);
logger.info("Auth micro app has been successfully started");
}

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package edu.harvard.hms.dbmi.avillach.auth.rest;

import edu.harvard.hms.dbmi.avillach.auth.data.entity.Role;
import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
import edu.harvard.hms.dbmi.avillach.auth.service.auth.FENCEAuthenticationService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.security.RolesAllowed;
import javax.inject.Inject;
import javax.transaction.Transactional;
import javax.ws.rs.Consumes;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

import java.util.Map;

import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.SUPER_ADMIN;

/**
* <p>Endpoint for service handling business logic for adding all the auth
* rules for a given study</p>
* <p>Note: Only users with the super admin role can access this endpoint.</p>
*/
@Api
@Path("/studyAccess")
public class StudyAccessService {
Logger logger = LoggerFactory.getLogger(StudyAccessService.class);

public static final String MANUAL = "MANUAL_";
public static final String STUDY_IDENTIFIER = "study_identifier";
public static final String CONSENT_GROUP_CODE = "consent_group_code";

@Inject
FENCEAuthenticationService fenceAuthenticationService;

@ApiOperation(value = "POST a single study and it creates the role, privs, and rules for it, requires SUPER_ADMIN role")
@Transactional
@POST
@RolesAllowed(SUPER_ADMIN)
@Consumes(MediaType.APPLICATION_JSON)
@Path("/")
public Response addStudyAccess(String studyIdentifier) {
if (StringUtils.isBlank(studyIdentifier)) {
return Response.status(Response.Status.BAD_REQUEST)
.entity("Study identifier cannot be blank")
.build();
}
Map fenceMappingForStudy = null;
try {
Map<String, Map> fenceMapping = fenceAuthenticationService.getFENCEMapping();
if (fenceMapping == null) {
throw new Exception("Fence mapping is null");
}
fenceMappingForStudy = fenceMapping.get(studyIdentifier);
} catch(Exception ex) {
logger.error(ex.toString());
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Error occurred while fetching FENCE mapping")
.build();
}
if (fenceMappingForStudy == null || fenceMappingForStudy.isEmpty()) {
logger.error("addStudyAccess - Could not find study: " + studyIdentifier + " in FENCE mapping");
return Response.status(Response.Status.NOT_FOUND)
.entity("Could not find study with the provided identifier")
.build();
}
String projectId = (String) fenceMappingForStudy.get(STUDY_IDENTIFIER);
String consentCode = (String) fenceMappingForStudy.get(CONSENT_GROUP_CODE);
String newRoleName = StringUtils.isNotBlank(consentCode) ? MANUAL+projectId+"_"+consentCode : MANUAL+projectId;

logger.debug("addStudyAccess - New manual PSAMA role name: "+newRoleName);

if (fenceAuthenticationService.upsertRole(null, newRoleName, MANUAL + " role "+newRoleName)) {
logger.info("addStudyAccess - Updated user role. Now it includes `"+newRoleName+"`");
return Response.ok("Role '" + newRoleName + "' successfully created").build();
} else {
logger.error("addStudyAccess - could not add " + newRoleName + " role to to database");
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("Could not add role '" + newRoleName + "' to database")
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,6 @@ private User createUserFromFENCEProfile(JsonNode node) {
*/
public boolean upsertRole(User u, String roleName, String roleDescription) {
boolean status = false;
logger.debug("upsertRole() starting for user subject:"+u.getSubject());

// Get the User's list of Roles. The first time, this will be an empty Set.
// This method is called for every Role, and the User's list of Roles will
Expand All @@ -350,7 +349,9 @@ public boolean upsertRole(User u, String roleName, String roleDescription) {
roleRepo.persist(r);
logger.info("upsertRole() created new role");
}
u.getRoles().add(r);
if (u != null) {
u.getRoles().add(r);
}
status = true;
} catch (Exception ex) {
logger.error("upsertRole() Could not inser/update role "+roleName+" to repo", ex);
Expand Down Expand Up @@ -435,6 +436,9 @@ private Set<Privilege> addFENCEPrivileges(User u, Role r) {

private static String extractProject(String roleName) {
String projectPattern = "FENCE_(.*?)(?:_c\\d+)?$";
if (roleName.startsWith("MANUAL_")) {
projectPattern = "MANUAL_(.*?)(?:_c\\d+)?$";
}
Pattern projectRegex = Pattern.compile(projectPattern);
Matcher projectMatcher = projectRegex.matcher(roleName);
String project = "";
Expand All @@ -451,7 +455,9 @@ private static String extractProject(String roleName) {

private static String extractConsentGroup(String roleName) {
String consentPattern = "FENCE_.*?_c(\\d+)$";

if (roleName.startsWith("MANUAL_")) {
consentPattern = "MANUAL_.*?_c(\\d+)$";
}
Pattern consentRegex = Pattern.compile(consentPattern);
Matcher consentMatcher = consentRegex.matcher(roleName);
String consentGroup = "";
Expand Down Expand Up @@ -1125,7 +1131,7 @@ private Map getFENCEMappingforProjectAndConsent(String projectId, String consent
return null;
}

private Map<String, Map> getFENCEMapping(){
public Map<String, Map> getFENCEMapping(){
if(_projectMap == null || _projectMap.isEmpty()) {
try {
Map fenceMapping = JAXRSConfiguration.objectMapper.readValue(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package edu.harvard.hms.dbmi.avillach;

import edu.harvard.hms.dbmi.avillach.auth.data.repository.RoleRepository;
import edu.harvard.hms.dbmi.avillach.auth.rest.StudyAccessService;
import edu.harvard.hms.dbmi.avillach.auth.service.auth.FENCEAuthenticationService;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;

import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.Map;

import static org.junit.Assert.assertEquals;
import static org.mockito.Mockito.*;

public class StudyAccessServiceTest {

@InjectMocks
private StudyAccessService studyAccessService;

@Mock
private FENCEAuthenticationService fenceAuthenticationService;

@Before
public void init() {
MockitoAnnotations.initMocks(this);
}


@Test
public void testAddStudyAccessWithBlankIdentifier() {
String studyIdentifier = "";
Response response = studyAccessService.addStudyAccess(studyIdentifier);
assertEquals(Response.Status.BAD_REQUEST.getStatusCode(), response.getStatus());
assertEquals("Study identifier cannot be blank", response.getEntity());
}

@Test
public void testAddStudyAccess() {
String studyIdentifier = "testStudy";
when(fenceAuthenticationService.getFENCEMapping()).thenReturn(Map.of(studyIdentifier, Map.of(StudyAccessService.STUDY_IDENTIFIER, studyIdentifier,StudyAccessService.CONSENT_GROUP_CODE, "")));
when(fenceAuthenticationService.upsertRole(null, "MANUAL_testStudy", "MANUAL_ role MANUAL_testStudy")).thenReturn(true);

Response response = studyAccessService.addStudyAccess(studyIdentifier);
assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
assertEquals("Role 'MANUAL_testStudy' successfully created", response.getEntity());
}

@Test
public void testAddStudyAccessWithConsent() {
String studyIdentifier = "testStudy2.c2";
when(fenceAuthenticationService.getFENCEMapping()).thenReturn(Map.of(studyIdentifier, Map.of(StudyAccessService.STUDY_IDENTIFIER, "testStudy2", StudyAccessService.CONSENT_GROUP_CODE, "c2")));
when(fenceAuthenticationService.upsertRole(null, "MANUAL_testStudy2_c2", "MANUAL_ role MANUAL_testStudy2_c2")).thenReturn(true);
Response response = studyAccessService.addStudyAccess(studyIdentifier);

assertEquals(Response.Status.OK.getStatusCode(), response.getStatus());
assertEquals("Role 'MANUAL_testStudy2_c2' successfully created", response.getEntity());
}
}

0 comments on commit 19339d3

Please sign in to comment.