diff --git a/cache.http b/cache.http new file mode 100644 index 000000000..8b380736c --- /dev/null +++ b/cache.http @@ -0,0 +1,12 @@ +# This class can be used to interact with the CacheController in the PSAMA API +# The CacheController must be enable in the application.properties file +# You can enable it by setting the following property: +# app.cache.inspect.enabled=true +# If you are using a environment variable to set the property, you can set it like this: +# CACHE_INSPECT_ENABLED=true + +### GET /cache - Get a list of all caches in the application +GET https://dev.picsure.biodatacatalyst.nhlbi.nih.gov/psama/cache + +### GET /cache/{cacheName} - Get the contents of a specific cache +GET https://dev.picsure.biodatacatalyst.nhlbi.nih.gov/psama/cache/mergedRulesCache \ No newline at end of file diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomLogoutHandler.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomLogoutHandler.java index 4b82e74d3..e045cfc91 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomLogoutHandler.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomLogoutHandler.java @@ -1,7 +1,6 @@ package edu.harvard.hms.dbmi.avillach.auth.config; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.SessionService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.CacheEvictionService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil; import io.jsonwebtoken.Claims; @@ -18,15 +17,13 @@ public class CustomLogoutHandler implements LogoutHandler { private final Logger logger = LoggerFactory.getLogger(CustomLogoutHandler.class); - private final SessionService sessionService; private final UserService userService; - private final AccessRuleService accessRuleService; + private final CacheEvictionService cacheEvictionService; private final JWTUtil jwtUtil; - public CustomLogoutHandler(SessionService sessionService, UserService userService, AccessRuleService accessRuleService, edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil jwtUtil) { - this.sessionService = sessionService; + public CustomLogoutHandler(UserService userService, CacheEvictionService cacheEvictionService, edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil jwtUtil) { this.userService = userService; - this.accessRuleService = accessRuleService; + this.cacheEvictionService = cacheEvictionService; this.jwtUtil = jwtUtil; } @@ -47,9 +44,7 @@ public void logout(HttpServletRequest request, HttpServletResponse response, Aut if (StringUtils.isNotBlank(subject)) { logger.info("logout() Logging out User: {}", subject); - this.sessionService.endSession(subject); - this.userService.evictFromCache(subject); - this.accessRuleService.evictFromCache(subject); + this.cacheEvictionService.evictCache(subject); this.userService.removeUserPassport(subject); } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/SecurityConfig.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/SecurityConfig.java index 3b47eb4b4..036f31be8 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/SecurityConfig.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/SecurityConfig.java @@ -2,6 +2,7 @@ import edu.harvard.hms.dbmi.avillach.auth.filter.JWTFilter; import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.CacheEvictionService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.SessionService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; import edu.harvard.hms.dbmi.avillach.auth.utils.JWTUtil; @@ -24,25 +25,23 @@ public class SecurityConfig { private final JWTFilter jwtFilter; - private final SessionService sessionService; + private final CacheEvictionService cacheEvictionService; private final AuthenticationProvider authenticationProvider; private final UserService userService; - private final AccessRuleService accessRuleService; private final JWTUtil jwtUtil; @Autowired - public SecurityConfig(JWTFilter jwtFilter, SessionService sessionService, AuthenticationProvider authenticationProvider, UserService userService, AccessRuleService accessRuleService, JWTUtil jwtUtil) { + public SecurityConfig(JWTFilter jwtFilter, AuthenticationProvider authenticationProvider, UserService userService, CacheEvictionService cacheEvictionService, JWTUtil jwtUtil) { this.jwtFilter = jwtFilter; - this.sessionService = sessionService; this.authenticationProvider = authenticationProvider; this.userService = userService; - this.accessRuleService = accessRuleService; this.jwtUtil = jwtUtil; + this.cacheEvictionService = cacheEvictionService; } @Bean public CustomLogoutHandler customLogoutHandler() { - return new CustomLogoutHandler(sessionService, userService, accessRuleService, jwtUtil); + return new CustomLogoutHandler(userService, cacheEvictionService, jwtUtil); } @Bean @@ -61,7 +60,8 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti "/user/me/queryTemplate", "/user/me/queryTemplate/**", "/open/validate", - "/logout" + "/logout", + "/cache/**" ).permitAll() .anyRequest().authenticated() ) diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/CacheController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/CacheController.java new file mode 100644 index 000000000..36ebccb5f --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/CacheController.java @@ -0,0 +1,42 @@ +package edu.harvard.hms.dbmi.avillach.auth.rest; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.Collection; + +@RestController +@ConditionalOnExpression("${app.cache.inspect.enabled:false}") +@RequestMapping("/cache") +public class CacheController { + + private final CacheManager cacheManager; + + @Autowired + public CacheController(CacheManager cacheManager) { + this.cacheManager = cacheManager; + } + + @GetMapping + public Collection getCacheNames() { + return cacheManager.getCacheNames(); + } + + @GetMapping("/{cacheName}") + public Object getCache(@PathVariable("cacheName") String cacheName) { + Cache cache = cacheManager.getCache(cacheName); + if (cache == null) { + throw new IllegalArgumentException("Cache not found: " + cacheName); + } + + return cache.getNativeCache(); + } + + +} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java index e032821aa..971bab1c2 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java @@ -158,16 +158,6 @@ public Set getAccessRulesForUserAndApp(User user, Application applic return new HashSet<>(); } - /** - * Evicts the user from all AccessRule caches - * @param userSubject the email to evict - */ - public void evictFromCache(String userSubject) { - logger.info("evictFromCache called for user.email: {}", userSubject); - evictFromMergedAccessRuleCache(userSubject); - evictFromPreProcessedAccessRules(userSubject); - } - @CacheEvict(value = "mergedRulesCache") public void evictFromMergedAccessRuleCache(String userSubject) { if (StringUtils.isBlank(userSubject)) { diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/CacheEvictionService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/CacheEvictionService.java new file mode 100644 index 000000000..d2cdb6df0 --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/CacheEvictionService.java @@ -0,0 +1,33 @@ +package edu.harvard.hms.dbmi.avillach.auth.service.impl; + +import edu.harvard.hms.dbmi.avillach.auth.entity.User; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +@Service +public class CacheEvictionService { + + private final SessionService sessionService; + private final UserService userService; + private final AccessRuleService accessRuleService; + + @Autowired + public CacheEvictionService(SessionService sessionService, UserService userService, AccessRuleService accessRuleService) { + this.sessionService = sessionService; + this.userService = userService; + this.accessRuleService = accessRuleService; + } + + public void evictCache(String userSubject) { + this.sessionService.endSession(userSubject); + this.userService.evictFromCache(userSubject); + this.accessRuleService.evictFromMergedAccessRuleCache(userSubject); + this.accessRuleService.evictFromPreProcessedAccessRules(userSubject); + } + + public void evictCache(User user) { + String userSubject = user.getSubject(); + evictCache(userSubject); + } + +} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortService.java index f1c61643e..e4b09a1a8 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortService.java @@ -33,14 +33,16 @@ public class RASPassPortService { private final RestClientUtil restClientUtil; private final UserService userService; private final String rasURI; + private final CacheEvictionService cacheEvictionService; @Autowired public RASPassPortService(RestClientUtil restClientUtil, UserService userService, - @Value("${ras.idp.uri}") String rasURI) { + @Value("${ras.idp.uri}") String rasURI, CacheEvictionService cacheEvictionService) { this.restClientUtil = restClientUtil; this.userService = userService; this.rasURI = rasURI.replaceAll("/$", ""); + this.cacheEvictionService = cacheEvictionService; logger.info("RASPassPortService initialized with rasURI: {}", rasURI); } @@ -72,7 +74,7 @@ public void validateAllUserPassports() { logger.error("FAILED TO DECODE PASSPORT ___ USER: {}", user.getSubject()); user.setPassport(null); userService.save(user); - userService.logoutUser(user); + cacheEvictionService.evictCache(user); return; } @@ -127,6 +129,7 @@ private boolean handlePassportValidationResponse(String response, User user) { private boolean handleFailedValidationResponse(String validateResponse, User user) { this.userService.save(user); this.userService.logoutUser(user); + this.cacheEvictionService.evictCache(user); this.logger.info("handleFailedValidationResponse - {} - USER LOGGED OUT - {}", validateResponse, user.getSubject()); return false; } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java index 6d59ba37c..386cd0060 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserService.java @@ -55,7 +55,6 @@ public class UserService { private final RoleService roleService; private final long tokenExpirationTime; private static final long defaultTokenExpirationTime = 1000L * 60 * 60; // 1 hour - private final SessionService sessionService; private final boolean openAccessIsEnabled; public long longTermTokenExpirationTime; @@ -73,7 +72,7 @@ public UserService(BasicMailService basicMailService, TOSService tosService, @Value("${application.token.expiration.time}") long tokenExpirationTime, @Value("${application.default.uuid}") String applicationUUID, @Value("${application.long.term.token.expiration.time}") long longTermTokenExpirationTime, - JWTUtil jwtUtil, SessionService sessionService, + JWTUtil jwtUtil, @Value("${open.idp.provider.is.enabled}") boolean openIdpProviderIsEnabled) { this.basicMailService = basicMailService; this.tosService = tosService; @@ -88,7 +87,6 @@ public UserService(BasicMailService basicMailService, TOSService tosService, long defaultLongTermTokenExpirationTime = 1000L * 60 * 60 * 24 * 30; this.longTermTokenExpirationTime = longTermTokenExpirationTime > 0 ? longTermTokenExpirationTime : defaultLongTermTokenExpirationTime; - this.sessionService = sessionService; this.openAccessIsEnabled = openIdpProviderIsEnabled; } @@ -665,9 +663,7 @@ public Set getAllUsersWithAPassport() { * @param user */ public void logoutUser(User user) { - evictFromCache(user.getSubject()); this.removeUserPassport(user.getSubject()); - this.sessionService.endSession(user.getSubject()); } /** diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AimAheadAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AimAheadAuthenticationService.java index 736198f08..54e61b586 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AimAheadAuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AimAheadAuthenticationService.java @@ -8,7 +8,7 @@ import edu.harvard.hms.dbmi.avillach.auth.service.AuthenticationService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.RoleService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.CacheEvictionService; import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; import jakarta.persistence.NoResultException; import org.apache.commons.lang3.StringUtils; @@ -32,7 +32,8 @@ public class AimAheadAuthenticationService extends OktaAuthenticationService imp private final String connectionId; private final boolean isOktaEnabled; - private final AccessRuleService accessRuleService; + + private final CacheEvictionService cacheEvictionService; /** * Constructor for the OktaOAuthAuthenticationService @@ -46,26 +47,24 @@ public class AimAheadAuthenticationService extends OktaAuthenticationService imp @Autowired public AimAheadAuthenticationService(UserService userService, RoleService roleService, - AccessRuleService accessRuleService, RestClientUtil restClientUtil, @Value("${a4.okta.idp.provider.is.enabled}") boolean isOktaEnabled, @Value("${a4.okta.idp.provider.uri}") String idp_provider_uri, @Value("${a4.okta.connection.id}") String connectionId, @Value("${a4.okta.client.id}") String clientId, - @Value("${a4.okta.client.secret}") String spClientSecret) { + @Value("${a4.okta.client.secret}") String spClientSecret, CacheEvictionService cacheEvictionService) { super(idp_provider_uri, clientId, spClientSecret, restClientUtil); this.userService = userService; this.roleService = roleService; this.connectionId = connectionId; this.isOktaEnabled = isOktaEnabled; + this.cacheEvictionService = cacheEvictionService; logger.info("OktaOAuthAuthenticationService is enabled: {}", isOktaEnabled); logger.info("OktaOAuthAuthenticationService initialized"); logger.info("idp_provider_uri: {}", idp_provider_uri); logger.info("connectionId: {}", connectionId); - - this.accessRuleService = accessRuleService; } /** @@ -128,7 +127,7 @@ private User initializeUser(JsonNode introspectResponse) { return null; } - clearCache(user); + cacheEvictionService.evictCache(user); return user; } @@ -146,11 +145,6 @@ private HashMap createUserClaims(User user) { return userService.getUserProfileResponse(claims); } - private void clearCache(User user) { - userService.evictFromCache(user.getSubject()); - accessRuleService.evictFromCache(user.getSubject()); - } - /** * Using the introspection token response, load the user from the database. If the user does not exist, we * will reject their login attempt. diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/Auth0AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/Auth0AuthenticationService.java index 8c7f4accc..6a0f8a230 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/Auth0AuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/Auth0AuthenticationService.java @@ -9,6 +9,7 @@ import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository; import edu.harvard.hms.dbmi.avillach.auth.service.AuthenticationService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.BasicMailService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.CacheEvictionService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.OauthUserMatchingService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; @@ -46,6 +47,7 @@ public class Auth0AuthenticationService implements AuthenticationService { private static final int AUTH_RETRY_LIMIT = 3; private final boolean isAuth0Enabled; + private final CacheEvictionService cacheEvictionService; private boolean deniedEmailEnabled; @@ -64,8 +66,8 @@ public Auth0AuthenticationService(OauthUserMatchingService matchingService, RestClientUtil restClientUtil, @Value("${auth0.idp.provider.is.enabled}") boolean isAuth0Enabled, @Value("${auth0.denied.email.enabled}") boolean deniedEmailEnabled, - @Value("${auth0.host}") String auth0host - ) { + @Value("${auth0.host}") String auth0host, + CacheEvictionService cacheEvictionService) { this.matchingService = matchingService; this.userRepository = userRepository; this.basicMailService = basicMailService; @@ -75,6 +77,7 @@ public Auth0AuthenticationService(OauthUserMatchingService matchingService, this.connectionRepository = connectionRepository; this.restClientUtil = restClientUtil; this.isAuth0Enabled = isAuth0Enabled; + this.cacheEvictionService = cacheEvictionService; } @Override @@ -121,6 +124,7 @@ public HashMap authenticate(Map authRequest, Str } } + cacheEvictionService.evictCache(user); HashMap claims = new HashMap<>(); claims.put("sub", userId); claims.put("name", user.getName()); diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/FENCEAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/FENCEAuthenticationService.java index 10b624e08..1e8e56576 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/FENCEAuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/FENCEAuthenticationService.java @@ -4,7 +4,6 @@ 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.Role; import edu.harvard.hms.dbmi.avillach.auth.entity.User; import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException; import edu.harvard.hms.dbmi.avillach.auth.model.fenceMapping.StudyMetaData; @@ -36,8 +35,8 @@ public class FENCEAuthenticationService implements AuthenticationService { private final UserService userService; private final ConnectionWebService connectionService; // We will need to investigate if the ConnectionWebService will need to be versioned as well. - private final AccessRuleService accessRuleService; private final FenceMappingUtility fenceMappingUtility; + private final CacheEvictionService cacheEvictionService; private Connection fenceConnection; @@ -57,17 +56,16 @@ public FENCEAuthenticationService(UserService userService, @Value("${fence.idp.provider.uri}") String idpProviderUri, @Value("${fence.client.id}") String fenceClientId, @Value("${fence.client.secret}") String fenceClientSecret, - AccessRuleService accessRuleService, - FenceMappingUtility fenceMappingUtility) { + FenceMappingUtility fenceMappingUtility, CacheEvictionService cacheEvictionService) { this.userService = userService; this.connectionService = connectionService; this.idp_provider_uri = idpProviderUri; this.fence_client_id = fenceClientId; this.fence_client_secret = fenceClientSecret; this.restClientUtil = restClientUtil; - this.accessRuleService = accessRuleService; this.fenceMappingUtility = fenceMappingUtility; this.isFenceEnabled = isFenceEnabled; + this.cacheEvictionService = cacheEvictionService; } @PostConstruct @@ -119,12 +117,8 @@ public HashMap authenticate(Map authRequest, Str // in the Gen3/FENCE profile currentUser = createUserFromFENCEProfile(fence_user_profile); logger.info("getFENCEProfile() saved details for user with e-mail:{} and subject:{}", currentUser.getEmail(), currentUser.getSubject()); + cacheEvictionService.evictCache(currentUser); - if (!currentUser.getEmail().isEmpty()) { - String subject = currentUser.getSubject(); - accessRuleService.evictFromCache(subject); - userService.evictFromCache(subject); - } } catch (Exception ex) { logger.error("getFENCEToken() Could not persist the user information, because {}", ex.getMessage()); throw new NotAuthorizedException("The user details could not be persisted. Please contact the administrator."); @@ -167,7 +161,6 @@ public HashMap authenticate(Map authRequest, Str } - @Override public String getProvider() { return "fence"; diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationService.java index 95df31d14..254bace56 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationService.java @@ -28,9 +28,9 @@ public class RASAuthenticationService extends OktaAuthenticationService implemen private final UserService userService; private final boolean isEnabled; - private final AccessRuleService accessRuleService; private final RoleService roleService; private final RASPassPortService rasPassPortService; + private final CacheEvictionService cacheEvictionService; private Connection rasConnection; private final String rasPassportIssuer; @@ -45,7 +45,6 @@ public class RASAuthenticationService extends OktaAuthenticationService implemen */ @Autowired public RASAuthenticationService(UserService userService, - AccessRuleService accessRuleService, RestClientUtil restClientUtil, @Value("${ras.okta.idp.provider.is.enabled}") boolean isEnabled, @Value("${ras.okta.idp.provider.uri}") String idp_provider_uri, @@ -55,7 +54,7 @@ public RASAuthenticationService(UserService userService, @Value("${ras.passport.issuer}") String rasPassportIssuer, RoleService roleService, RASPassPortService rasPassPortService, - ConnectionWebService connectionService ) { + ConnectionWebService connectionService, CacheEvictionService cacheEvictionService) { super(idp_provider_uri, clientId, clientSecret, restClientUtil); this.userService = userService; @@ -69,8 +68,8 @@ public RASAuthenticationService(UserService userService, logger.info("idp_provider_uri: {}", idp_provider_uri); logger.info("connectionId: {}", connectionId); - this.accessRuleService = accessRuleService; this.rasConnection = connectionService.getConnectionByLabel("RAS"); + this.cacheEvictionService = cacheEvictionService; } /** @@ -161,7 +160,7 @@ private Optional initializeUser(JsonNode introspectResponse) { currentUser.setGeneralMetadata(generateRasUserMetadata(currentUser).toString()); logger.info("USER METADATA SUCCESSFULLY ADDED - USER DATA: {}", currentUser.getGeneralMetadata()); - clearCache(currentUser); + cacheEvictionService.evictCache(currentUser); return Optional.of(currentUser); } @@ -180,11 +179,6 @@ private HashMap createUserClaims(User user, String idToken) { return userService.getUserProfileResponse(claims); } - private void clearCache(User user) { - userService.evictFromCache(user.getSubject()); - accessRuleService.evictFromCache(user.getSubject()); - } - /** * Generate the user metadata that will be stored in the database. This metadata is used to determine the user's * role and other information. diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationService.java index 65f8cb4a1..7ab39432b 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationService.java @@ -150,7 +150,13 @@ public boolean isAuthorized(Application application, Object requestBody, User us label = user.getConnection().getLabel(); } - if (!this.strictConnections.contains(label)) { + if (this.strictConnections.contains(label)) { + accessRules = this.accessRuleService.getAccessRulesForUserAndApp(user, application); + if (accessRules.isEmpty()) { + logger.info("ACCESS_LOG ___ {},{},{} ___ has been denied access to execute query ___ {} ___ in application ___ {} ___ NO ACCESS RULES EVALUATED", user.getUuid().toString(), user.getEmail(), user.getName(), formattedQuery, applicationName); + return false; + } + } else { Set privileges = user.getPrivilegesByApplication(application); // List all privileges of the user logger.info("ACCESS_LOG ___ {},{},{} ___ has the following privileges: {}", user.getUuid().toString(), user.getEmail(), user.getName(), privileges.stream().map(Privilege::getName).collect(Collectors.joining(", "))); @@ -160,21 +166,14 @@ public boolean isAuthorized(Application application, Object requestBody, User us } accessRules = this.accessRuleService.cachedPreProcessAccessRules(user, privileges); - logger.info("ACCESS_LOG ___ {},{},{} ___ has the following access rules: {}", user.getUuid().toString(), user.getEmail(), user.getName(), accessRules.stream().map(AccessRule::getName).collect(Collectors.joining(", "))); if (accessRules.isEmpty()) { logger.info("ACCESS_LOG ___ {},{},{} ___ has been granted access to execute query ___ {} ___ in application ___ {} ___ NO ACCESS RULES EVALUATED", user.getUuid().toString(), user.getEmail(), user.getName(), formattedQuery, applicationName); return true; } - } else { - accessRules = this.accessRuleService.getAccessRulesForUserAndApp(user, application); - - logger.info("ACCESS_LOG ___ {},{},{} ___ has the following access rules: {}", user.getUuid().toString(), user.getEmail(), user.getName(), accessRules.stream().map(AccessRule::toString).collect(Collectors.joining(", "))); - if (accessRules.isEmpty()) { - logger.info("ACCESS_LOG ___ {},{},{} ___ has been denied access to execute query ___ {} ___ in application ___ {} ___ NO ACCESS RULES EVALUATED", user.getUuid().toString(), user.getEmail(), user.getName(), formattedQuery, applicationName); - return false; - } } + logger.info("ACCESS_LOG ___ {},{},{} ___ has the following access rules: {}", user.getUuid().toString(), user.getEmail(), user.getName(), accessRules.stream().map(AccessRule::toString).collect(Collectors.joining(", "))); + EvaluateAccessRuleResult evaluationResult = passesAccessRuleEvaluation(requestBody, accessRules); boolean result = evaluationResult.result(); String passRuleName = evaluationResult.passRuleName(); diff --git a/pic-sure-auth-services/src/main/resources/application.properties b/pic-sure-auth-services/src/main/resources/application.properties index e51b7d3f3..e2c227c31 100644 --- a/pic-sure-auth-services/src/main/resources/application.properties +++ b/pic-sure-auth-services/src/main/resources/application.properties @@ -24,6 +24,13 @@ logging.level.org.springframework.security=${LOGGING_LEVEL_SECURITY:INFO} logging.level.root=${LOGGING_LEVEL_ROOT:INFO} logging.level.org.springframework.web=${LOGGING_LEVEL_SLF4J:INFO} logging.level.edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.RASAuthenticationService=${LOGGING_LEVEL_RAS_AUTHENTICATION:INFO} +logging.level.edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.FENCEAuthenticationService=${LOGGING_LEVEL_FENCE_AUTHENTICATION:INFO} +logging.level.edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService=${LOGGING_LEVEL_ACCESS_RULE_SERVICE:INFO} +logging.level.org.springframework.cache=${LOGGING_LEVEL_CACHE:INFO} + +# Cache Controller Configuration. This is used to gain insight into the cache. +# This should never be enabled in production. +app.cache.inspect.enabled=${CACHE_INSPECT_ENABLED:false} # Mail session configuration (Assuming Gmail SMTP for example) spring.mail.host=smtp.gmail.com diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationServiceTest.java index e0720425c..621b50de9 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthenticationServiceTest.java @@ -42,6 +42,8 @@ public class AuthenticationServiceTest { private ConnectionRepository connectionRepository; @Mock private RestClientUtil restClientUtil; + @Mock + private CacheEvictionService cacheEvictionService; private Auth0AuthenticationService authenticationService; @@ -58,7 +60,7 @@ public void setUp() { authRequest.put("access_token", accessToken); authRequest.put("redirectURI", redirectURI); - authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost"); + authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost", cacheEvictionService); } // Tests missing parameters in the authentication request diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationServiceTest.java index ed95ccb91..193812784 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AuthorizationServiceTest.java @@ -52,6 +52,7 @@ public void testIsAuthorized_AccessRulePassed() { // create access_rule for privilege AccessRule accessRule = new AccessRule(); + accessRule.setUuid(UUID.randomUUID()); accessRule.setRule("$.test"); accessRule.setType(AccessRule.TypeNaming.ALL_EQUALS); accessRule.setValue("value"); diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortServiceTest.java index 01e9eea59..091f55c12 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RASPassPortServiceTest.java @@ -26,6 +26,8 @@ public class RASPassPortServiceTest { @Mock private RestClientUtil restClientUtil; + @Mock + private CacheEvictionService cacheEvictionService; private RASPassPortService rasPassPortService; @@ -33,7 +35,7 @@ public class RASPassPortServiceTest { @Before public void setUp() throws Exception { - this.rasPassPortService = new RASPassPortService(restClientUtil, null, "https://test.com/"); + this.rasPassPortService = new RASPassPortService(restClientUtil, null, "https://test.com/", cacheEvictionService); // Parse the passport and get the visa so we can mock the validateVisa method Optional passport = JWTUtil.parsePassportJWTV11(exampleRasPassport); diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserServiceTest.java index e15961078..028480691 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/UserServiceTest.java @@ -49,8 +49,6 @@ public class UserServiceTest { @Mock private RoleService roleService; @Mock - private SessionService sessionService; - @Mock private JWTUtil mockJwtUtil; private JWTUtil jwtUtil; @@ -80,7 +78,6 @@ public void setUp() { applicationUUID, longTermTokenExpirationTime, mockJwtUtil, - sessionService, false); } diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceTest.java index df74304e3..e9aa36f70 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceTest.java @@ -7,6 +7,7 @@ import edu.harvard.hms.dbmi.avillach.auth.repository.ConnectionRepository; import edu.harvard.hms.dbmi.avillach.auth.repository.UserRepository; import edu.harvard.hms.dbmi.avillach.auth.service.impl.BasicMailService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.CacheEvictionService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.OauthUserMatchingService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; @@ -44,13 +45,12 @@ public class AuthenticationServiceTest { private ConnectionRepository connectionRepository; @Mock private RestClientUtil restClientUtil; + @Mock + private CacheEvictionService cacheEvictionService; private Auth0AuthenticationService authenticationService; private final String accessToken = "dummyAccessToken"; - private final String redirectURI = "http://dummyRedirectUri.com"; - private final String userId = "user123"; - private final String connectionId = "conn123"; private Map authRequest; @Before @@ -58,9 +58,10 @@ public void setUp() { MockitoAnnotations.initMocks(this); authRequest = new HashMap<>(); authRequest.put("access_token", accessToken); + String redirectURI = "http://dummyRedirectUri.com"; authRequest.put("redirectURI", redirectURI); - authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost"); + authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost", cacheEvictionService); } // Tests missing parameters in the authentication request @@ -131,10 +132,12 @@ private void setupSuccessfulTokenRetrievalScenario() throws IOException { this.authenticationService.setDeniedEmailEnabled(false); JsonNode mockUserInfo = mock(JsonNode.class); when(mockUserInfo.get("user_id")).thenReturn(mock(JsonNode.class)); + String userId = "user123"; when(mockUserInfo.get("user_id").asText()).thenReturn(userId); when(mockUserInfo.get("identities")).thenReturn(mock(JsonNode.class)); when(mockUserInfo.get("identities").get(0)).thenReturn(mock(JsonNode.class)); when(mockUserInfo.get("identities").get(0).get("connection")).thenReturn(mock(JsonNode.class)); + String connectionId = "conn123"; when(mockUserInfo.get("identities").get(0).get("connection").asText()).thenReturn(connectionId); String validJson = "{" diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationServiceTest.java index dbc7ceec6..431a12cce 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/RASAuthenticationServiceTest.java @@ -15,6 +15,7 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.junit.MockitoJUnitRunner; +import org.mockito.stubbing.OngoingStubbing; import org.springframework.http.ResponseEntity; import java.util.*; @@ -28,11 +29,12 @@ public class RASAuthenticationServiceTest { @Mock private UserService userService; @Mock - private AccessRuleService accessRuleService; - @Mock private RestClientUtil restClientUtil; @Mock private ConnectionWebService connectionService; + @Mock + private CacheEvictionService cacheEvictionService; + private RASPassPortService rasPassPortService; private RASAuthenticationService rasAuthenticationService; @@ -46,12 +48,11 @@ public class RASAuthenticationServiceTest { public void setUp() throws Exception { MockitoAnnotations.initMocks(this); RoleService roleService = new RoleService(mock(RoleRepository.class), mock(PrivilegeService.class), mock(FenceMappingUtility.class)); - this.rasPassPortService = spy(new RASPassPortService(restClientUtil, userService, "")); + this.rasPassPortService = spy(new RASPassPortService(restClientUtil, userService, "", cacheEvictionService)); doReturn(false).when(rasPassPortService).isExpired(any()); rasAuthenticationService = new RASAuthenticationService( userService, - accessRuleService, restClientUtil, true, "test.com", @@ -61,7 +62,8 @@ public void setUp() throws Exception { "https://stsstg.nih.gov", roleService, rasPassPortService, - connectionService + connectionService, + cacheEvictionService ); Connection rasConnection = new Connection(); @@ -91,6 +93,8 @@ public void testAuthorizationCodeFlow_Successful() { // introspect when(restClientUtil.retrievePostResponse(anyString(), any(), eq(payload))).thenReturn(ResponseEntity.ok(introspectionResponse)); + doNothing().when(cacheEvictionService).evictCache(any(User.class)); + User user = createTestUser(); user.setSubject("okta-ras|adfadfaf"); when(userService.createRasUser(any(), any())).thenReturn(Optional.of(user)); diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTest.java index 7a779eaf9..5a81ab52e 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTest.java @@ -332,6 +332,7 @@ public void testIsAuthorized_AccessRulePassed() { // create access_rule for privilege AccessRule accessRule = new AccessRule(); + accessRule.setUuid(UUID.randomUUID()); accessRule.setRule("$.test"); accessRule.setType(AccessRule.TypeNaming.ALL_EQUALS); accessRule.setValue("value");