Skip to content

Commit 3b0a5cc

Browse files
committed
Remove OpenAuthenticationService and added open access handling
Deleted OpenAuthenticationService and its test class. Updated AuthorizationService to handle open access requests and refactored UserService for open access query template merging. Adjusted SecurityConfig to permit new endpoints and altered logout behavior. Updated dependencies to include Spring Boot DevTools for development profile.
1 parent 222a096 commit 3b0a5cc

19 files changed

+202
-254
lines changed

pic-sure-auth-services/pom.xml

+12-1
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,19 @@
198198
<artifactId>jackson-annotations</artifactId>
199199
<version>2.17.0</version>
200200
</dependency>
201-
202201
</dependencies>
202+
<profiles>
203+
<profile>
204+
<id>dev</id>
205+
<dependencies>
206+
<dependency>
207+
<groupId>org.springframework.boot</groupId>
208+
<artifactId>spring-boot-devtools</artifactId>
209+
<optional>true</optional>
210+
</dependency>
211+
</dependencies>
212+
</profile>
213+
</profiles>
203214
<build>
204215
<finalName>${project.artifactId}</finalName>
205216
<plugins>

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/SecurityConfig.java

+9-3
Original file line numberDiff line numberDiff line change
@@ -57,15 +57,21 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti
5757
"/authentication",
5858
"/authentication/**",
5959
"/swagger.yaml",
60-
"/swagger.json"
60+
"/swagger.json",
61+
"/user/me/queryTemplate",
62+
"/user/me/queryTemplate/**",
63+
"/open/validate"
6164
).permitAll()
6265
.anyRequest().authenticated()
6366
)
6467
.httpBasic(AbstractHttpConfigurer::disable)
6568
.formLogin(AbstractHttpConfigurer::disable)
6669
.addFilterBefore(jwtFilter, UsernamePasswordAuthenticationFilter.class)
67-
.logout((logout) -> logout.logoutUrl("/logout").addLogoutHandler(customLogoutHandler()));
68-
70+
.logout((logout) -> logout.logoutUrl("/logout").addLogoutHandler(customLogoutHandler()).logoutSuccessHandler((request, response, authentication) -> {
71+
// We don't want to redirect to a login page, we just want to return a 200
72+
// We leave it to the client to handle the redirect
73+
response.setStatus(200);
74+
}));
6975

7076
return http.build();
7177
}

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/entity/AccessRule.java

-5
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,11 @@
11
package edu.harvard.hms.dbmi.avillach.auth.entity;
22

3-
import com.fasterxml.jackson.annotation.JsonIgnore;
4-
import com.fasterxml.jackson.annotation.JsonProperty;
5-
63
import jakarta.persistence.Column;
74
import jakarta.persistence.Entity;
85
import jakarta.persistence.FetchType;
96
import jakarta.persistence.JoinColumn;
107
import jakarta.persistence.JoinTable;
118
import jakarta.persistence.ManyToMany;
12-
import jakarta.persistence.ManyToOne;
13-
import jakarta.persistence.OneToMany;
149
import jakarta.persistence.Transient;
1510

1611
import java.lang.reflect.Field;

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/filter/JWTFilter.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ protected void doFilterInternal(HttpServletRequest request, @NonNull HttpServlet
114114

115115
// Check if user is attempting to access the correct introspect endpoint. If not reject the request
116116
// log an error indicating the user's token may be being used by a malicious actor.
117-
if (!request.getRequestURI().endsWith("token/inspect")) {
117+
if (!request.getRequestURI().endsWith("token/inspect") && !request.getRequestURI().endsWith("open/validate")) {
118118
logger.error("{} attempted to perform request {} token may be compromised.", userId, request.getRequestURI());
119119
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User is deactivated");
120120
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package edu.harvard.hms.dbmi.avillach.auth.model;
2+
3+
import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule;
4+
5+
import java.util.Set;
6+
7+
public record EvaluateAccessRuleResult(boolean result, Set<AccessRule> failedRules, String passRuleName) {
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package edu.harvard.hms.dbmi.avillach.auth.rest;
2+
3+
import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AuthorizationService;
4+
import io.swagger.v3.oas.annotations.Parameter;
5+
import org.springframework.beans.factory.annotation.Autowired;
6+
import org.springframework.http.ResponseEntity;
7+
import org.springframework.stereotype.Controller;
8+
import org.springframework.web.bind.annotation.RequestBody;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
11+
import java.util.Map;
12+
13+
@Controller
14+
@RequestMapping(value = "/open")
15+
public class OpenAccessController {
16+
17+
private final AuthorizationService authorizationService;
18+
19+
@Autowired
20+
public OpenAccessController(AuthorizationService authorizationService) {
21+
this.authorizationService = authorizationService;
22+
}
23+
24+
@RequestMapping(value = "/validate", produces = "application/json")
25+
public ResponseEntity<?> validate(@Parameter(required = true, description = "A JSON object that at least" +
26+
" include a user the token for validation")
27+
@RequestBody Map<String, Object> inputMap) {
28+
boolean isValid = authorizationService.openAccessRequestIsValid(inputMap);
29+
return ResponseEntity.ok(isValid);
30+
}
31+
32+
}

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RoleService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,7 @@ public Role createRole(String roleName, String roleDescription) {
201201
Role role = findByName(roleName);
202202
if (role != null) {
203203
// Role already exists
204-
logger.info("upsertRole() role already exists");
204+
logger.debug("upsertRole() role already exists");
205205
} else {
206206
logger.info("createRole() New PSAMA role name:{}", roleName);
207207
// This is a new Role

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java

+58-22
Original file line numberDiff line numberDiff line change
@@ -56,15 +56,14 @@ public class UserService {
5656
private final long tokenExpirationTime;
5757
private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour
5858
private final SessionService sessionService;
59+
private final boolean openAccessIsEnabled;
5960

6061
public long longTermTokenExpirationTime;
6162

6263
private final String applicationUUID;
6364
private final ObjectMapper objectMapper = new ObjectMapper();
6465
private final JWTUtil jwtUtil;
6566

66-
private final Set<String> openAccessIdpValues = Set.of("fence", "ras");
67-
6867
@Autowired
6968
public UserService(BasicMailService basicMailService, TOSService tosService,
7069
UserRepository userRepository,
@@ -74,7 +73,8 @@ public UserService(BasicMailService basicMailService, TOSService tosService,
7473
@Value("${application.token.expiration.time}") long tokenExpirationTime,
7574
@Value("${application.default.uuid}") String applicationUUID,
7675
@Value("${application.long.term.token.expiration.time}") long longTermTokenExpirationTime,
77-
JWTUtil jwtUtil, SessionService sessionService) {
76+
JWTUtil jwtUtil, SessionService sessionService,
77+
@Value("${open.idp.provider.is.enabled}") boolean openIdpProviderIsEnabled) {
7878
this.basicMailService = basicMailService;
7979
this.tosService = tosService;
8080
this.userRepository = userRepository;
@@ -89,6 +89,7 @@ public UserService(BasicMailService basicMailService, TOSService tosService,
8989
long defaultLongTermTokenExpirationTime = 1000L * 60 * 60 * 24 * 30;
9090
this.longTermTokenExpirationTime = longTermTokenExpirationTime > 0 ? longTermTokenExpirationTime : defaultLongTermTokenExpirationTime;
9191
this.sessionService = sessionService;
92+
this.openAccessIsEnabled = openIdpProviderIsEnabled;
9293
}
9394

9495
public HashMap<String, String> getUserProfileResponse(Map<String, Object> claims) {
@@ -387,19 +388,48 @@ public Optional<String> getQueryTemplate(String applicationId) {
387388

388389
SecurityContext securityContext = SecurityContextHolder.getContext();
389390
Optional<CustomUserDetails> customUserDetails = Optional.ofNullable((CustomUserDetails) securityContext.getAuthentication().getPrincipal());
390-
if (customUserDetails.isEmpty() || customUserDetails.get().getUser() == null) {
391-
logger.error("Security context didn't have a user stored.");
392-
return Optional.empty();
391+
if ((customUserDetails.isEmpty() || customUserDetails.get().getUser() == null) && openAccessIsEnabled) {
392+
Optional<Application> application = this.applicationRepository.findById(UUID.fromString(applicationId));
393+
if (application.isEmpty()) {
394+
logger.error("getQueryTemplate() cannot find corresponding application by UUID: {}", UUID.fromString(applicationId));
395+
throw new IllegalArgumentException("Cannot find application by input UUID: " + UUID.fromString(applicationId));
396+
}
397+
398+
return Optional.ofNullable(openMergeTemplate(application.orElse(null)));
399+
} else {
400+
if (customUserDetails.isEmpty() || customUserDetails.get().getUser() == null) {
401+
logger.error("Security context didn't have a user stored.");
402+
return Optional.empty();
403+
}
404+
405+
User user = customUserDetails.get().getUser();
406+
Optional<Application> application = this.applicationRepository.findById(UUID.fromString(applicationId));
407+
if (application.isEmpty()) {
408+
logger.error("getQueryTemplate() cannot find corresponding application by UUID: {}", UUID.fromString(applicationId));
409+
throw new IllegalArgumentException("Cannot find application by input UUID: " + UUID.fromString(applicationId));
410+
}
411+
412+
return Optional.ofNullable(mergeTemplate(user, application.orElse(null)));
393413
}
414+
}
394415

395-
User user = customUserDetails.get().getUser();
396-
Optional<Application> application = this.applicationRepository.findById(UUID.fromString(applicationId));
397-
if (application.isEmpty()) {
398-
logger.error("getQueryTemplate() cannot find corresponding application by UUID: {}", UUID.fromString(applicationId));
399-
throw new IllegalArgumentException("Cannot find application by input UUID: " + UUID.fromString(applicationId));
416+
private String openMergeTemplate(Application application) {
417+
Set<Privilege> applicationPrivileges = application.getPrivileges();
418+
Role openAccessRole = roleService.findByName(MANAGED_OPEN_ACCESS_ROLE_NAME);
419+
// get all of the privileges for the open access role
420+
Set<Privilege> privileges = openAccessRole.getPrivileges();
421+
privileges.addAll(applicationPrivileges);
422+
// get the query template for each privilege
423+
Map mergedTemplateMap = getMergedQueryTemplateMap(privileges);
424+
String resultJSON;
425+
try {
426+
resultJSON = objectMapper.writeValueAsString(mergedTemplateMap);
427+
} catch (JsonProcessingException ex) {
428+
logger.error("mergeTemplate() cannot convert map to json string. The map mergedTemplate is: {}", mergedTemplateMap);
429+
throw new IllegalArgumentException("Inner application error, please contact admin.");
400430
}
401431

402-
return Optional.ofNullable(mergeTemplate(user, application.orElse(null)));
432+
return resultJSON;
403433
}
404434

405435
public Map<String, String> getDefaultQueryTemplate() {
@@ -416,8 +446,22 @@ public Map<String, String> getDefaultQueryTemplate() {
416446
@Cacheable(value = "mergedTemplateCache", keyGenerator = "customKeyGenerator")
417447
public String mergeTemplate(User user, Application application) {
418448
String resultJSON;
449+
Set<Privilege> privilegesByApplication = user.getPrivilegesByApplication(application);
450+
Map mergedTemplateMap = getMergedQueryTemplateMap(privilegesByApplication);
451+
452+
try {
453+
resultJSON = objectMapper.writeValueAsString(mergedTemplateMap);
454+
} catch (JsonProcessingException ex) {
455+
logger.error("mergeTemplate() cannot convert map to json string. The map mergedTemplate is: {}", mergedTemplateMap);
456+
throw new IllegalArgumentException("Inner application error, please contact admin.");
457+
}
458+
459+
return resultJSON;
460+
}
461+
462+
private Map getMergedQueryTemplateMap(Set<Privilege> privilegesByApplication) {
419463
Map mergedTemplateMap = null;
420-
for (Privilege privilege : user.getPrivilegesByApplication(application)) {
464+
for (Privilege privilege : privilegesByApplication) {
421465
String template = privilege.getQueryTemplate();
422466
logger.debug("mergeTemplate() processing template:{}", template);
423467
if (template == null || template.trim().isEmpty()) {
@@ -442,15 +486,7 @@ public String mergeTemplate(User user, Application application) {
442486

443487
mergedTemplateMap = JsonUtils.mergeTemplateMap(mergedTemplateMap, templateMap);
444488
}
445-
446-
try {
447-
resultJSON = objectMapper.writeValueAsString(mergedTemplateMap);
448-
} catch (JsonProcessingException ex) {
449-
logger.error("mergeTemplate() cannot convert map to json string. The map mergedTemplate is: {}", mergedTemplateMap);
450-
throw new IllegalArgumentException("Inner application error, please contact admin.");
451-
}
452-
453-
return resultJSON;
489+
return mergedTemplateMap;
454490
}
455491

456492
@CacheEvict(value = "mergedTemplateCache")

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/FENCEAuthenticationService.java

+1-4
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ public class FENCEAuthenticationService implements AuthenticationService {
3535
private final Logger logger = LoggerFactory.getLogger(FENCEAuthenticationService.class);
3636

3737
private final UserService userService;
38-
private final RoleService roleService;
3938
private final ConnectionWebService connectionService; // We will need to investigate if the ConnectionWebService will need to be versioned as well.
4039
private final AccessRuleService accessRuleService;
4140
private final FenceMappingUtility fenceMappingUtility;
@@ -52,7 +51,6 @@ public class FENCEAuthenticationService implements AuthenticationService {
5251

5352
@Autowired
5453
public FENCEAuthenticationService(UserService userService,
55-
RoleService roleService,
5654
ConnectionWebService connectionService,
5755
RestClientUtil restClientUtil,
5856
@Value("${fence.idp.provider.is.enabled}") boolean isFenceEnabled,
@@ -62,7 +60,6 @@ public FENCEAuthenticationService(UserService userService,
6260
AccessRuleService accessRuleService,
6361
FenceMappingUtility fenceMappingUtility) {
6462
this.userService = userService;
65-
this.roleService = roleService;
6663
this.connectionService = connectionService;
6764
this.idp_provider_uri = idpProviderUri;
6865
this.fence_client_id = fenceClientId;
@@ -82,7 +79,7 @@ public void initializeFenceService() {
8279

8380
@Override
8481
public HashMap<String, String> authenticate(Map<String, String> authRequest, String host) {
85-
String callBackUrl = "https://" + host + "/psamaui/login/";
82+
String callBackUrl = "https://" + host + "/login/loading/";
8683

8784
logger.debug("getFENCEProfile() starting...");
8885
String fence_code = authRequest.get("code");

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OktaAuthenticationService.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public OktaAuthenticationService(String idpProviderUri, String clientId, String
3636
* @return The response from the token endpoint as a JsonNode
3737
*/
3838
protected JsonNode handleCodeTokenExchange(String host, String code) {
39-
String redirectUri = "https://" + host + "/psamaui/login";
39+
String redirectUri = "https://" + host + "/login/loading";
4040
String queryString = "grant_type=authorization_code" + "&code=" + code + "&redirect_uri=" + redirectUri;
4141
String oktaTokenUrl = "https://" + this.idp_provider_uri + "/oauth2/default/v1/token";
4242

pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationService.java

-85
This file was deleted.

0 commit comments

Comments
 (0)