diff --git a/Jenkinsfile.fence b/Jenkinsfile.fence index b0016f0d6..faa280622 100644 --- a/Jenkinsfile.fence +++ b/Jenkinsfile.fence @@ -4,10 +4,13 @@ pipeline { parameters { string(name: 'DOCKER_REGISTRY', description: 'Docker registry URL (e.g., ECR URL)', defaultValue: 'hms-dbmi') string(name: 'REPOSITORY_NAME', description: 'Docker repository name', defaultValue: 'psama') + string(name: 'DATASOURCE_URL', description: 'Datasource URL', defaultValue: '${database_host_address}') + string(name: 'DATASOURCE_USERNAME', description: 'Datasource username', defaultValue: '${database_app_user_secret_name}') + string(name: 'STACK_SPECIFIC_APPLICATION_ID', description: 'Application ID for base query', defaultValue: '${application_id_for_base_query}') } environment { - DOCKER_BUILD_ARGS = "-f ./pic-sure-auth-services/Dockerfile" + DOCKER_BUILD_ARGS = "-f ./pic-sure-auth-services/bdc.Dockerfile" GIT_BRANCH_SHORT = sh(script: 'echo ${GIT_BRANCH} | cut -d "/" -f 2', returnStdout: true).trim() GIT_COMMIT_SHORT = sh(script: 'echo ${GIT_COMMIT} | cut -c1-7', returnStdout: true).trim() IMAGE_TAG = "${GIT_BRANCH_SHORT}_${GIT_COMMIT_SHORT}" @@ -15,16 +18,23 @@ pipeline { } stages { - stage('build') { + stage('Build Docker Image') { steps { - sh "docker build ${DOCKER_BUILD_ARGS} -t ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${IMAGE_TAG} ." - sh "docker tag ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${IMAGE_TAG} ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${LATEST_TAG}" + script { + // Define the build args + def buildArgs = " --build-arg DATASOURCE_URL=${env.DATASOURCE_URL} " + + " --build-arg DATASOURCE_USERNAME=${env.DATASOURCE_USERNAME} " + + " --build-arg STACK_SPECIFIC_APPLICATION_ID=${env.STACK_SPECIFIC_APPLICATION_ID} " + + sh "docker build ${DOCKER_BUILD_ARGS} ${buildArgs} -t ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${IMAGE_TAG} ." + sh "docker tag ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${IMAGE_TAG} ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${LATEST_TAG}" + } } } - stage('deploy') { + stage('Deploy Docker Image') { steps { sh "docker save ${params.DOCKER_REGISTRY}/${params.REPOSITORY_NAME}:${LATEST_TAG} | gzip > psama.tar.gz" - sh "aws s3 --sse=AES256 cp psama.tar.gz s3://$S3_BUCKET_NAME/releases/jenkins_pipeline_build_${pipeline_build_id}/psama.tar.gz" + sh "aws s3 --sse=AES256 cp psama.tar.gz s3://$S3_BUCKET_NAME/releases/psama/psama.tar.gz" } } } diff --git a/config/psama/bdc/psama-db-config.properties b/config/psama/bdc/psama-db-config.properties new file mode 100644 index 000000000..4d0461862 --- /dev/null +++ b/config/psama/bdc/psama-db-config.properties @@ -0,0 +1,4 @@ +spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect +spring.datasource.driver-class-name=com.amazonaws.secretsmanager.sql.AWSSecretsManagerMySQLDriver +spring.datasource.url=jdbc-secretsmanager:mysql://${DATASOURCE_URL}/auth?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&autoReconnectForPools=true +spring.datasource.username=${DATASOURCE_USERNAME} \ No newline at end of file diff --git a/config/psama/psama.env b/config/psama/psama.env new file mode 100644 index 000000000..9994d850e --- /dev/null +++ b/config/psama/psama.env @@ -0,0 +1,31 @@ +# This is a template file that can be used to configure the application. Pass this to the dockerfile and +# the application will be configured with the values provided in this file. + +# These properties are used to configure our applications JWT token. +# The JWT token is used to authenticate the user and authorize them to access the application +# after initial login. +APPLICATION_CLIENT_SECRET= +APPLICATION_CLIENT_SECRET_IS_BASE_64=false + +# Fence IDP Configuration +FENCE_IDP_PROVIDER_IS_ENABLED=false +FENCE_CLIENT_ID= +FENCE_CLIENT_SECRET= +FENCE_IDP_PROVIDER_URI= + +# AIM AHEAD Authorized Access (A4) OKTA IDP Configuration +A4_OKTA_IDP_PROVIDER_IS_ENABLED=false +A4_OKTA_CLIENT_SECRET= +A4_OKTA_CLIENT_ID= +A4_OKTA_CONNECTION_ID= +A4_OKTA_IDP_PROVIDER_URI= + +# Auth0 IDP Configuration (Generally, this is used for the All-In-One and Authorized Access) +AUTH0_IDP_PROVIDER_IS_ENABLED=false +AUTH0_HOST= +AUTH0_DENIED_EMAIL_ENABLED= + +# Open Access IDP Configuration +OPEN_IDP_PROVIDER_IS_ENABLED=false + +SYSTEM_NAME=PIC-SURE BioDataCatalyst \ No newline at end of file diff --git a/pic-sure-auth-db/db/sql/V5_MIGRATE_TO_ACCESSRULE_JOINTABLE.sql b/pic-sure-auth-db/db/sql/V5__MIGRATE_TO_ACCESSRULE_JOINTABLE.sql similarity index 100% rename from pic-sure-auth-db/db/sql/V5_MIGRATE_TO_ACCESSRULE_JOINTABLE.sql rename to pic-sure-auth-db/db/sql/V5__MIGRATE_TO_ACCESSRULE_JOINTABLE.sql diff --git a/pic-sure-auth-db/db/sql/V6__UPDATE_EXISTING_ROLE_PRIVILEGE_RULE_NAMES.sql b/pic-sure-auth-db/db/sql/V6__UPDATE_EXISTING_ROLE_PRIVILEGE_RULE_NAMES.sql new file mode 100644 index 000000000..e32a94fb0 --- /dev/null +++ b/pic-sure-auth-db/db/sql/V6__UPDATE_EXISTING_ROLE_PRIVILEGE_RULE_NAMES.sql @@ -0,0 +1,28 @@ +# We have migrated our naming convention away from "FENCE_" to a more generalized "MANAGED_". + +-- Update roles table +UPDATE role +SET name = REPLACE(name, 'FENCE', 'MANAGED') +WHERE name LIKE '%FENCE%'; + +UPDATE role +SET description = REPLACE(description, 'FENCE', 'MANAGED') +WHERE description LIKE '%FENCE%'; + +-- Update privileges table +UPDATE privilege +SET name = REPLACE(name, 'FENCE', 'MANAGED') +WHERE name LIKE '%FENCE%'; + +UPDATE privilege +SET description = REPLACE(description, 'FENCE', 'MANAGED') +WHERE description LIKE '%FENCE%'; + +-- Update access rules table +UPDATE access_rule +SET name = REPLACE(name, 'FENCE', 'MANAGED') +WHERE name LIKE '%FENCE%'; + +UPDATE access_rule +SET description = REPLACE(description, 'FENCE', 'MANAGED') +WHERE description LIKE '%FENCE%'; \ No newline at end of file diff --git a/pic-sure-auth-services/bdc.Dockerfile b/pic-sure-auth-services/bdc.Dockerfile new file mode 100644 index 000000000..69a1bdad8 --- /dev/null +++ b/pic-sure-auth-services/bdc.Dockerfile @@ -0,0 +1,37 @@ +FROM maven:3.9.6-amazoncorretto-21 as build + +# Copy the source code into the container +COPY ./ /app + +# Change the working directory +WORKDIR /app + +# Build the jar +RUN mvn clean install -DskipTests + +FROM amazoncorretto:21.0.1-alpine3.18 + +ARG DATASOURCE_URL +ARG DATASOURCE_USERNAME +ARG STACK_SPECIFIC_APPLICATION_ID + +ENV DATASOURCE_URL=${DATASOURCE_URL} +ENV DATASOURCE_USERNAME=${DATASOURCE_USERNAME} +ENV STACK_SPECIFIC_APPLICATION_ID=${application_id_for_base_query} + +# Copy jar and access token from maven build +COPY --from=build /app/pic-sure-auth-services/target/pic-sure-auth-services.jar /pic-sure-auth-service.jar + +# Copy additional bdc configuration files. Root of the project +COPY config/psama/bdc/psama-db-config.properties /config/psama-db-config.properties + +# Set SPRING_CONFIG_ADDITIONAL_LOCATION +ENV SPRING_CONFIG_ADDITIONAL_LOCATION=file:/config/psama-db-config.properties + +# Copy the AWS certificate +COPY pic-sure-auth-services/aws_certs/certificate.der /certificate.der + +# Import the certificate into the Java trust store +RUN keytool -noprompt -import -alias aws_cert -keystore $JAVA_HOME/lib/security/cacerts -storepass changeit -file /certificate.der + +ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /pic-sure-auth-service.jar"] \ No newline at end of file diff --git a/pic-sure-auth-services/pom.xml b/pic-sure-auth-services/pom.xml index f81adeac1..a5f33274f 100644 --- a/pic-sure-auth-services/pom.xml +++ b/pic-sure-auth-services/pom.xml @@ -60,6 +60,16 @@ 3.2.4 true + + org.springframework.boot + spring-boot-starter-cache + 3.3.1 + + + org.springframework + spring-context + 6.1.10 + net.bytebuddy diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/ApplicationConfig.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/ApplicationConfig.java index ea5e43fe1..ae72e1393 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/ApplicationConfig.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/ApplicationConfig.java @@ -2,6 +2,8 @@ import edu.harvard.hms.dbmi.avillach.auth.service.impl.CustomUserDetailService; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.security.authentication.AuthenticationManager; @@ -10,6 +12,7 @@ import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration; @Configuration +@EnableCaching public class ApplicationConfig { private final CustomUserDetailService customUserDetailService; @@ -30,4 +33,9 @@ public AuthenticationProvider authenticationProvider() { provider.setUserDetailsService(customUserDetailService); return provider; } + + @Bean("customKeyGenerator") + public KeyGenerator generator() { + return new CustomKeyGenerator(); + } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomKeyGenerator.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomKeyGenerator.java new file mode 100644 index 000000000..74d4adb8b --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/config/CustomKeyGenerator.java @@ -0,0 +1,20 @@ +package edu.harvard.hms.dbmi.avillach.auth.config; + +import edu.harvard.hms.dbmi.avillach.auth.entity.User; +import org.springframework.cache.interceptor.KeyGenerator; + +import java.lang.reflect.Method; + +public class CustomKeyGenerator implements KeyGenerator { + + @Override + public Object generate(Object target, Method method, Object... params) { + for (Object param : params) { + if (param instanceof User user) { + return user.getEmail(); + } + } + + throw new IllegalArgumentException("No valid params found. Cannot generate cache key"); + } +} 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 04e741cca..b880d5df1 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 @@ -35,7 +35,14 @@ public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Excepti .sessionManagement((session) -> session.sessionCreationPolicy(STATELESS)) .authenticationProvider(authenticationProvider) .authorizeHttpRequests((authorizeRequests) -> - authorizeRequests.requestMatchers("/actuator/health", "/actuator/info", "/authentication/**", "/swagger.yaml", "/swagger.json","/authentication", "/okta/authentication", "/open/authentication").permitAll() + authorizeRequests.requestMatchers( + "/actuator/health", + "/actuator/info", + "/authentication", + "/authentication/**", + "/swagger.yaml", + "/swagger.json" + ).permitAll() .anyRequest().authenticated() ) .httpBasic(AbstractHttpConfigurer::disable) diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java index 2af131647..2b877ec52 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AccessRuleController.java @@ -2,7 +2,7 @@ import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import io.swagger.v3.oas.annotations.*; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.security.RolesAllowed; diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java deleted file mode 100644 index 5210d6e30..000000000 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthController.java +++ /dev/null @@ -1,75 +0,0 @@ -package edu.harvard.hms.dbmi.avillach.auth.rest; - -import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.Auth0AuthenticationService; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AuthorizationService; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.FENCEAuthenticationService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - - -/** - *

The authentication endpoint for PSAMA.

- */ -@Tag(name = "Authentication") -@Controller -@RequestMapping("/") -public class AuthController { - - private final static Logger logger = LoggerFactory.getLogger(AuthController.class.getName()); - - public final AuthorizationService authorizationService; - - public final Auth0AuthenticationService authenticationService; - public final FENCEAuthenticationService fenceAuthenticationService; - - private final String idp_provider; - - @Autowired - public AuthController(AuthorizationService authorizationService, Auth0AuthenticationService authenticationService, FENCEAuthenticationService fenceAuthenticationService, @Value("${application.idp.provider}") String idpProvider) { - this.authorizationService = authorizationService; - this.authenticationService = authenticationService; - this.fenceAuthenticationService = fenceAuthenticationService; - this.idp_provider = idpProvider; - } - - @Operation(description = "The authentication endpoint for retrieving a valid user token") - @PostMapping(path = "/authentication", consumes = "application/json", produces = "application/json") - public ResponseEntity authentication( - @Parameter(required = true, description = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") - @RequestBody Map authRequest, HttpServletRequest request) throws IOException { - logger.debug("authentication() starting..."); - String requestHost = request.getServerName(); - logger.debug("authentication() requestHost: {}", requestHost); - - if(authRequest == null) { - logger.error("authentication() authRequest is null"); - return ResponseEntity.badRequest().body("authRequest is null"); - } - - if (this.idp_provider.equalsIgnoreCase("fence")) { - logger.debug("authentication() FENCE authentication"); - HashMap fenceProfile = fenceAuthenticationService.getFENCEProfile("https://" + requestHost + "/psamaui/login/", authRequest); - return PICSUREResponse.success(fenceProfile); - } else { - logger.debug("authentication() default authentication"); - HashMap token = authenticationService.getToken(authRequest); - return PICSUREResponse.success(token); - } - } -} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthenticationController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthenticationController.java new file mode 100644 index 000000000..f1e281684 --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/AuthenticationController.java @@ -0,0 +1,71 @@ +package edu.harvard.hms.dbmi.avillach.auth.rest; + +import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse; +import edu.harvard.hms.dbmi.avillach.auth.service.AuthenticationService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.AuthenticationServiceRegistry; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.servlet.http.HttpServletRequest; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + + +/** + *

The authentication endpoint for PSAMA.

+ */ +@Tag(name = "Authentication") +@Controller +@RequestMapping("/") +public class AuthenticationController { + + private final static Logger logger = LoggerFactory.getLogger(AuthenticationController.class.getName()); + + private final AuthenticationServiceRegistry authenticationServiceRegistry; + + @Autowired + public AuthenticationController(AuthenticationServiceRegistry authenticationServiceRegistry) { + this.authenticationServiceRegistry = authenticationServiceRegistry; + } + + @Operation(description = "The authentication endpoint for retrieving a valid user token") + @PostMapping(path = "/authentication/{idpProvider}", consumes = "application/json", produces = "application/json") + public ResponseEntity authentication( + @PathVariable("idpProvider") String idpProvider, + @Parameter(required = true, description = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") + @RequestBody Map authRequest, HttpServletRequest request) throws IOException { + logger.debug("authentication() starting..."); + logger.debug("authentication() requestHost: {}", request.getServerName()); + + if (authRequest == null) { + logger.error("authentication() authRequest is null"); + return ResponseEntity.badRequest().body("authRequest is null"); + } + + AuthenticationService authenticationService = authenticationServiceRegistry.getAuthenticationService(idpProvider); + if (authenticationService == null) { + logger.error("authentication() authenticationService is null"); + return ResponseEntity.badRequest().body("authenticationService is null"); + } + + HashMap authenticate = authenticationService.authenticate(authRequest, request.getServerName()); + if (authenticate != null && !authenticate.isEmpty()) { + logger.info("authentication() User authenticated successfully."); + return PICSUREResponse.success(authenticate); + } + + logger.error("authentication() User not authenticated."); + return PICSUREResponse.unauthorizedError("User not authenticated."); + } +} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OktaAuthenticationController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OktaAuthenticationController.java deleted file mode 100644 index f8d591c58..000000000 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OktaAuthenticationController.java +++ /dev/null @@ -1,54 +0,0 @@ -package edu.harvard.hms.dbmi.avillach.auth.rest; - -import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.OktaOAuthAuthenticationService; -import io.swagger.v3.oas.annotations.tags.Tag; -import jakarta.servlet.http.HttpServletRequest; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.util.HashMap; -import java.util.Map; - -@Tag(name = "Okta Authentication Controller", description = "The authentication endpoint for Okta.") -@Controller -@RequestMapping("/okta") -public class OktaAuthenticationController { - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final OktaOAuthAuthenticationService oktaOAuthAuthenticationService; - private final String idp_provider; - - @Autowired - public OktaAuthenticationController(OktaOAuthAuthenticationService oktaOAuthAuthenticationService, - @Value("${application.idp.provider}") String idp_provider) { - this.oktaOAuthAuthenticationService = oktaOAuthAuthenticationService; - this.idp_provider = idp_provider; - } - - @PostMapping("/authentication") - public ResponseEntity authenticate(@RequestBody Map authRequest, HttpServletRequest request) { - logger.info("OKTA LOGIN ATTEMPT ___ {} ___", authRequest.get("code")); - String host = request.getServerName(); - - String idp_provider = this.idp_provider; - if (idp_provider.equalsIgnoreCase("okta")) { - HashMap authenticate = oktaOAuthAuthenticationService.authenticate(host, authRequest); - if (authenticate != null) { - return PICSUREResponse.success(authenticate); - } else { - return PICSUREResponse.unauthorizedError("User not authenticated."); - } - } else { - return PICSUREResponse.error("IDP provider not configured correctly.x"); - } - } - -} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OpenAuthenticationController.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OpenAuthenticationController.java deleted file mode 100644 index 4a8ccbec5..000000000 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/rest/OpenAuthenticationController.java +++ /dev/null @@ -1,50 +0,0 @@ -package edu.harvard.hms.dbmi.avillach.auth.rest; - -import edu.harvard.hms.dbmi.avillach.auth.model.response.PICSUREResponse; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.OpenAuthenticationService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.tags.Tag; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.util.Map; - -/** - *

The authentication endpoint for PSAMA.

- */ -@Tag(name = "Open Authentication") -@RequestMapping("/open") -@Controller -public class OpenAuthenticationController { - - private final Logger logger = LoggerFactory.getLogger(this.getClass()); - - private final OpenAuthenticationService openAuthenticationService; - private final String idp_provider; - - @Autowired - public OpenAuthenticationController(OpenAuthenticationService openAuthenticationService, @Value("${application.idp.provider}") String idp_provider) { - this.openAuthenticationService = openAuthenticationService; - this.idp_provider = idp_provider; - } - - @Operation(summary = "Authenticate a user using the open endpoint") - @PostMapping(value = "/authentication", consumes = "application/json", produces = "application/json") - public ResponseEntity authentication(@Parameter(required = true, description = "A json object that includes all Oauth authentication needs, for example, access_token and redirectURI") Map authRequest) { - logger.debug("authentication() starting..."); - if (!this.idp_provider.equalsIgnoreCase("fence")) { - Map authenticate = openAuthenticationService.authenticate(authRequest); - return PICSUREResponse.success(authenticate); - } - - return PICSUREResponse.unauthorizedError("Not authorized."); - } - -} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AuthenticationService.java new file mode 100644 index 000000000..ff0e10045 --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/AuthenticationService.java @@ -0,0 +1,15 @@ +package edu.harvard.hms.dbmi.avillach.auth.service; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public interface AuthenticationService { + + HashMap authenticate(Map authRequest, String requestHost) throws IOException; + + String getProvider(); + + boolean isEnabled(); + +} 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 new file mode 100644 index 000000000..746b30b7b --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/AccessRuleService.java @@ -0,0 +1,995 @@ +package edu.harvard.hms.dbmi.avillach.auth.service.impl; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jayway.jsonpath.JsonPath; +import com.jayway.jsonpath.PathNotFoundException; +import com.mysql.cj.xdevapi.JsonArray; +import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; +import edu.harvard.hms.dbmi.avillach.auth.entity.Application; +import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege; +import edu.harvard.hms.dbmi.avillach.auth.entity.User; +import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; +import jakarta.annotation.PostConstruct; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +@Service +public class AccessRuleService { + + private final Logger logger = LoggerFactory.getLogger(AccessRuleService.class); + + private final AccessRuleRepository accessRuleRepo; + private final ObjectMapper objectMapper = new ObjectMapper(); + + private final ConcurrentHashMap accessRuleCache = new ConcurrentHashMap<>(); + private Set allowQueryTypeRules; + + private static final String parentAccessionField = "\\\\_Parent Study Accession with Subject ID\\\\"; + private static final String topmedAccessionField = "\\\\_Topmed Study Accession with Subject ID\\\\"; + private final String fence_harmonized_consent_group_concept_path; + private final String fence_parent_consent_group_concept_path; + private final String fence_topmed_consent_group_concept_path; + private final String fence_standard_access_rules; + private final String fence_allowed_query_types; + private final String fence_harmonized_concept_path; + + private String[] underscoreFields; + + @Autowired + public AccessRuleService(AccessRuleRepository accessRuleRepo, + @Value("${fence.harmonized.consent.group.concept.path}") String fenceHarmonizedConsentGroupConceptPath, + @Value("${fence.parent.consent.group.concept.path}") String fenceParentConceptPath, + @Value("${fence.topmed.consent.group.concept.path}") String fenceTopmedConceptPath, + @Value("${fence.standard.access.rules}") String fenceStandardAccessRules, + @Value("${fence.allowed.query.types}") String fenceAllowedQueryTypes, + @Value("${fence.consent.group.concept.path}") String fenceHarmonizedConceptPath) { + this.accessRuleRepo = accessRuleRepo; + this.fence_harmonized_consent_group_concept_path = fenceHarmonizedConsentGroupConceptPath; + this.fence_parent_consent_group_concept_path = fenceParentConceptPath; + this.fence_topmed_consent_group_concept_path = fenceTopmedConceptPath; + this.fence_standard_access_rules = fenceStandardAccessRules; + this.fence_allowed_query_types = fenceAllowedQueryTypes; + this.fence_harmonized_concept_path = fenceHarmonizedConceptPath; + } + + @PostConstruct + public void init() { + // We need to set the underscoreFields here so that we can use them in the access rules during PostConstruct + // If we don't set them here, we will get a NullPointerException when we try to use them in the access rules + underscoreFields = new String[]{ + parentAccessionField, + topmedAccessionField, + fence_harmonized_consent_group_concept_path, + fence_parent_consent_group_concept_path, + fence_topmed_consent_group_concept_path, + "\\\\_VCF Sample Id\\\\", + "\\\\_studies\\\\", + "\\\\_studies_consents\\\\", //used to provide consent-level counts for open access + "\\\\_parent_consents\\\\", //parent consents not used for auth (use combined _consents) + "\\\\_Consents\\\\" ///old _Consents\Short Study... path no longer used, but still present in examples. + }; + + logger.info("fence_standard_access_rules: {}", fence_standard_access_rules); + logger.info("fence_allowed_query_types: {}", fence_allowed_query_types); + logger.info("fence_harmonized_consent_group_concept_path: {}", fence_harmonized_consent_group_concept_path); + logger.info("fence_parent_consent_group_concept_path: {}", fence_parent_consent_group_concept_path); + logger.info("fence_topmed_consent_group_concept_path: {}", fence_topmed_consent_group_concept_path); + logger.info("fence_harmonized_concept_path: {}", fence_harmonized_concept_path); + logger.info("underscoreFields: {}", Arrays.toString(underscoreFields)); + } + + public Optional getAccessRuleById(String accessRuleId) { + return accessRuleRepo.findById(UUID.fromString(accessRuleId)); + } + + public List getAllAccessRules() { + return accessRuleRepo.findAll(); + } + + public List addAccessRule(List accessRules) { + accessRules.forEach(accessRule -> { + if (accessRule.getEvaluateOnlyByGates() == null) + accessRule.setEvaluateOnlyByGates(false); + + if (accessRule.getCheckMapKeyOnly() == null) + accessRule.setCheckMapKeyOnly(false); + + if (accessRule.getCheckMapNode() == null) + accessRule.setCheckMapNode(false); + + if (accessRule.getGateAnyRelation() == null) + accessRule.setGateAnyRelation(false); + }); + + return this.accessRuleRepo.saveAll(accessRules); + } + + public List updateAccessRules(List accessRules) { + return this.accessRuleRepo.saveAll(accessRules); + } + + @Transactional + public List removeAccessRuleById(String accessRuleId) { + this.accessRuleRepo.deleteById(UUID.fromString(accessRuleId)); + return this.accessRuleRepo.findAll(); + } + + public AccessRule save(AccessRule accessRule) { + return this.accessRuleRepo.save(accessRule); + } + + public AccessRule getAccessRuleByName(String arName) { + return this.accessRuleRepo.findByName(arName); + } + + @Cacheable(value = "mergedRulesCache", keyGenerator = "customKeyGenerator") + public Set getAccessRulesForUserAndApp(User user, Application application) { + try { + Set privileges = user.getPrivilegesByApplication(application); + if (privileges == null || privileges.isEmpty()) { + return new HashSet<>(); + } + + Set detachedMergedRules = new HashSet<>(); + for (AccessRule rule : preProcessAccessRules(privileges)) { + detachedMergedRules.add(objectMapper.readValue(objectMapper.writeValueAsString(rule), AccessRule.class)); + } + + return detachedMergedRules; + } catch (Exception e) { + logger.error("Error populating or retrieving data from cache: ", e); + } + + return new HashSet<>(); + } + + /** + * Evicts the user from all AccessRule caches + * @param email the email to evict + */ + public void evictFromCache(String email) { + logger.info("evictFromCache called for user.email: {}", email); + evictFromMergedAccessRuleCache(email); + evictFromPreProcessedAccessRules(email); + } + + @CacheEvict(value = "mergedRulesCache") + public void evictFromMergedAccessRuleCache(String email) { + if (email == null || email.isEmpty()) { + logger.warn("evictFromMergedAccessRuleCache() was called with a null or empty email"); + return; + } + logger.info("evictFromMergedAccessRuleCache() evicting cache for user: {}", email); + } + + @Cacheable(value = "preProcessedAccessRules", keyGenerator = "customKeyGenerator") + public Set cachedPreProcessAccessRules(User user, Set privileges) { + Set accessRules = new HashSet<>(); + for (Privilege privilege : privileges) { + accessRules.addAll(privilege.getAccessRules()); + } + + return preProcessARBySortedKeys(accessRules); + } + + public Set preProcessAccessRules(Set privileges) { + Set accessRules = new HashSet<>(); + for (Privilege privilege : privileges) { + accessRules.addAll(privilege.getAccessRules()); + } + + return preProcessARBySortedKeys(accessRules); + } + + @CacheEvict(value = "preProcessedAccessRules", key = "#email", condition = "#email!=null") + public void evictFromPreProcessedAccessRules(String email) { + if (email == null || email.isEmpty()) { + logger.warn("evictFromPreProcessedAccessRules() was called with a null or empty email"); + return; + } + logger.info("evictFromPreProcessedAccessRules() evicting cache for user: {}", email); + } + + public Set preProcessARBySortedKeys(Set accessRules) { + Map> accessRuleMap = new HashMap<>(); + + for (AccessRule accessRule : accessRules) { + + // 1st generate the key by grabbing all related string and put them together in order + // we use a treeSet here to put orderly combine Strings together + Set keys = new TreeSet<>(); + + // the current accessRule rule + keys.add(accessRule.getRule()); + + // all gates' UUID as strings + keys.add(accessRule.getType().toString()); + + if (accessRule.getGates() != null) { + for (AccessRule gate : accessRule.getGates()) { + keys.add(gate.getUuid().toString()); + } + } + + // all sub accessRule rules + if (accessRule.getSubAccessRule() != null) { + for (AccessRule subAccessRule : accessRule.getSubAccessRule()) { + keys.add(subAccessRule.getRule()); + } + } + Boolean checkMapKeyOnly = accessRule.getCheckMapKeyOnly(), + checkMapNode = accessRule.getCheckMapNode(), + evaluateOnlyByGates = accessRule.getEvaluateOnlyByGates(), + gateAnyRelation = accessRule.getGateAnyRelation(); + + keys.add(checkMapKeyOnly == null ? "null" : Boolean.toString(checkMapKeyOnly)); + keys.add(checkMapNode == null ? "null" : Boolean.toString(checkMapNode)); + keys.add(evaluateOnlyByGates == null ? "null" : Boolean.toString(evaluateOnlyByGates)); + keys.add(gateAnyRelation == null ? "null" : Boolean.toString(gateAnyRelation)); + + String key = String.join("", keys); + if (accessRuleMap.containsKey(key)) { + accessRuleMap.get(key).add(accessRule); + } else { + Set accessRuleSet = new HashSet<>(); + accessRuleSet.add(accessRule); + accessRuleMap.put(key, accessRuleSet); + } + } + + return mergeSameKeyAccessRules(accessRuleMap.values()); + } + + private Set mergeSameKeyAccessRules(Collection> accessRuleMap) { + Set accessRules = new HashSet<>(); + for (Set accessRulesSet : accessRuleMap) { + AccessRule accessRule = null; + for (AccessRule innerAccessRule : accessRulesSet) { + accessRule = mergeAccessRules(accessRule, innerAccessRule); + } + if (accessRule != null) { + accessRules.add(accessRule); + } + } + return accessRules; + } + + private AccessRule mergeAccessRules(AccessRule baseAccessRule, AccessRule accessRuleToBeMerged) { + if (baseAccessRule == null) { + accessRuleToBeMerged.getMergedValues().add(accessRuleToBeMerged.getValue()); + return accessRuleToBeMerged; + } + + if (baseAccessRule.getSubAccessRule() != null && accessRuleToBeMerged.getSubAccessRule() != null) { + baseAccessRule.getSubAccessRule().addAll(accessRuleToBeMerged.getSubAccessRule()); + } else if (baseAccessRule.getSubAccessRule() == null && accessRuleToBeMerged.getSubAccessRule() != null) { + baseAccessRule.setSubAccessRule(accessRuleToBeMerged.getSubAccessRule()); + } + + baseAccessRule.getMergedValues().add(accessRuleToBeMerged.getValue()); + if (baseAccessRule.getMergedName().startsWith("Merged|")) { + baseAccessRule.setMergedName(baseAccessRule.getMergedName() + "|" + accessRuleToBeMerged.getName()); + } else { + baseAccessRule.setMergedName("Merged|" + baseAccessRule.getName() + "|" + accessRuleToBeMerged.getName()); + } + + return baseAccessRule; + } + + public boolean evaluateAccessRule(Object parsedRequestBody, AccessRule accessRule) { + logger.debug("evaluateAccessRule() starting with: {}", parsedRequestBody); + logger.debug("evaluateAccessRule() access rule: {}", accessRule.getName()); + + Set gates = accessRule.getGates(); + boolean gatesPassed = true; + + // depends on the flag getGateAnyRelation is true or false, + // the logic of checking if apply gate will be changed + // the following cases are gate passed: + // 1. if gates are null or empty + // 2. if getGateAnyRelation is false, all gates passed + // 3. if getGateAnyRelation is true, one of the gate passed + if (gates != null && !gates.isEmpty()) { + if (accessRule.getGateAnyRelation() == null || !accessRule.getGateAnyRelation()) { + // All gates are AND relationship + // means one fails all fail + for (AccessRule gate : gates) { + if (!evaluateAccessRule(parsedRequestBody, gate)) { + logger.error("evaluateAccessRule() gate {} failed: {} ____ {}", gate.getName(), gate.getRule(), gate.getValue()); + gatesPassed = false; + break; + } + } + } else { + // All gates are OR relationship + // means one passes all pass + gatesPassed = false; + for (AccessRule gate : gates) { + if (evaluateAccessRule(parsedRequestBody, gate)) { + logger.debug("evaluateAccessRule() gate {} passed ", gate.getName()); + gatesPassed = true; + break; + } + } + + if (!gatesPassed) { + logger.debug("all OR gates failed"); + } + } + } + + if (accessRule.getEvaluateOnlyByGates() != null && accessRule.getEvaluateOnlyByGates()) { + logger.debug("evaluateAccessRule() eval only by gates"); + return gatesPassed; + } + + if (gatesPassed) { + logger.debug("evaluateAccessRule() gates passed"); + if (!extractAndCheckRule(accessRule, parsedRequestBody)) { + logger.debug("Query Rejected by rule(1) {} :: {} :: {}", accessRule.getRule(), accessRule.getType(), accessRule.getValue()); + return false; + } else { + if (accessRule.getSubAccessRule() != null) { + for (AccessRule subAccessRule : accessRule.getSubAccessRule()) { + if (!extractAndCheckRule(subAccessRule, parsedRequestBody)) { + logger.debug("Query Rejected by rule(2) {} :: {} :: {}", subAccessRule.getRule(), subAccessRule.getType(), subAccessRule.getValue()); + return false; + } + } + } + } + } else { + logger.debug("evaluateAccessRule() gates failed"); + return false; + } + + return true; + } + + public boolean extractAndCheckRule(AccessRule accessRule, Object parsedRequestBody) { + logger.debug("extractAndCheckRule() starting"); + String rule = accessRule.getRule(); + + if (rule == null || rule.isEmpty()) + return true; + + Object requestBodyValue; + int accessRuleType = accessRule.getType(); + + try { + requestBodyValue = JsonPath.parse(parsedRequestBody).read(rule); + + // Json parse will always return a list even when we want a map (to check keys) + if (requestBodyValue instanceof JsonArray && ((JsonArray) requestBodyValue).size() == 1) { + requestBodyValue = ((JsonArray) requestBodyValue).get(0); + } + + } catch (PathNotFoundException ex) { + if (accessRuleType == AccessRule.TypeNaming.IS_EMPTY) { + // We could return accessRuleType == AccessRule.TypeNaming.IS_EMPTY directly, but we want to log the reason + logger.debug("extractAndCheckRule() -> JsonPath.parse().read() PathNotFound; passing IS_EMPTY rule"); + return true; + } + logger.debug("extractAndCheckRule() -> JsonPath.parse().read() throws exception with parsedRequestBody - {} : {} - {}", parsedRequestBody, ex.getClass().getSimpleName(), ex.getMessage()); + return false; + } + + if (accessRuleType == AccessRule.TypeNaming.IS_EMPTY + || accessRuleType == AccessRule.TypeNaming.IS_NOT_EMPTY) { + if (requestBodyValue == null + || (requestBodyValue instanceof String && ((String) requestBodyValue).isEmpty()) + || (requestBodyValue instanceof Collection && ((Collection) requestBodyValue).isEmpty()) + || (requestBodyValue instanceof Map && ((Map) requestBodyValue).isEmpty())) { + return accessRuleType == AccessRule.TypeNaming.IS_EMPTY; + } else { + return accessRuleType == AccessRule.TypeNaming.IS_NOT_EMPTY; + } + } + + return evaluateNode(requestBodyValue, accessRule); + } + + private boolean evaluateNode(Object requestBodyValue, AccessRule accessRule) { + logger.debug("evaluateNode() starting: {} :: {} :: {}", accessRule.getRule(), accessRule.getType(), accessRule.getMergedValues().isEmpty() ? accessRule.getValue() : ("Merged " + Arrays.deepToString(accessRule.getMergedValues().toArray()))); + logger.trace("evaluateNode() requestBody {} {}", requestBodyValue.getClass().getName(), requestBodyValue instanceof Collection ? + Arrays.deepToString(((Collection) requestBodyValue).toArray()) : + requestBodyValue.toString()); + + return switch (requestBodyValue) { + case String s -> decisionMaker(accessRule, s); + case Collection collection -> evaluateCollection(collection, accessRule); + case Map map when accessRule.getCheckMapNode() != null && accessRule.getCheckMapNode() -> + evaluateMap(requestBodyValue, accessRule); + default -> true; + }; + } + + private boolean evaluateMap(Object requestBodyValue, AccessRule accessRule) { + switch (accessRule.getType()) { + case (AccessRule.TypeNaming.ANY_EQUALS): + case (AccessRule.TypeNaming.ANY_CONTAINS): + case (AccessRule.TypeNaming.ANY_REG_MATCH): + for (Map.Entry entry : ((Map) requestBodyValue).entrySet()) { + if (decisionMaker(accessRule, (String) entry.getKey())) + return true; + + if ((accessRule.getCheckMapKeyOnly() == null || !accessRule.getCheckMapKeyOnly()) + && evaluateNode(entry.getValue(), accessRule)) + return true; + } + return false; + default: + if (((Map) requestBodyValue).isEmpty()) { + switch (accessRule.getType()) { + case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE): + case (AccessRule.TypeNaming.ALL_EQUALS): + case (AccessRule.TypeNaming.ALL_CONTAINS): + case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE): + return false; + case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY): + case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE): + default: + return true; + } + } + for (Map.Entry entry : ((Map) requestBodyValue).entrySet()) { + if (!decisionMaker(accessRule, (String) entry.getKey())) + return false; + + if ((accessRule.getCheckMapKeyOnly() == null || !accessRule.getCheckMapKeyOnly()) + && !evaluateNode(entry.getValue(), accessRule)) + return false; + } + + } + + return true; + } + + private Boolean evaluateCollection(Collection requestBodyValue, AccessRule accessRule) { + switch (accessRule.getType()) { + case (AccessRule.TypeNaming.ANY_EQUALS): + case (AccessRule.TypeNaming.ANY_CONTAINS): + case (AccessRule.TypeNaming.ANY_REG_MATCH): + for (Object item : requestBodyValue) { + if (item instanceof String) { + if (decisionMaker(accessRule, (String) item)) { + return true; + } + } else { + if (evaluateNode(item, accessRule)) { + return true; + } + } + } + return false; + default: + if (requestBodyValue.isEmpty()) { + switch (accessRule.getType()) { + case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE): + case (AccessRule.TypeNaming.ALL_EQUALS): + case (AccessRule.TypeNaming.ALL_CONTAINS): + case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE): + return false; + case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY): + case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE): + default: + return true; + } + } + + for (Object item : requestBodyValue) { + if (item instanceof String) { + if (!decisionMaker(accessRule, (String) item)) { + return false; + } + } else { + if (!evaluateNode(item, accessRule)) + return false; + } + } + } + + return true; + } + + public boolean decisionMaker(AccessRule accessRule, String requestBodyValue) { + if (accessRule.getMergedValues().isEmpty()) { + String value = accessRule.getValue(); + if (value == null) { + return requestBodyValue == null; + } + return _decisionMaker(accessRule, requestBodyValue, value); + } + + // recursively check the values + // until one of them is true + // if there is only one element in the merged value set + // the operation equals to _decisionMaker(accessRule, requestBodyValue, value) + boolean res = false; + for (String s : accessRule.getMergedValues()) { + // check the special case value is null + // if value is null, the check will stop here and + // not goes to _decisionMaker() + if (s == null) { + if (requestBodyValue == null) { + res = true; + break; + } else { + continue; + } + } + + // all the merged values are OR relationship + // means if you pass one of them, you pass the rule + if (_decisionMaker(accessRule, requestBodyValue, s)) { + res = true; + break; + } + } + return res; + } + + private boolean _decisionMaker(AccessRule accessRule, String requestBodyValue, String value) { + logger.debug("_decisionMaker() starting"); + logger.debug("_decisionMaker() access rule:{}", accessRule.getName()); + logger.debug(requestBodyValue); + logger.debug(value); + + return switch (accessRule.getType()) { + case AccessRule.TypeNaming.NOT_CONTAINS -> !requestBodyValue.contains(value); + case AccessRule.TypeNaming.NOT_CONTAINS_IGNORE_CASE -> !requestBodyValue.toLowerCase().contains(value.toLowerCase()); + case (AccessRule.TypeNaming.NOT_EQUALS) -> !value.equals(requestBodyValue); + case (AccessRule.TypeNaming.ANY_EQUALS), (AccessRule.TypeNaming.ALL_EQUALS) -> value.equals(requestBodyValue); + case (AccessRule.TypeNaming.ALL_CONTAINS), (AccessRule.TypeNaming.ANY_CONTAINS), (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY) -> requestBodyValue.contains(value); + case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE), (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE) -> requestBodyValue.toLowerCase().contains(value.toLowerCase()); + case (AccessRule.TypeNaming.NOT_EQUALS_IGNORE_CASE) -> !value.equalsIgnoreCase(requestBodyValue); + case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE) -> value.equalsIgnoreCase(requestBodyValue); + case (AccessRule.TypeNaming.ALL_REG_MATCH), (AccessRule.TypeNaming.ANY_REG_MATCH) -> requestBodyValue.matches(value); + default -> { + logger.warn("evaluateAccessRule() incoming accessRule type is out of scope. Just return true."); + yield true; + } + }; + } + + /** + * Configures the AccessRule with gates and sub-rules. + * + * @param ar The AccessRule to configure. + * @param studyIdentifier The study identifier. + * @param consent_group The consent group. + * @param conceptPath The concept path. + * @param projectAlias The project alias. + * @param parent Whether to include parent gates. + * @param harmonized Whether to include harmonized gates. + * @param topmed Whether to include Topmed gates. + */ + protected void configureAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias, boolean parent, boolean harmonized, boolean topmed) { + if (ar.getGates() == null) { + ar.setGates(new HashSet<>()); + ar.getGates().addAll(getGates(parent, harmonized, topmed)); + + if (ar.getSubAccessRule() == null) { + ar.setSubAccessRule(new HashSet<>()); + } + ar.getSubAccessRule().addAll(getAllowedQueryTypeRules()); + ar.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, conceptPath, projectAlias)); + ar.getSubAccessRule().addAll(getTopmedRestrictedSubRules()); + this.save(ar); + } + } + + /** + * Configures the harmonized AccessRule with gates and sub-rules. + * + * @param ar The AccessRule to configure. + * @param studyIdentifier The study identifier. + * @param consent_group The consent group. + * @param conceptPath The concept path. + * @param projectAlias The project alias. + */ + protected void configureHarmonizedAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias) { + if (ar.getGates() == null) { + ar.setGates(new HashSet<>()); + ar.getGates().add(upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", true, "harmonized data")); + + if (ar.getSubAccessRule() == null) { + ar.setSubAccessRule(new HashSet<>()); + } + ar.getSubAccessRule().addAll(getAllowedQueryTypeRules()); + ar.getSubAccessRule().addAll(getHarmonizedSubRules()); + ar.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, conceptPath, projectAlias)); + this.save(ar); + } + } + + private Set getAllowedQueryTypeRules() { + if (allowQueryTypeRules == null) { + allowQueryTypeRules = loadAllowedQueryTypeRules(); + } + + return allowQueryTypeRules; + } + + /** + * Retrieves or creates AccessRules for allowed query types. + * + * @return A set of AccessRules for allowed query types. + */ + private Set loadAllowedQueryTypeRules() { + // Initialize a set to hold the AccessRules + Set rules = new HashSet<>(); + // Split the allowed query types from the configuration + String[] allowedTypes = this.fence_allowed_query_types.split(","); + + // Iterate over each allowed query type + for (String queryType : allowedTypes) { + // Construct the AccessRule name + String ar_name = "AR_ALLOW_" + queryType; + + // Log the creation of a new AccessRule + AccessRule ar = getOrCreateAccessRule( + ar_name, + "MANAGED SUB AR to allow " + queryType + " Queries", + "$.query.query.expectedResultType", + AccessRule.TypeNaming.ALL_EQUALS, + queryType, + false, + false, + false, + false + ); + + // Add the newly created rule to the set + rules.add(ar); + } + // Return the set of AccessRules + return rules; + } + + private Collection getTopmedRestrictedSubRules() { + Set rules = new HashSet(); + rules.add(upsertTopmedRestrictedSubRule("CATEGORICAL", "$.query.query.variantInfoFilters[*].categoryVariantInfoFilters.*")); + rules.add(upsertTopmedRestrictedSubRule("NUMERIC", "$.query.query.variantInfoFilters[*].numericVariantInfoFilters.*")); + + return rules; + } + + /** + * Creates and returns a restricted sub-rule AccessRule for Topmed. + * topmed restriction rules don't need much configuration. Just deny all access. + * + * @param type The type of the Topmed restriction. + * @param rule The rule expression. + * @return The created AccessRule. + */ + private AccessRule upsertTopmedRestrictedSubRule(String type, String rule) { + // Construct the AccessRule name + String ar_name = "AR_TOPMED_RESTRICTED_" + type; + // Check if the AccessRule already exists + AccessRule ar = this.getAccessRuleByName(ar_name); + if (ar != null) { + // Log and return the existing rule + logger.debug("Found existing rule: {}", ar.getName()); + return ar; + } + + // Log the creation of a new AccessRule + // Create the AccessRule using the createAccessRule method + return getOrCreateAccessRule( + ar_name, + "MANAGED SUB AR for restricting " + type + " genomic concepts", + rule, + AccessRule.TypeNaming.IS_EMPTY, + null, + false, + false, + false, + false + ); + } + + protected Collection getPhenotypeSubRules(String studyIdentifier, String conceptPath, String alias) { + + Set rules = new HashSet(); + //categorical filters will always contain at least one entry (for the consent groups); it will never be empty + rules.add(createPhenotypeSubRule(fence_parent_consent_group_concept_path, "ALLOW_PARENT_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); + + for (String underscorePath : underscoreFields) { + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); + } + + rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); + rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true)); + rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); + rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false)); + + return rules; + } + + /** + * Harmonized rules should allow the user to supply paretn and top med consent groups; this allows a single harmonized + * rules instead of splitting between a topmed+harmonized and parent+harmonized + * + * @return + */ + private Collection getHarmonizedSubRules() { + + Set rules = new HashSet(); + //categorical filters will always contain at least one entry (for the consent groups); it will never be empty + rules.add(createPhenotypeSubRule(fence_parent_consent_group_concept_path, "ALLOW_PARENT_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); + rules.add(createPhenotypeSubRule(fence_harmonized_consent_group_concept_path, "ALLOW_HARMONIZED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); + rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); + + for (String underscorePath : underscoreFields) { + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); + } + + rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); + rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true)); + rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); + rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false)); + + return rules; + } + + + /** + * generate and return a set of rules that disallow access to phenotype data (only genomic filters allowed) + * + * @return + */ + protected Collection getPhenotypeRestrictedSubRules(String studyIdentifier, String consentCode, String alias) { + Set rules = new HashSet(); + //categorical filters will always contain at least one entry (for the consent groups); it will never be empty + rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); + + for (String underscorePath : underscoreFields) { + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); + rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); + } + + rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.numericFilters.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_NUMERIC", false)); + rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_REQUIRED_FIELDS", false)); + + return rules; + } + + /** + * Return a set of gates that identify which consent values have been provided. the boolean parameters indicate + * if a value in the specified consent location should allow this gate to pass. + * + * @param parent + * @param harmonized + * @param topmed + * @return + */ + private Collection getGates(boolean parent, boolean harmonized, boolean topmed) { + Set gates = new HashSet(); + gates.add(upsertConsentGate("PARENT_CONSENT", "$.query.query.categoryFilters." + fence_parent_consent_group_concept_path + "[*]", parent, "parent study data")); + gates.add(upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", harmonized, "harmonized data")); + gates.add(upsertConsentGate("TOPMED_CONSENT", "$.query.query.categoryFilters." + fence_topmed_consent_group_concept_path + "[*]", topmed, "Topmed data")); + + return gates; + } + + protected void populateAccessRule(AccessRule rule, boolean includeParent, boolean includeHarmonized, boolean includeTopmed) { + if (rule.getGates() == null) { + rule.setGates(new HashSet<>(getGates(includeParent, includeHarmonized, includeTopmed))); + } + + if (rule.getSubAccessRule() == null) { + rule.setSubAccessRule(new HashSet<>(getAllowedQueryTypeRules())); + } + + this.save(rule); + } + + protected void populateHarmonizedAccessRule(AccessRule rule, String parentConceptPath, String studyIdentifier, String projectAlias) { + if (rule.getGates() == null) { + rule.setGates(new HashSet<>(Collections.singletonList( + upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", true, "harmonized data") + ))); + } + + if (rule.getSubAccessRule() == null) { + rule.setSubAccessRule(new HashSet<>(getAllowedQueryTypeRules())); + rule.getSubAccessRule().addAll(getHarmonizedSubRules()); + rule.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, parentConceptPath, projectAlias)); + } + + this.save(rule); + } + + // A set of standard access rules that are added to all privileges + // to cache the standard access rules + private Set standardAccessRules; + + protected void addStandardAccessRules(Set accessRules) { + if (standardAccessRules != null && !standardAccessRules.isEmpty()) { + accessRules.addAll(standardAccessRules); + } else { + standardAccessRules = new HashSet<>(); + for (String arName : fence_standard_access_rules.split(",")) { + if (arName.startsWith("AR_")) { + logger.info("Adding AccessRule {} to privilege", arName); + AccessRule ar = this.getAccessRuleByName(arName); + if (ar != null) { + standardAccessRules.add(ar); + } else { + logger.warn("Unable to find an access rule with name {}", arName); + } + } + } + + accessRules.addAll(standardAccessRules); + } + } + + + /** + * Creates and returns a consent access rule AccessRule. + * Generates Main rule only; gates & sub-rules attached after calling this + * prentRule should be null if this is the main rule, or the appropriate value if this is a sub-rule + * + * @param studyIdentifier The study identifier. + * @param consent_group The consent group. + * @param label The label for the rule. + * @param consent_path The consent path. + * @return The created AccessRule. + */ + protected AccessRule createConsentAccessRule(String studyIdentifier, String consent_group, String label, String consent_path) { + String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_CONSENT_" + studyIdentifier + "_" + consent_group + "_" + label : "AR_CONSENT_" + studyIdentifier; + String description = (consent_group != null && !consent_group.isEmpty()) ? "MANAGED AR for " + studyIdentifier + "." + consent_group + " clinical concepts" : "MANAGED AR for " + studyIdentifier + " clinical concepts"; + String ruleText = "$.query.query.categoryFilters." + consent_path + "[*]"; + String arValue = (consent_group != null && !consent_group.isEmpty()) ? studyIdentifier + "." + consent_group : studyIdentifier; + + return getOrCreateAccessRule( + ar_name, + description, + ruleText, + AccessRule.TypeNaming.ALL_CONTAINS, + arValue, + false, + false, + false, + false + ); + } + + /** + * Creates and returns a Topmed access rule AccessRule. + * Generates Main Rule only; gates & sub-rules attached by calling method + * + * @param project_name The name of the project. + * @param consent_group The consent group. + * @param label The label for the rule. + * @return The created AccessRule. + */ + protected AccessRule upsertTopmedAccessRule(String project_name, String consent_group, String label) { + String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label : "AR_TOPMED_" + project_name + "_" + label; + String description = "MANAGED AR for " + project_name + "." + consent_group + " Topmed data"; + String ruleText = "$.query.query.categoryFilters." + fence_topmed_consent_group_concept_path + "[*]"; + String arValue = (consent_group != null && !consent_group.isEmpty()) ? project_name + "." + consent_group : project_name; + + return getOrCreateAccessRule( + ar_name, + description, + ruleText, + AccessRule.TypeNaming.ALL_CONTAINS, + arValue, + false, + false, + false, + false + ); + } + + /** + * Creates and returns a harmonized access rule AccessRule for Topmed. + * Generates Main Rule only; gates & sub rules attached by calling method + * + * @param project_name The name of the project. + * @param consent_group The consent group. + * @param label The label for the rule. + * @return The created AccessRule. + */ + protected AccessRule upsertHarmonizedAccessRule(String project_name, String consent_group, String label) { + String ar_name = "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label; + logger.info("upsertHarmonizedAccessRule() Creating new access rule {}", ar_name); + String description = "MANAGED AR for " + project_name + "." + consent_group + " Topmed data"; + String ruleText = "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]"; + String arValue = project_name + "." + consent_group; + + return getOrCreateAccessRule( + ar_name, + description, + ruleText, + AccessRule.TypeNaming.ALL_CONTAINS, + arValue, + false, + false, + false, + false + ); + } + + /** + * Creates and returns a consent gate AccessRule. + * Insert a new gate (if it doesn't exist yet) to identify if consent values are present in the query. + * return an existing gate named GATE_{gateName}_(PRESENT|MISSING) if it exists. + * + * @param gateName The name of the gate. + * @param rule The rule expression. + * @param is_present Whether the gate is for present or missing consent. + * @param description The description of the gate. + * @return The created AccessRule. + */ + private AccessRule upsertConsentGate(String gateName, String rule, boolean is_present, String description) { + gateName = "GATE_" + gateName + "_" + (is_present ? "PRESENT" : "MISSING"); + return getOrCreateAccessRule( + gateName, + "MANAGED GATE for " + description + " consent " + (is_present ? "present" : "missing"), + rule, + is_present ? AccessRule.TypeNaming.IS_NOT_EMPTY : AccessRule.TypeNaming.IS_EMPTY, + null, + false, + false, + false, + false + ); + } + + private AccessRule createPhenotypeSubRule(String conceptPath, String alias, String rule, int ruleType, String label, boolean useMapKey) { + String ar_name = "AR_PHENO_" + alias + "_" + label; + logger.info("createPhenotypeSubRule() Creating new access rule {}", ar_name); + return getOrCreateAccessRule( + ar_name, + "MANAGED SUB AR for " + alias + " " + label + " clinical concepts", + rule, + ruleType, + ruleType == AccessRule.TypeNaming.IS_NOT_EMPTY ? null : conceptPath, + useMapKey, + useMapKey, + false, + false + ); + } + + private AccessRule getOrCreateAccessRule(String name, String description, String rule, int type, String value, boolean checkMapKeyOnly, boolean checkMapNode, boolean evaluateOnlyByGates, boolean gateAnyRelation) { + return accessRuleCache.computeIfAbsent(name, key -> { + AccessRule ar = this.getAccessRuleByName(key); + if (ar == null) { + logger.info("Creating new access rule {}", key); + ar = new AccessRule(); + ar.setName(name); + ar.setDescription(description); + ar.setRule(rule); + ar.setType(type); + ar.setValue(value); + ar.setCheckMapKeyOnly(checkMapKeyOnly); + ar.setCheckMapNode(checkMapNode); + ar.setEvaluateOnlyByGates(evaluateOnlyByGates); + ar.setGateAnyRelation(gateAnyRelation); + this.save(ar); + } + + return ar; + }); + } +} diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/PrivilegeService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/PrivilegeService.java index 8befa75cc..8d6fae8df 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/PrivilegeService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/PrivilegeService.java @@ -1,18 +1,26 @@ package edu.harvard.hms.dbmi.avillach.auth.service.impl; +import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; +import edu.harvard.hms.dbmi.avillach.auth.entity.Application; 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.model.fenceMapping.StudyMetaData; import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository; +import edu.harvard.hms.dbmi.avillach.auth.utils.FenceMappingUtility; +import jakarta.annotation.PostConstruct; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import java.util.*; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; import static edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming.AuthRoleNaming.ADMIN; @@ -22,10 +30,42 @@ public class PrivilegeService { private final static Logger logger = LoggerFactory.getLogger(PrivilegeService.class.getName()); private final PrivilegeRepository privilegeRepository; + private final ApplicationService applicationService; + private final AccessRuleService accessRuleService; + private final FenceMappingUtility fenceMappingUtility; + + private Application picSureApp; + private final String variantAnnotationColumns; + + private final String fence_harmonized_consent_group_concept_path; + private final String fence_parent_consent_group_concept_path; + private final String fence_topmed_consent_group_concept_path; + + private String fence_harmonized_concept_path; + private static final String topmedAccessionField = "\\\\_Topmed Study Accession with Subject ID\\\\"; @Autowired - protected PrivilegeService(PrivilegeRepository privilegeRepository) { + protected PrivilegeService(PrivilegeRepository privilegeRepository, ApplicationService applicationService, AccessRuleService accessRuleService, FenceMappingUtility fenceMappingUtility, + @Value("${fence.variant.annotation.columns}") String variantAnnotationColumns, + @Value("${fence.harmonized.consent.group.concept.path}") String fenceHarmonizedConsentGroupConceptPath, + @Value("${fence.parent.consent.group.concept.path}") String fenceParentConceptPath, + @Value("${fence.topmed.consent.group.concept.path}") String fenceTopmedConceptPath, + @Value("${fence.consent.group.concept.path}") String fenceHarmonizedConceptPath) { this.privilegeRepository = privilegeRepository; + this.applicationService = applicationService; + this.accessRuleService = accessRuleService; + this.fenceMappingUtility = fenceMappingUtility; + this.variantAnnotationColumns = variantAnnotationColumns; + this.fence_harmonized_consent_group_concept_path = fenceHarmonizedConsentGroupConceptPath; + this.fence_parent_consent_group_concept_path = fenceParentConceptPath; + this.fence_topmed_consent_group_concept_path = fenceTopmedConceptPath; + this.fence_harmonized_concept_path = fenceHarmonizedConceptPath; + } + + @PostConstruct + private void init() { + picSureApp = applicationService.getApplicationByName("PICSURE"); + logger.info("variantAnnotationColumns: {}", variantAnnotationColumns); } @Transactional @@ -37,7 +77,7 @@ public List deletePrivilegeByPrivilegeId(String privilegeId) { // Get the principal name from the security context String principalName = securityContext.getAuthentication().getName(); - if (ADMIN.equals(privilege.get().getName())) { + if (ADMIN.equals(privilege.get().getName())) { logger.info("User: {}, is trying to remove the system admin privilege: " + ADMIN, principalName); throw new RuntimeException("System Admin privilege cannot be removed - uuid: " + privilege.get().getUuid().toString() + ", name: " + privilege.get().getName()); @@ -71,4 +111,297 @@ public Privilege findByName(String privilegeName) { public Privilege save(Privilege privilege) { return this.privilegeRepository.save(privilege); } + + public Set addPrivileges(Role r) { + String roleName = r.getName(); + logger.info("addFENCEPrivileges() starting, adding privilege(s) to role {}", roleName); + + //each project can have up to three privileges: Parent | Harmonized | Topmed + //harmonized has 2 ARs for parent + harminized and harmonized only + //Topmed has up to three ARs for topmed / topmed + parent / topmed + harmonized + Set privs = r.getPrivileges(); + if (privs == null) { + privs = new HashSet(); + } + + //e.g. FENCE_phs0000xx_c2 or FENCE_tutorial-biolinc_camp + String project_name = extractProject(roleName); + if (project_name.length() <= 0) { + logger.warn("addFENCEPrivileges() role name: {} returned an empty project name", roleName); + } + String consent_group = extractConsentGroup(roleName); + if (consent_group.length() <= 0) { + logger.warn("addFENCEPrivileges() role name: {} returned an empty consent group", roleName); + } + logger.info("addFENCEPrivileges() project name: {} consent group: {}", project_name, consent_group); + + // Look up the metadata by consent group. + StudyMetaData projectMetadata = getFENCEMappingforProjectAndConsent(project_name, consent_group); + + if (projectMetadata == null) { + //no privileges means no access to this project. just return existing set of privs. + logger.warn("No metadata available for project {}.{}", project_name, consent_group); + return privs; + } + + logger.info("addPrivileges() This is a new privilege"); + + String dataType = projectMetadata.getDataType(); + Boolean isHarmonized = projectMetadata.getIsHarmonized(); + String concept_path = projectMetadata.getTopLevelPath(); + String projectAlias = projectMetadata.getAbbreviatedName(); + + // we need to add escape sequence back in to the path for parsing later (also need to double escape the regex) + // we need to do this for the query Template and scopes, but should NOT do this for the rules. + if (concept_path != null) { + concept_path = concept_path.replaceAll("\\\\", "\\\\\\\\"); + } + + if (dataType != null && dataType.contains("G")) { + //insert genomic/topmed privs - this will also add rules for including harmonized & parent data if applicable + privs.add(upsertTopmedPrivilege(project_name, projectAlias, consent_group, concept_path, isHarmonized)); + } + + if (dataType != null && dataType.contains("P")) { + //insert clinical privs + logger.info("addPrivileges() project:{} consent_group:{} concept_path:{}", project_name, consent_group, concept_path); + privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, false)); + + //if harmonized study, also create harmonized privileges + if (Boolean.TRUE.equals(isHarmonized)) { + privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, true)); + } + } + + //projects without G or P in data_type are skipped + if (dataType == null || (!dataType.contains("P") && !dataType.contains("G"))) { + logger.warn("Missing study type for {} {}. Skipping.", project_name, consent_group); + } + + logger.info("addPrivileges() Finished"); + return privs; + } + + /** + * Creates a privilege with a set of access rules that allow queries containing a consent group to pass if the query only contains valid entries that match conceptPath. If the study is harmonized, + * this also creates an access rule to allow access when using the harmonized consent concept path. + * Privileges created with this method will deny access if any genomic filters (topmed data) are included. + * + * @param studyIdentifier The study identifier + * @param consent_group The consent group + * @param conceptPath The concept path + * @param isHarmonized Whether the study is harmonized + * @return The created privilege + */ + private Privilege upsertClinicalPrivilege(String studyIdentifier, String projectAlias, String consent_group, String conceptPath, boolean isHarmonized) { + // Construct the privilege name + String privilegeName = (consent_group != null && !consent_group.isEmpty()) ? + "PRIV_MANAGED_" + studyIdentifier + "_" + consent_group + (isHarmonized ? "_HARMONIZED" : "") : + "PRIV_MANAGED_" + studyIdentifier + (isHarmonized ? "_HARMONIZED" : ""); + + // Check if the Privilege already exists + Privilege priv = this.findByName(privilegeName); + if (priv != null) { + logger.info("{} already exists", privilegeName); + return priv; + } + + priv = new Privilege(); + try { + priv.setApplication(picSureApp); + priv.setName(privilegeName); + + // Set consent concept path + String consent_concept_path = isHarmonized ? fence_harmonized_consent_group_concept_path : fence_parent_consent_group_concept_path; + if (!consent_concept_path.contains("\\\\")) { + consent_concept_path = consent_concept_path.replaceAll("\\\\", "\\\\\\\\"); + logger.debug("Escaped consent concept path: {}", consent_concept_path); + } + + if (fence_harmonized_concept_path != null && !fence_harmonized_concept_path.contains("\\\\")) { + //these have to be escaped again so that jaxson can convert it correctly + fence_harmonized_concept_path = fence_harmonized_concept_path.replaceAll("\\\\", "\\\\\\\\"); + logger.debug("upsertTopmedPrivilege(): escaped harmonized consent path" + fence_harmonized_concept_path); + } + + + String studyIdentifierField = (consent_group != null && !consent_group.isEmpty()) ? studyIdentifier + "." + consent_group : studyIdentifier; + String queryTemplateText = String.format( + "{\"categoryFilters\": {\"%s\":[\"%s\"]},\"numericFilters\":{},\"requiredFields\":[],\"fields\":[],\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}],\"expectedResultType\": \"COUNT\"}", + consent_concept_path, studyIdentifierField + ); + + priv.setQueryTemplate(queryTemplateText); + priv.setQueryScope(isHarmonized ? String.format("[\"%s\",\"_\",\"%s\"]", conceptPath, fence_harmonized_concept_path) : String.format("[\"%s\",\"_\"]", conceptPath)); + + // Initialize the set of AccessRules + Set accessrules = new HashSet<>(); + + // Create and add the parent consent access rule + AccessRule ar = this.accessRuleService.createConsentAccessRule(studyIdentifier, consent_group, "PARENT", fence_parent_consent_group_concept_path); + this.accessRuleService.configureAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias, true, false, false); + accessrules.add(ar); + + // Create and add the Topmed+Parent access rule + ar = this.accessRuleService.upsertTopmedAccessRule(studyIdentifier, consent_group, "TOPMED+PARENT"); + this.accessRuleService.configureAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias, true, false, true); + accessrules.add(ar); + + // If harmonized, create and add the harmonized access rule + if (isHarmonized) { + ar = this.accessRuleService.createConsentAccessRule(studyIdentifier, consent_group, "HARMONIZED", fence_harmonized_consent_group_concept_path); + this.accessRuleService.configureHarmonizedAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias); + accessrules.add(ar); + } + + // Add standard access rules + this.accessRuleService.addStandardAccessRules(accessrules); + + priv.setAccessRules(accessrules); + logger.info("Added {} access_rules to privilege", accessrules.size()); + + this.save(priv); + logger.info("Added new privilege {} to DB", priv.getName()); + } catch (Exception ex) { + logger.error("Could not save privilege", ex); + } + return priv; + } + + /** + * Creates a privilege for Topmed access. This has (up to) three access rules: + * 1) topmed only 2) topmed + parent 3) topmed + harmonized. + * + * @param studyIdentifier + * @param projectAlias + * @param consentGroup + * @param parentConceptPath + * @param isHarmonized + * @return Privilege + */ + private Privilege upsertTopmedPrivilege(String studyIdentifier, String projectAlias, String consentGroup, String parentConceptPath, boolean isHarmonized) { + String privilegeName = "PRIV_MANAGED_" + studyIdentifier + "_" + consentGroup + "_TOPMED"; + Privilege priv = this.findByName(privilegeName); + + if (priv != null) { + logger.info("upsertTopmedPrivilege() {} already exists", privilegeName); + return priv; + } + + priv = new Privilege(); + + try { + buildPrivilegeObject(priv, privilegeName, studyIdentifier, consentGroup); + + Set accessRules = new HashSet<>(); + AccessRule topmedRule = this.accessRuleService.upsertTopmedAccessRule(studyIdentifier, consentGroup, "TOPMED"); + + this.accessRuleService.populateAccessRule(topmedRule, false, false, true); + topmedRule.getSubAccessRule().addAll(this.accessRuleService.getPhenotypeRestrictedSubRules(studyIdentifier, consentGroup, projectAlias)); + accessRules.add(topmedRule); + + if (parentConceptPath != null) { + AccessRule topmedParentRule = this.accessRuleService.upsertTopmedAccessRule(studyIdentifier, consentGroup, "TOPMED+PARENT"); + this.accessRuleService.populateAccessRule(topmedParentRule, true, false, true); + topmedParentRule.getSubAccessRule().addAll(this.accessRuleService.getPhenotypeSubRules(studyIdentifier, parentConceptPath, projectAlias)); + accessRules.add(topmedParentRule); + + if (isHarmonized) { + AccessRule harmonizedRule = this.accessRuleService.upsertHarmonizedAccessRule(studyIdentifier, consentGroup, "HARMONIZED"); + this.accessRuleService.populateHarmonizedAccessRule(harmonizedRule, parentConceptPath, studyIdentifier, projectAlias); + accessRules.add(harmonizedRule); + } + } + + this.accessRuleService.addStandardAccessRules(accessRules); + + priv.setAccessRules(accessRules); + logger.info("upsertTopmedPrivilege() Added {} access_rules to privilege", accessRules.size()); + + this.save(priv); + logger.info("upsertTopmedPrivilege() Added new privilege {} to DB", priv.getName()); + } catch (Exception ex) { + logger.error("upsertTopmedPrivilege() could not save privilege", ex); + } + + return priv; + } + + private void buildPrivilegeObject(Privilege priv, String privilegeName, String studyIdentifier, String consentGroup) { + priv.setApplication(picSureApp); + priv.setName(privilegeName); + priv.setDescription("MANAGED privilege for Topmed " + studyIdentifier + "." + consentGroup); + + String consentConceptPath = escapePath(fence_topmed_consent_group_concept_path); + fence_harmonized_concept_path = escapePath(fence_harmonized_concept_path); + + String queryTemplateText = "{\"categoryFilters\": {\"" + consentConceptPath + "\":[\"" + studyIdentifier + "." + consentGroup + "\"]}," + + "\"numericFilters\":{},\"requiredFields\":[]," + + "\"fields\":[\"" + topmedAccessionField + "\"]," + + "\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}]," + + "\"expectedResultType\": \"COUNT\"" + + "}"; + + priv.setQueryTemplate(queryTemplateText); + + priv.setQueryScope(buildQueryScope(this.variantAnnotationColumns)); + } + + private String escapePath(String path) { + if (path != null && !path.contains("\\\\")) { + return path.replaceAll("\\\\", "\\\\\\\\"); + } + return path; + } + + private String buildQueryScope(String variantColumns) { + if (variantColumns == null || variantColumns.isEmpty()) { + return "[\"_\"]"; + } + + return Arrays.stream(variantColumns.split(",")) + .map(path -> "\"" + path + "\"") + .collect(Collectors.joining(",", "[", ",\"_\"]")); + } + + private String extractProject(String roleName) { + String projectPattern = "MANAGED_(.*?)(?:_c\\d+)?$"; + if (roleName.startsWith("MANUAL_")) { + projectPattern = "MANUAL_(.*?)(?:_c\\d+)?$"; + } + Pattern projectRegex = Pattern.compile(projectPattern); + Matcher projectMatcher = projectRegex.matcher(roleName); + String project = ""; + if (projectMatcher.find()) { + project = projectMatcher.group(1).trim(); + } else { + logger.info("extractProject() Could not extract project from role name: {}", roleName); + String[] parts = roleName.split("_", 1); + if (parts.length > 0) { + project = parts[1]; + } + } + return project; + } + + private static String extractConsentGroup(String roleName) { + String consentPattern = "MANAGED_.*?_c(\\d+)$"; + if (roleName.startsWith("MANUAL_")) { + consentPattern = "MANUAL_.*?_c(\\d+)$"; + } + Pattern consentRegex = Pattern.compile(consentPattern); + Matcher consentMatcher = consentRegex.matcher(roleName); + String consentGroup = ""; + if (consentMatcher.find()) { + consentGroup = "c" + consentMatcher.group(1).trim(); + } + return consentGroup; + } + + private StudyMetaData getFENCEMappingforProjectAndConsent(String projectId, String consent_group) { + String consentVal = (consent_group != null && !consent_group.isEmpty()) ? projectId + "." + consent_group : projectId; + logger.info("getFENCEMappingforProjectAndConsent() looking up {}", consentVal); + + return this.fenceMappingUtility.getFENCEMapping().get(consentVal); + } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RoleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RoleService.java index 69d8d5453..8aced3c4f 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RoleService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/RoleService.java @@ -2,10 +2,13 @@ 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.enums.SecurityRoles; import edu.harvard.hms.dbmi.avillach.auth.model.CustomUserDetails; import edu.harvard.hms.dbmi.avillach.auth.repository.PrivilegeRepository; import edu.harvard.hms.dbmi.avillach.auth.repository.RoleRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; @@ -13,21 +16,22 @@ import org.springframework.transaction.annotation.Transactional; import java.util.*; -import java.util.logging.Logger; import java.util.stream.Collectors; @Service public class RoleService { - private final static Logger logger = Logger.getLogger(RoleService.class.getName()); + private final Logger logger = LoggerFactory.getLogger(RoleService.class); private final RoleRepository roleRepository; private final PrivilegeRepository privilegeRepo; + private final PrivilegeService privilegeService; @Autowired - protected RoleService(RoleRepository roleRepository, PrivilegeRepository privilegeRepo) { + protected RoleService(RoleRepository roleRepository, PrivilegeRepository privilegeRepo, PrivilegeService privilegeService) { this.roleRepository = roleRepository; this.privilegeRepo = privilegeRepo; + this.privilegeService = privilegeService; } public Optional getRoleById(String roleId) { @@ -124,12 +128,85 @@ public List persistAll(List newRoles) { return this.roleRepository.saveAll(newRoles); } - public void persistAll(Set newRoles) { - this.roleRepository.saveAll(newRoles); + public List persistAll(Set newRoles) { + return this.roleRepository.saveAll(newRoles); } public Role findByName(String roleName) { return this.roleRepository.findByName(roleName); } + + public Role createRole(String roleName, String roleDescription) { + if (roleName.isEmpty()) { + logger.info("createRole() roleName is empty"); + return null; + } + logger.info("getFENCEProfile() New PSAMA role name:{}", roleName); + Role r; + // Create the Role in the repository, if it does not exist. Otherwise, add it. + Role existing_role = findByName(roleName); + if (existing_role != null) { + // Role already exists + logger.info("upsertRole() role already exists"); + r = existing_role; + } else { + // This is a new Role + r = new Role(); + r.setName(roleName); + r.setDescription(roleDescription); + // Since this is a new Role, we need to ensure that the + // corresponding Privilege (with gates) and AccessRule is added. + r.setPrivileges(privilegeService.addPrivileges(r)); + logger.info("upsertRole() created new role"); + } + + return r; + } + + /** + * Insert or Update the User object's list of Roles in the database. + * + * @param u The User object the generated Role will be added to + * @param roleName Name of the Role + * @param roleDescription Description of the Role + * @return boolean Whether the Role was successfully added to the User or not + */ + public boolean upsertRole(User u, String roleName, String roleDescription) { + boolean status = false; + + // 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 + // be updated for all subsequent calls. + try { + Role r = null; + // Create the Role in the Servicesitory, if it does not exist. Otherwise, add it. + Role existing_role = this.getRoleByName(roleName); + if (existing_role != null) { + // Role already exists + logger.info("upsertRole() role already exists"); + r = existing_role; + } else { + // This is a new Role + r = new Role(); + r.setName(roleName); + r.setDescription(roleDescription); + // Since this is a new Role, we need to ensure that the + // corresponding Privilege (with gates) and AccessRule is added. + r.setPrivileges(privilegeService.addPrivileges(r)); + this.save(r); + logger.info("upsertRole() created new role"); + } + if (u != null) { + u.getRoles().add(r); + } + status = true; + } catch (Exception ex) { + logger.error("upsertRole() Could not inser/update role {} to Service", roleName, ex); + } + + + logger.debug("upsertRole() finished"); + return status; + } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessService.java index b0453820f..0c03bed7d 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessService.java @@ -1,7 +1,6 @@ package edu.harvard.hms.dbmi.avillach.auth.service.impl; import edu.harvard.hms.dbmi.avillach.auth.model.fenceMapping.StudyMetaData; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication.FENCEAuthenticationService; import edu.harvard.hms.dbmi.avillach.auth.utils.FenceMappingUtility; import io.swagger.v3.oas.annotations.Parameter; import org.apache.commons.lang3.StringUtils; @@ -14,19 +13,17 @@ @Service public class StudyAccessService { + private final Logger logger = LoggerFactory.getLogger(StudyAccessService.class); + private final FenceMappingUtility fenceMappingUtility; - Logger logger = LoggerFactory.getLogger(StudyAccessService.class); + private final RoleService roleService; public static final String MANUAL = "MANUAL_"; - public static final String STUDY_IDENTIFIER = "study_identifier"; - public static final String CONSENT_GROUP_CODE = "consent_group_code"; - - private final FENCEAuthenticationService fenceAuthenticationService; @Autowired - public StudyAccessService(FENCEAuthenticationService fenceAuthenticationService, FenceMappingUtility fenceMappingUtility) { - this.fenceAuthenticationService = fenceAuthenticationService; + public StudyAccessService(FenceMappingUtility fenceMappingUtility, RoleService roleService) { this.fenceMappingUtility = fenceMappingUtility; + this.roleService = roleService; } public String addStudyAccess(@Parameter(description="The Study Identifier of the new study from the metadata.json") @@ -58,7 +55,7 @@ public String addStudyAccess(@Parameter(description="The Study Identifier of the 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)) { + if (roleService.upsertRole(null, newRoleName, MANUAL + " role "+newRoleName)) { logger.info("addStudyAccess - Updated user role. Now it includes `{}`", newRoleName); return "Role '" + newRoleName + "' successfully created"; } else { 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 25fdcc837..e8e9b504e 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 @@ -404,7 +404,7 @@ public Map getDefaultQueryTemplate() { return Map.of("queryTemplate", mergedTemplate.orElse(null)); } - @Cacheable(value = "mergedTemplateCache", key = "#user.getEmail()") + @Cacheable(value = "mergedTemplateCache", keyGenerator = "customKeyGenerator") public String mergeTemplate(User user, Application application) { String resultJSON; Map mergedTemplateMap = null; @@ -444,9 +444,13 @@ public String mergeTemplate(User user, Application application) { return resultJSON; } - @CacheEvict(value = "mergedTemplateCache", key = "#user.getEmail()") - public void evictFromCache(User user) { - logger.info("evictMergedTemplate() evicting cache for user: {}", user.getUuid()); + @CacheEvict(value = "mergedTemplateCache") + public void evictFromCache(String email) { + if (email == null || email.isEmpty()) { + logger.warn("evictFromCache() was called with a null or empty email"); + return; + } + logger.info("evictFromCache() evicting cache for user: {}", email); } @Transactional 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 d6f56b745..0d7d2a330 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 @@ -7,6 +7,7 @@ import edu.harvard.hms.dbmi.avillach.auth.entity.Connection; import edu.harvard.hms.dbmi.avillach.auth.exceptions.NotAuthorizedException; 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.OauthUserMatchingService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.UserService; @@ -36,20 +37,19 @@ */ @Service -public class Auth0AuthenticationService { +public class Auth0AuthenticationService implements AuthenticationService { private final Logger logger = LoggerFactory.getLogger(Auth0AuthenticationService.class); private final OauthUserMatchingService matchingService; - private final UserRepository userRepository; - private final BasicMailService basicMailService; - private final UserService userService; + private static final int AUTH_RETRY_LIMIT = 3; + private final boolean isAuth0Enabled; - private String deniedEmailEnabled; + private boolean deniedEmailEnabled; private final String auth0host; @@ -58,8 +58,16 @@ public class Auth0AuthenticationService { private final RestClientUtil restClientUtil; @Autowired - public Auth0AuthenticationService(OauthUserMatchingService matchingService, UserRepository userRepository, BasicMailService basicMailService, UserService userService, - @Value("${application.denied.email.enabled}") String deniedEmailEnabled, @Value("${application.auth0.host}") String auth0host, ConnectionRepository connectionRepository, RestClientUtil restClientUtil) { + public Auth0AuthenticationService(OauthUserMatchingService matchingService, + UserRepository userRepository, + BasicMailService basicMailService, + UserService userService, + ConnectionRepository connectionRepository, + RestClientUtil restClientUtil, + @Value("${auth0.idp.provider.is.enabled}") boolean isAuth0Enabled, + @Value("${auth0.denied.email.enabled}") boolean deniedEmailEnabled, + @Value("${auth0.host}") String auth0host + ) { this.matchingService = matchingService; this.userRepository = userRepository; this.basicMailService = basicMailService; @@ -68,9 +76,11 @@ public Auth0AuthenticationService(OauthUserMatchingService matchingService, User this.auth0host = auth0host; this.connectionRepository = connectionRepository; this.restClientUtil = restClientUtil; + this.isAuth0Enabled = isAuth0Enabled; } - public HashMap getToken(Map authRequest) throws IOException { + @Override + public HashMap authenticate(Map authRequest, String requestHost) throws IOException { String accessToken = authRequest.get("access_token"); String redirectURI = authRequest.get("redirectURI"); @@ -102,7 +112,7 @@ public HashMap getToken(Map authRequest) throws //Try to match user = matchingService.matchTokenToUser(userInfo); if (user == null) { - if (this.deniedEmailEnabled.startsWith("true")) { + if (this.deniedEmailEnabled) { try { basicMailService.sendDeniedAccessEmail(userInfo); } catch (jakarta.mail.MessagingException e) { @@ -123,6 +133,16 @@ public HashMap getToken(Map authRequest) throws return responseMap; } + @Override + public String getProvider() { + return "auth0"; + } + + @Override + public boolean isEnabled() { + return this.isAuth0Enabled; + } + public JsonNode retrieveUserInfo(String accessToken) throws IOException { String auth0UserInfoURI = this.auth0host + "/userinfo"; HttpHeaders headers = new HttpHeaders(); @@ -136,7 +156,7 @@ public JsonNode retrieveUserInfo(String accessToken) throws IOException { ResponseEntity response = this.restClientUtil.retrieveGetResponseWithRequestConfiguration( auth0UserInfoURI, headers, - this.createRequestConfigWithCustomTimeout(2000) + RestClientUtil.createRequestConfigWithCustomTimeout(2000) ); auth0Response = objectMapper.readTree(response.getBody()); @@ -152,14 +172,7 @@ public JsonNode retrieveUserInfo(String accessToken) throws IOException { return auth0Response; } - public ClientHttpRequestFactory createRequestConfigWithCustomTimeout(int timeoutMs) { - SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory(); - requestFactory.setConnectTimeout(timeoutMs); - requestFactory.setReadTimeout(timeoutMs); - return requestFactory; - } - - public void setDeniedEmailEnabled(String deniedEmailEnabled) { + public void setDeniedEmailEnabled(boolean deniedEmailEnabled) { this.deniedEmailEnabled = deniedEmailEnabled; } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceRegistry.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceRegistry.java new file mode 100644 index 000000000..f744a5d07 --- /dev/null +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/AuthenticationServiceRegistry.java @@ -0,0 +1,42 @@ +package edu.harvard.hms.dbmi.avillach.auth.service.impl.authentication; + +import edu.harvard.hms.dbmi.avillach.auth.service.AuthenticationService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component +public class AuthenticationServiceRegistry { + + private final Logger logger = LoggerFactory.getLogger(AuthenticationServiceRegistry.class); + + private final Map authenticationServices = new HashMap<>(); + + @Autowired + public AuthenticationServiceRegistry(List authenticationServices) { + authenticationServices.forEach(authenticationService -> { + logger.info("Registering authentication service: {}", authenticationService.getProvider()); + if (authenticationService.isEnabled()) { + this.authenticationServices.put(authenticationService.getProvider(), authenticationService); + logger.info("Registered authentication service: {}", authenticationService.getProvider()); + } else { + logger.info("Skipping registration of disabled authentication service: {}", authenticationService.getProvider()); + } + }); + } + + public AuthenticationService getAuthenticationService(String provider) { + // Check if the provider is registered by the provider name + if (!authenticationServices.containsKey(provider)) { + throw new IllegalArgumentException("No authentication service found for provider: " + provider); + } + + return authenticationServices.get(provider); + } + +} 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 0dffb59b9..690c51b85 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,16 +4,14 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.node.ObjectNode; -import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; -import edu.harvard.hms.dbmi.avillach.auth.entity.Application; 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.exceptions.NotAuthorizedException; import edu.harvard.hms.dbmi.avillach.auth.model.fenceMapping.StudyMetaData; +import edu.harvard.hms.dbmi.avillach.auth.service.AuthenticationService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.*; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import edu.harvard.hms.dbmi.avillach.auth.utils.FenceMappingUtility; import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; import org.apache.commons.lang3.StringUtils; @@ -32,129 +30,65 @@ import org.springframework.util.MultiValueMap; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import java.util.stream.Collectors; @Service -public class FENCEAuthenticationService { +public class FENCEAuthenticationService implements AuthenticationService { private final Logger logger = LoggerFactory.getLogger(FENCEAuthenticationService.class); private final UserService userService; private final RoleService roleService; private final ConnectionWebService connectionService; // We will need to investigate if the ConnectionWebService will need to be versioned as well. - private final ApplicationService applicationService; - private final PrivilegeService privilegeService; private final AccessRuleService accessRuleService; private final FenceMappingUtility fenceMappingUtility; - private Application picSureApp; private Connection fenceConnection; private final Set openAccessIdpValues = Set.of("fence", "ras"); + private final boolean isFenceEnabled; private final String idp_provider_uri; private final String fence_client_id; private final String fence_client_secret; - private final String idp_provider; - private final String fence_standard_access_rules; - private final String fence_allowed_query_types; - private final String variantAnnotationColumns; - private final String templatePath; - private final String fence_harmonized_consent_group_concept_path; - private final String fence_parent_consent_group_concept_path; - private final String fence_topmed_consent_group_concept_path; - private String fence_harmonized_concept_path; - private static final String parentAccessionField = "\\\\_Parent Study Accession with Subject ID\\\\"; - private static final String topmedAccessionField = "\\\\_Topmed Study Accession with Subject ID\\\\"; - public static final String fence_open_access_role_name = "FENCE_ROLE_OPEN_ACCESS"; - - private String[] underscoreFields; + public static final String fence_open_access_role_name = "MANAGED_ROLE_OPEN_ACCESS"; private final RestClientUtil restClientUtil; - private final ConcurrentHashMap accessRuleCache = new ConcurrentHashMap<>(); - private Set allowQueryTypeRules; - @Autowired public FENCEAuthenticationService(UserService userService, RoleService roleService, ConnectionWebService connectionService, - ApplicationService applicationService, - PrivilegeService privilegeService, RestClientUtil restClientUtil, - @Value("${application.idp.provider.uri}") String idpProviderUri, + @Value("${fence.idp.provider.is.enabled}") boolean isFenceEnabled, + @Value("${fence.idp.provider.uri}") String idpProviderUri, @Value("${fence.client.id}") String fenceClientId, @Value("${fence.client.secret}") String fenceClientSecret, - @Value("${application.idp.provider}") String idpProvider, - @Value("${fence.standard.access.rules}") String fenceStandardAccessRules, - @Value("${fence.allowed.query.types}") String fenceAllowedQueryTypes, - @Value("${fence.variant.annotation.columns}") String variantAnnotationColumns, - @Value("${application.template.path}") String templatePath, - @Value("${fence.harmonized.consent.group.concept.path}") String fenceHarmonizedConsentGroupConceptPath, - @Value("${fence.parent.consent.group.concept.path}") String fenceParentConceptPath, - @Value("${fence.topmed.consent.group.concept.path}") String fenceTopmedConceptPath, - @Value("${fence.consent.group.concept.path}") String fenceHarmonizedConceptPath, - AccessRuleService accessRuleService, FenceMappingUtility fenceMappingUtility) { + AccessRuleService accessRuleService, + FenceMappingUtility fenceMappingUtility) { this.userService = userService; this.roleService = roleService; this.connectionService = connectionService; - this.applicationService = applicationService; - this.privilegeService = privilegeService; this.idp_provider_uri = idpProviderUri; this.fence_client_id = fenceClientId; this.fence_client_secret = fenceClientSecret; - this.idp_provider = idpProvider; - this.fence_standard_access_rules = fenceStandardAccessRules; - this.fence_allowed_query_types = fenceAllowedQueryTypes; - this.variantAnnotationColumns = variantAnnotationColumns; - this.templatePath = templatePath; this.restClientUtil = restClientUtil; this.accessRuleService = accessRuleService; - this.fence_parent_consent_group_concept_path = fenceParentConceptPath; - this.fence_topmed_consent_group_concept_path = fenceTopmedConceptPath; - this.fence_harmonized_concept_path = fenceHarmonizedConceptPath; - this.fence_harmonized_consent_group_concept_path = fenceHarmonizedConsentGroupConceptPath; this.fenceMappingUtility = fenceMappingUtility; + this.isFenceEnabled = isFenceEnabled; } @EventListener(ContextRefreshedEvent.class) public void initializeFenceService() { - picSureApp = applicationService.getApplicationByName("PICSURE"); fenceConnection = connectionService.getConnectionByLabel("FENCE"); - // We need to set the underscoreFields here so that we can use them in the access rules during PostConstruct - // If we don't set them here, we will get a NullPointerException when we try to use them in the access rules - underscoreFields = new String[]{ - parentAccessionField, - topmedAccessionField, - fence_harmonized_consent_group_concept_path, - fence_parent_consent_group_concept_path, - fence_topmed_consent_group_concept_path, - "\\\\_VCF Sample Id\\\\", - "\\\\_studies\\\\", - "\\\\_studies_consents\\\\", //used to provide consent-level counts for open access - "\\\\_parent_consents\\\\", //parent consents not used for auth (use combined _consents) - "\\\\_Consents\\\\" ///old _Consents\Short Study... path no longer used, but still present in examples. - }; - // log all the properties + logger.info("isFenceEnabled: {}", isFenceEnabled); logger.info("idp_provider_uri: {}", idp_provider_uri); - logger.info("idp_provider: {}", idp_provider); - logger.info("fence_standard_access_rules: {}", fence_standard_access_rules); - logger.info("fence_allowed_query_types: {}", fence_allowed_query_types); - logger.info("variantAnnotationColumns: {}", variantAnnotationColumns); - logger.info("templatePath: {}", templatePath); - logger.info("fence_harmonized_consent_group_concept_path: {}", fence_harmonized_consent_group_concept_path); - logger.info("fence_parent_consent_group_concept_path: {}", fence_parent_consent_group_concept_path); - logger.info("fence_topmed_consent_group_concept_path: {}", fence_topmed_consent_group_concept_path); - logger.info("fence_harmonized_concept_path: {}", fence_harmonized_concept_path); - logger.info("underscoreFields: {}", Arrays.toString(underscoreFields)); - if (!fenceMappingUtility.getFENCEMapping().isEmpty() && !fenceMappingUtility.getFenceMappingByAuthZ().isEmpty()) { + if (fenceMappingUtility.getFENCEMapping() != null && fenceMappingUtility.getFenceMappingByAuthZ() != null + && !fenceMappingUtility.getFENCEMapping().isEmpty() && !fenceMappingUtility.getFenceMappingByAuthZ().isEmpty()) { // Create all potential access rules using the fence mapping Set roles = fenceMappingUtility.getFenceMappingByAuthZ().values().parallelStream().map(projectMetadata -> { if (projectMetadata == null) { @@ -174,9 +108,9 @@ public void initializeFenceService() { String projectId = projectMetadata.getStudyIdentifier(); String consentCode = projectMetadata.getConsentGroupCode(); - String newRoleName = StringUtils.isNotBlank(consentCode) ? "FENCE_" + projectId + "_" + consentCode : "FENCE_" + projectId; + String newRoleName = StringUtils.isNotBlank(consentCode) ? "MANAGED_" + projectId + "_" + consentCode : "MANAGED_" + projectId; - return createRole(newRoleName, "FENCE role " + newRoleName); + return this.roleService.createRole(newRoleName, "MANAGED role " + newRoleName); }).filter(Objects::nonNull).collect(Collectors.toSet()); roleService.persistAll(roles); @@ -185,7 +119,10 @@ public void initializeFenceService() { } } - public HashMap getFENCEProfile(String callback_url, Map authRequest) { + @Override + public HashMap authenticate(Map authRequest, String host) { + String callBackUrl = "https://" + host + "/psamaui/login/"; + logger.debug("getFENCEProfile() starting..."); String fence_code = authRequest.get("code"); @@ -199,8 +136,8 @@ public HashMap getFENCEProfile(String callback_url, Map getFENCEProfile(String callback_url, Map getFENCEProfile(String callback_url, Map "PIC-SURE Top Admin".equals(userRole.getName()) || "Admin".equals(userRole.getName()) || userRole.getName().startsWith("MANUAL_") Set rolesToRemove = current_user.getRoles().parallelStream() @@ -266,7 +210,7 @@ public HashMap getFENCEProfile(String callback_url, Map newRoles = roleNames.parallelStream() - .map(roleName -> createRole(roleName, "FENCE role " + roleName)) + .map(roleName -> this.roleService.createRole(roleName, "MANAGED role " + roleName)) .filter(Objects::nonNull) .collect(Collectors.toList()); @@ -303,31 +247,14 @@ public HashMap getFENCEProfile(String callback_url, Map fence_user_profile_response = this.restClientUtil.retrieveGetResponse( + ResponseEntity fence_user_profile_response = this.restClientUtil.retrieveGetResponseWithRequestConfiguration( this.idp_provider_uri + "/user/user", - headers + headers, + RestClientUtil.createRequestConfigWithCustomTimeout(10000) ); // Map the response to a JsonNode object @@ -428,774 +356,4 @@ private User createUserFromFENCEProfile(JsonNode node) { return actual_user; } - /** - * Insert or Update the User object's list of Roles in the database. - * - * @param u The User object the generated Role will be added to - * @param roleName Name of the Role - * @param roleDescription Description of the Role - * @return boolean Whether the Role was successfully added to the User or not - */ - public boolean upsertRole(User u, String roleName, String roleDescription) { - boolean status = false; - - // 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 - // be updated for all subsequent calls. - try { - Role r = null; - // Create the Role in the Servicesitory, if it does not exist. Otherwise, add it. - Role existing_role = roleService.getRoleByName(roleName); - if (existing_role != null) { - // Role already exists - logger.info("upsertRole() role already exists"); - r = existing_role; - } else { - // This is a new Role - r = new Role(); - r.setName(roleName); - r.setDescription(roleDescription); - // Since this is a new Role, we need to ensure that the - // corresponding Privilege (with gates) and AccessRule is added. - r.setPrivileges(addFENCEPrivileges(r)); - roleService.save(r); - logger.info("upsertRole() created new role"); - } - if (u != null) { - u.getRoles().add(r); - } - status = true; - } catch (Exception ex) { - logger.error("upsertRole() Could not inser/update role {} to Service", roleName, ex); - } - - - logger.debug("upsertRole() finished"); - return status; - } - - private Set addFENCEPrivileges(Role r) { - String roleName = r.getName(); - logger.info("addFENCEPrivileges() starting, adding privilege(s) to role {}", roleName); - - //each project can have up to three privileges: Parent | Harmonized | Topmed - //harmonized has 2 ARs for parent + harminized and harmonized only - //Topmed has up to three ARs for topmed / topmed + parent / topmed + harmonized - Set privs = r.getPrivileges(); - if (privs == null) { - privs = new HashSet(); - } - - //e.g. FENCE_phs0000xx_c2 or FENCE_tutorial-biolinc_camp - String project_name = extractProject(roleName); - if (project_name.length() <= 0) { - logger.warn("addFENCEPrivileges() role name: {} returned an empty project name", roleName); - } - String consent_group = extractConsentGroup(roleName); - if (consent_group.length() <= 0) { - logger.warn("addFENCEPrivileges() role name: {} returned an empty consent group", roleName); - } - logger.info("addFENCEPrivileges() project name: {} consent group: {}", project_name, consent_group); - - // Look up the metadata by consent group. - StudyMetaData projectMetadata = getFENCEMappingforProjectAndConsent(project_name, consent_group); - - if (projectMetadata == null) { - //no privileges means no access to this project. just return existing set of privs. - logger.warn("No metadata available for project {}.{}", project_name, consent_group); - return privs; - } - - logger.info("addPrivileges() This is a new privilege"); - - String dataType = projectMetadata.getDataType(); - Boolean isHarmonized = projectMetadata.getIsHarmonized(); - String concept_path = projectMetadata.getTopLevelPath(); - String projectAlias = projectMetadata.getAbbreviatedName(); - - // we need to add escape sequence back in to the path for parsing later (also need to double escape the regex) - // we need to do this for the query Template and scopes, but should NOT do this for the rules. - if (concept_path != null) { - concept_path = concept_path.replaceAll("\\\\", "\\\\\\\\"); - } - - if (dataType != null && dataType.contains("G")) { - //insert genomic/topmed privs - this will also add rules for including harmonized & parent data if applicable - privs.add(upsertTopmedPrivilege(project_name, projectAlias, consent_group, concept_path, isHarmonized)); - } - - if (dataType != null && dataType.contains("P")) { - //insert clinical privs - logger.info("addPrivileges() project:{} consent_group:{} concept_path:{}", project_name, consent_group, concept_path); - privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, false)); - - //if harmonized study, also create harmonized privileges - if (Boolean.TRUE.equals(isHarmonized)) { - privs.add(upsertClinicalPrivilege(project_name, projectAlias, consent_group, concept_path, true)); - } - } - - //projects without G or P in data_type are skipped - if (dataType == null || (!dataType.contains("P") && !dataType.contains("G"))) { - logger.warn("Missing study type for {} {}. Skipping.", project_name, consent_group); - } - - logger.info("addPrivileges() Finished"); - return privs; - } - - /** - * Creates a privilege with a set of access rules that allow queries containing a consent group to pass if the query only contains valid entries that match conceptPath. If the study is harmonized, - * this also creates an access rule to allow access when using the harmonized consent concept path. - * Privileges created with this method will deny access if any genomic filters (topmed data) are included. - * - * @param studyIdentifier The study identifier - * @param consent_group The consent group - * @param conceptPath The concept path - * @param isHarmonized Whether the study is harmonized - * @return The created privilege - */ - private Privilege upsertClinicalPrivilege(String studyIdentifier, String projectAlias, String consent_group, String conceptPath, boolean isHarmonized) { - // Construct the privilege name - String privilegeName = (consent_group != null && !consent_group.isEmpty()) ? - "PRIV_FENCE_" + studyIdentifier + "_" + consent_group + (isHarmonized ? "_HARMONIZED" : "") : - "PRIV_FENCE_" + studyIdentifier + (isHarmonized ? "_HARMONIZED" : ""); - - // Check if the Privilege already exists - Privilege priv = privilegeService.findByName(privilegeName); - if (priv != null) { - logger.info("{} already exists", privilegeName); - return priv; - } - - priv = new Privilege(); - try { - priv.setApplication(picSureApp); - priv.setName(privilegeName); - - // Set consent concept path - String consent_concept_path = isHarmonized ? fence_harmonized_consent_group_concept_path : fence_parent_consent_group_concept_path; - if (!consent_concept_path.contains("\\\\")) { - consent_concept_path = consent_concept_path.replaceAll("\\\\", "\\\\\\\\"); - logger.debug("Escaped consent concept path: {}", consent_concept_path); - } - - if (fence_harmonized_concept_path != null && !fence_harmonized_concept_path.contains("\\\\")) { - //these have to be escaped again so that jaxson can convert it correctly - fence_harmonized_concept_path = fence_harmonized_concept_path.replaceAll("\\\\", "\\\\\\\\"); - logger.debug("upsertTopmedPrivilege(): escaped harmonized consent path" + fence_harmonized_concept_path); - } - - - String studyIdentifierField = (consent_group != null && !consent_group.isEmpty()) ? studyIdentifier + "." + consent_group : studyIdentifier; - String queryTemplateText = String.format( - "{\"categoryFilters\": {\"%s\":[\"%s\"]},\"numericFilters\":{},\"requiredFields\":[],\"fields\":[],\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}],\"expectedResultType\": \"COUNT\"}", - consent_concept_path, studyIdentifierField - ); - - priv.setQueryTemplate(queryTemplateText); - priv.setQueryScope(isHarmonized ? String.format("[\"%s\",\"_\",\"%s\"]", conceptPath, fence_harmonized_concept_path) : String.format("[\"%s\",\"_\"]", conceptPath)); - - // Initialize the set of AccessRules - Set accessrules = new HashSet<>(); - - // Create and add the parent consent access rule - AccessRule ar = createConsentAccessRule(studyIdentifier, consent_group, "PARENT", fence_parent_consent_group_concept_path); - configureAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias, true, false, false); - accessrules.add(ar); - - // Create and add the Topmed+Parent access rule - ar = upsertTopmedAccessRule(studyIdentifier, consent_group, "TOPMED+PARENT"); - configureAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias, true, false, true); - accessrules.add(ar); - - // If harmonized, create and add the harmonized access rule - if (isHarmonized) { - ar = createConsentAccessRule(studyIdentifier, consent_group, "HARMONIZED", fence_harmonized_consent_group_concept_path); - configureHarmonizedAccessRule(ar, studyIdentifier, consent_group, conceptPath, projectAlias); - accessrules.add(ar); - } - - // Add standard access rules - addStandardAccessRules(accessrules); - - priv.setAccessRules(accessrules); - logger.info("Added {} access_rules to privilege", accessrules.size()); - - privilegeService.save(priv); - logger.info("Added new privilege {} to DB", priv.getName()); - } catch (Exception ex) { - logger.error("Could not save privilege", ex); - } - return priv; - } - - /** - * Configures the AccessRule with gates and sub-rules. - * - * @param ar The AccessRule to configure. - * @param studyIdentifier The study identifier. - * @param consent_group The consent group. - * @param conceptPath The concept path. - * @param projectAlias The project alias. - * @param parent Whether to include parent gates. - * @param harmonized Whether to include harmonized gates. - * @param topmed Whether to include Topmed gates. - */ - private void configureAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias, boolean parent, boolean harmonized, boolean topmed) { - if (ar.getGates() == null) { - ar.setGates(new HashSet<>()); - ar.getGates().addAll(getGates(parent, harmonized, topmed)); - - if (ar.getSubAccessRule() == null) { - ar.setSubAccessRule(new HashSet<>()); - } - ar.getSubAccessRule().addAll(getAllowedQueryTypeRules()); - ar.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, conceptPath, projectAlias)); - ar.getSubAccessRule().addAll(getTopmedRestrictedSubRules()); - accessRuleService.save(ar); - } - } - - /** - * Configures the harmonized AccessRule with gates and sub-rules. - * - * @param ar The AccessRule to configure. - * @param studyIdentifier The study identifier. - * @param consent_group The consent group. - * @param conceptPath The concept path. - * @param projectAlias The project alias. - */ - private void configureHarmonizedAccessRule(AccessRule ar, String studyIdentifier, String consent_group, String conceptPath, String projectAlias) { - if (ar.getGates() == null) { - ar.setGates(new HashSet<>()); - ar.getGates().add(upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", true, "harmonized data")); - - if (ar.getSubAccessRule() == null) { - ar.setSubAccessRule(new HashSet<>()); - } - ar.getSubAccessRule().addAll(getAllowedQueryTypeRules()); - ar.getSubAccessRule().addAll(getHarmonizedSubRules()); - ar.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, conceptPath, projectAlias)); - accessRuleService.save(ar); - } - } - - private Set getAllowedQueryTypeRules() { - if (allowQueryTypeRules == null) { - allowQueryTypeRules = loadAllowedQueryTypeRules(); - } - - return allowQueryTypeRules; - } - - /** - * Retrieves or creates AccessRules for allowed query types. - * - * @return A set of AccessRules for allowed query types. - */ - private Set loadAllowedQueryTypeRules() { - // Initialize a set to hold the AccessRules - Set rules = new HashSet<>(); - // Split the allowed query types from the configuration - String[] allowedTypes = this.fence_allowed_query_types.split(","); - - // Iterate over each allowed query type - for (String queryType : allowedTypes) { - // Construct the AccessRule name - String ar_name = "AR_ALLOW_" + queryType; - - // Log the creation of a new AccessRule - AccessRule ar = getOrCreateAccessRule( - ar_name, - "FENCE SUB AR to allow " + queryType + " Queries", - "$.query.query.expectedResultType", - AccessRule.TypeNaming.ALL_EQUALS, - queryType, - false, - false, - false, - false - ); - - // Add the newly created rule to the set - rules.add(ar); - } - // Return the set of AccessRules - return rules; - } - - private Collection getTopmedRestrictedSubRules() { - Set rules = new HashSet(); - rules.add(upsertTopmedRestrictedSubRule("CATEGORICAL", "$.query.query.variantInfoFilters[*].categoryVariantInfoFilters.*")); - rules.add(upsertTopmedRestrictedSubRule("NUMERIC", "$.query.query.variantInfoFilters[*].numericVariantInfoFilters.*")); - - return rules; - } - - /** - * Creates and returns a restricted sub-rule AccessRule for Topmed. - * topmed restriction rules don't need much configuration. Just deny all access. - * - * @param type The type of the Topmed restriction. - * @param rule The rule expression. - * @return The created AccessRule. - */ - private AccessRule upsertTopmedRestrictedSubRule(String type, String rule) { - // Construct the AccessRule name - String ar_name = "AR_TOPMED_RESTRICTED_" + type; - // Check if the AccessRule already exists - AccessRule ar = accessRuleService.getAccessRuleByName(ar_name); - if (ar != null) { - // Log and return the existing rule - logger.debug("Found existing rule: {}", ar.getName()); - return ar; - } - - // Log the creation of a new AccessRule - // Create the AccessRule using the createAccessRule method - return getOrCreateAccessRule( - ar_name, - "FENCE SUB AR for restricting " + type + " genomic concepts", - rule, - AccessRule.TypeNaming.IS_EMPTY, - null, - false, - false, - false, - false - ); - } - - private Collection getPhenotypeSubRules(String studyIdentifier, String conceptPath, String alias) { - - Set rules = new HashSet(); - //categorical filters will always contain at least one entry (for the consent groups); it will never be empty - rules.add(createPhenotypeSubRule(fence_parent_consent_group_concept_path, "ALLOW_PARENT_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); - - for (String underscorePath : underscoreFields) { - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); - } - - rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); - rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true)); - rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); - rules.add(createPhenotypeSubRule(conceptPath, alias + "_" + studyIdentifier, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false)); - - return rules; - } - - /** - * Harmonized rules should allow the user to supply paretn and top med consent groups; this allows a single harmonized - * rules instead of splitting between a topmed+harmonized and parent+harmonized - * - * @return - */ - private Collection getHarmonizedSubRules() { - - Set rules = new HashSet(); - //categorical filters will always contain at least one entry (for the consent groups); it will never be empty - rules.add(createPhenotypeSubRule(fence_parent_consent_group_concept_path, "ALLOW_PARENT_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); - rules.add(createPhenotypeSubRule(fence_harmonized_consent_group_concept_path, "ALLOW_HARMONIZED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); - rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); - - for (String underscorePath : underscoreFields) { - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); - } - - rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); - rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.numericFilters", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "NUMERIC", true)); - rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); - rules.add(createPhenotypeSubRule(fence_harmonized_concept_path, "HARMONIZED", "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQUIRED_FIELDS", false)); - - return rules; - } - - - /** - * generate and return a set of rules that disallow access to phenotype data (only genomic filters allowed) - * - * @return - */ - private Collection getPhenotypeRestrictedSubRules(String studyIdentifier, String consentCode, String alias) { - Set rules = new HashSet(); - //categorical filters will always contain at least one entry (for the consent groups); it will never be empty - rules.add(createPhenotypeSubRule(fence_topmed_consent_group_concept_path, "ALLOW_TOPMED_CONSENT", "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "", true)); - - for (String underscorePath : underscoreFields) { - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.fields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "FIELDS", false)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.categoryFilters", AccessRule.TypeNaming.ALL_CONTAINS, "CATEGORICAL", true)); - rules.add(createPhenotypeSubRule(underscorePath, "ALLOW " + underscorePath, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY, "REQ_FIELDS", false)); - } - - rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.numericFilters.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_NUMERIC", false)); - rules.add(createPhenotypeSubRule(null, alias + "_" + studyIdentifier + "_" + consentCode, "$.query.query.requiredFields.[*]", AccessRule.TypeNaming.IS_EMPTY, "DISALLOW_REQUIRED_FIELDS", false)); - - return rules; - } - - /** - * Return a set of gates that identify which consent values have been provided. the boolean parameters indicate - * if a value in the specified consent location should allow this gate to pass. - * - * @param parent - * @param harmonized - * @param topmed - * @return - */ - private Collection getGates(boolean parent, boolean harmonized, boolean topmed) { - Set gates = new HashSet(); - gates.add(upsertConsentGate("PARENT_CONSENT", "$.query.query.categoryFilters." + fence_parent_consent_group_concept_path + "[*]", parent, "parent study data")); - gates.add(upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", harmonized, "harmonized data")); - gates.add(upsertConsentGate("TOPMED_CONSENT", "$.query.query.categoryFilters." + fence_topmed_consent_group_concept_path + "[*]", topmed, "Topmed data")); - - return gates; - } - - /** - * Creates a privilege for Topmed access. This has (up to) three access rules: - * 1) topmed only 2) topmed + parent 3) topmed + harmonized. - * - * @param studyIdentifier - * @param projectAlias - * @param consentGroup - * @param parentConceptPath - * @param isHarmonized - * @return Privilege - */ - private Privilege upsertTopmedPrivilege(String studyIdentifier, String projectAlias, String consentGroup, String parentConceptPath, boolean isHarmonized) { - String privilegeName = "PRIV_FENCE_" + studyIdentifier + "_" + consentGroup + "_TOPMED"; - Privilege priv = privilegeService.findByName(privilegeName); - - if (priv != null) { - logger.info("upsertTopmedPrivilege() {} already exists", privilegeName); - return priv; - } - - priv = new Privilege(); - - try { - buildPrivilegeObject(priv, privilegeName, studyIdentifier, consentGroup); - - Set accessRules = new HashSet<>(); - AccessRule topmedRule = upsertTopmedAccessRule(studyIdentifier, consentGroup, "TOPMED"); - - populateAccessRule(topmedRule, false, false, true); - topmedRule.getSubAccessRule().addAll(getPhenotypeRestrictedSubRules(studyIdentifier, consentGroup, projectAlias)); - accessRules.add(topmedRule); - - if (parentConceptPath != null) { - AccessRule topmedParentRule = upsertTopmedAccessRule(studyIdentifier, consentGroup, "TOPMED+PARENT"); - populateAccessRule(topmedParentRule, true, false, true); - topmedParentRule.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, parentConceptPath, projectAlias)); - accessRules.add(topmedParentRule); - - if (isHarmonized) { - AccessRule harmonizedRule = upsertHarmonizedAccessRule(studyIdentifier, consentGroup, "HARMONIZED"); - populateHarmonizedAccessRule(harmonizedRule, parentConceptPath, studyIdentifier, projectAlias); - accessRules.add(harmonizedRule); - } - } - - addStandardAccessRules(accessRules); - - priv.setAccessRules(accessRules); - logger.info("upsertTopmedPrivilege() Added {} access_rules to privilege", accessRules.size()); - - privilegeService.save(priv); - logger.info("upsertTopmedPrivilege() Added new privilege {} to DB", priv.getName()); - } catch (Exception ex) { - logger.error("upsertTopmedPrivilege() could not save privilege", ex); - } - - return priv; - } - - private void buildPrivilegeObject(Privilege priv, String privilegeName, String studyIdentifier, String consentGroup) { - priv.setApplication(picSureApp); - priv.setName(privilegeName); - priv.setDescription("FENCE privilege for Topmed " + studyIdentifier + "." + consentGroup); - - String consentConceptPath = escapePath(fence_topmed_consent_group_concept_path); - fence_harmonized_concept_path = escapePath(fence_harmonized_concept_path); - - String queryTemplateText = "{\"categoryFilters\": {\"" + consentConceptPath + "\":[\"" + studyIdentifier + "." + consentGroup + "\"]}," - + "\"numericFilters\":{},\"requiredFields\":[]," - + "\"fields\":[\"" + topmedAccessionField + "\"]," - + "\"variantInfoFilters\":[{\"categoryVariantInfoFilters\":{},\"numericVariantInfoFilters\":{}}]," - + "\"expectedResultType\": \"COUNT\"" - + "}"; - - priv.setQueryTemplate(queryTemplateText); - - priv.setQueryScope(buildQueryScope(this.variantAnnotationColumns)); - } - - private String escapePath(String path) { - if (path != null && !path.contains("\\\\")) { - return path.replaceAll("\\\\", "\\\\\\\\"); - } - return path; - } - - private String buildQueryScope(String variantColumns) { - if (variantColumns == null || variantColumns.isEmpty()) { - return "[\"_\"]"; - } - - return Arrays.stream(variantColumns.split(",")) - .map(path -> "\"" + path + "\"") - .collect(Collectors.joining(",", "[", ",\"_\"]")); - } - - private void populateAccessRule(AccessRule rule, boolean includeParent, boolean includeHarmonized, boolean includeTopmed) { - if (rule.getGates() == null) { - rule.setGates(new HashSet<>(getGates(includeParent, includeHarmonized, includeTopmed))); - } - - if (rule.getSubAccessRule() == null) { - rule.setSubAccessRule(new HashSet<>(getAllowedQueryTypeRules())); - } - - accessRuleService.save(rule); - } - - private void populateHarmonizedAccessRule(AccessRule rule, String parentConceptPath, String studyIdentifier, String projectAlias) { - if (rule.getGates() == null) { - rule.setGates(new HashSet<>(Collections.singletonList( - upsertConsentGate("HARMONIZED_CONSENT", "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]", true, "harmonized data") - ))); - } - - if (rule.getSubAccessRule() == null) { - rule.setSubAccessRule(new HashSet<>(getAllowedQueryTypeRules())); - rule.getSubAccessRule().addAll(getHarmonizedSubRules()); - rule.getSubAccessRule().addAll(getPhenotypeSubRules(studyIdentifier, parentConceptPath, projectAlias)); - } - - accessRuleService.save(rule); - } - - // A set of standard access rules that are added to all privileges - // to cache the standard access rules - private Set standardAccessRules; - - private void addStandardAccessRules(Set accessRules) { - if (standardAccessRules != null && !standardAccessRules.isEmpty()) { - accessRules.addAll(standardAccessRules); - } else { - standardAccessRules = new HashSet<>(); - for (String arName : fence_standard_access_rules.split(",")) { - if (arName.startsWith("AR_")) { - logger.info("Adding AccessRule {} to privilege", arName); - AccessRule ar = accessRuleService.getAccessRuleByName(arName); - if (ar != null) { - standardAccessRules.add(ar); - } else { - logger.warn("Unable to find an access rule with name {}", arName); - } - } - } - - accessRules.addAll(standardAccessRules); - } - } - - - /** - * Creates and returns a consent access rule AccessRule. - * Generates Main rule only; gates & sub-rules attached after calling this - * prentRule should be null if this is the main rule, or the appropriate value if this is a sub-rule - * - * @param studyIdentifier The study identifier. - * @param consent_group The consent group. - * @param label The label for the rule. - * @param consent_path The consent path. - * @return The created AccessRule. - */ - private AccessRule createConsentAccessRule(String studyIdentifier, String consent_group, String label, String consent_path) { - String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_CONSENT_" + studyIdentifier + "_" + consent_group + "_" + label : "AR_CONSENT_" + studyIdentifier; - String description = (consent_group != null && !consent_group.isEmpty()) ? "FENCE AR for " + studyIdentifier + "." + consent_group + " clinical concepts" : "FENCE AR for " + studyIdentifier + " clinical concepts"; - String ruleText = "$.query.query.categoryFilters." + consent_path + "[*]"; - String arValue = (consent_group != null && !consent_group.isEmpty()) ? studyIdentifier + "." + consent_group : studyIdentifier; - - return getOrCreateAccessRule( - ar_name, - description, - ruleText, - AccessRule.TypeNaming.ALL_CONTAINS, - arValue, - false, - false, - false, - false - ); - } - - /** - * Creates and returns a Topmed access rule AccessRule. - * Generates Main Rule only; gates & sub-rules attached by calling method - * - * @param project_name The name of the project. - * @param consent_group The consent group. - * @param label The label for the rule. - * @return The created AccessRule. - */ - private AccessRule upsertTopmedAccessRule(String project_name, String consent_group, String label) { - String ar_name = (consent_group != null && !consent_group.isEmpty()) ? "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label : "AR_TOPMED_" + project_name + "_" + label; - String description = "FENCE AR for " + project_name + "." + consent_group + " Topmed data"; - String ruleText = "$.query.query.categoryFilters." + fence_topmed_consent_group_concept_path + "[*]"; - String arValue = (consent_group != null && !consent_group.isEmpty()) ? project_name + "." + consent_group : project_name; - - return getOrCreateAccessRule( - ar_name, - description, - ruleText, - AccessRule.TypeNaming.ALL_CONTAINS, - arValue, - false, - false, - false, - false - ); - } - - /** - * Creates and returns a harmonized access rule AccessRule for Topmed. - * Generates Main Rule only; gates & sub rules attached by calling method - * - * @param project_name The name of the project. - * @param consent_group The consent group. - * @param label The label for the rule. - * @return The created AccessRule. - */ - private AccessRule upsertHarmonizedAccessRule(String project_name, String consent_group, String label) { - String ar_name = "AR_TOPMED_" + project_name + "_" + consent_group + "_" + label; - logger.info("upsertHarmonizedAccessRule() Creating new access rule {}", ar_name); - String description = "FENCE AR for " + project_name + "." + consent_group + " Topmed data"; - String ruleText = "$.query.query.categoryFilters." + fence_harmonized_consent_group_concept_path + "[*]"; - String arValue = project_name + "." + consent_group; - - return getOrCreateAccessRule( - ar_name, - description, - ruleText, - AccessRule.TypeNaming.ALL_CONTAINS, - arValue, - false, - false, - false, - false - ); - } - - /** - * Creates and returns a consent gate AccessRule. - * Insert a new gate (if it doesn't exist yet) to identify if consent values are present in the query. - * return an existing gate named GATE_{gateName}_(PRESENT|MISSING) if it exists. - * - * @param gateName The name of the gate. - * @param rule The rule expression. - * @param is_present Whether the gate is for present or missing consent. - * @param description The description of the gate. - * @return The created AccessRule. - */ - private AccessRule upsertConsentGate(String gateName, String rule, boolean is_present, String description) { - gateName = "GATE_" + gateName + "_" + (is_present ? "PRESENT" : "MISSING"); - return getOrCreateAccessRule( - gateName, - "FENCE GATE for " + description + " consent " + (is_present ? "present" : "missing"), - rule, - is_present ? AccessRule.TypeNaming.IS_NOT_EMPTY : AccessRule.TypeNaming.IS_EMPTY, - null, - false, - false, - false, - false - ); - } - - private AccessRule createPhenotypeSubRule(String conceptPath, String alias, String rule, int ruleType, String label, boolean useMapKey) { - String ar_name = "AR_PHENO_" + alias + "_" + label; - logger.info("createPhenotypeSubRule() Creating new access rule {}", ar_name); - return getOrCreateAccessRule( - ar_name, - "FENCE SUB AR for " + alias + " " + label + " clinical concepts", - rule, - ruleType, - ruleType == AccessRule.TypeNaming.IS_NOT_EMPTY ? null : conceptPath, - useMapKey, - useMapKey, - false, - false - ); - } - - private AccessRule getOrCreateAccessRule(String name, String description, String rule, int type, String value, boolean checkMapKeyOnly, boolean checkMapNode, boolean evaluateOnlyByGates, boolean gateAnyRelation) { - return accessRuleCache.computeIfAbsent(name, key -> { - AccessRule ar = accessRuleService.getAccessRuleByName(key); - if (ar == null) { - logger.info("Creating new access rule {}", key); - ar = new AccessRule(); - ar.setName(name); - ar.setDescription(description); - ar.setRule(rule); - ar.setType(type); - ar.setValue(value); - ar.setCheckMapKeyOnly(checkMapKeyOnly); - ar.setCheckMapNode(checkMapNode); - ar.setEvaluateOnlyByGates(evaluateOnlyByGates); - ar.setGateAnyRelation(gateAnyRelation); - accessRuleService.save(ar); - } - - return ar; - }); - } - - private 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 = ""; - if (projectMatcher.find()) { - project = projectMatcher.group(1).trim(); - } else { - logger.info("extractProject() Could not extract project from role name: {}", roleName); - String[] parts = roleName.split("_", 1); - if (parts.length > 0) { - project = parts[1]; - } - } - return project; - } - - 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 = ""; - if (consentMatcher.find()) { - consentGroup = "c" + consentMatcher.group(1).trim(); - } - return consentGroup; - } - - private StudyMetaData getFENCEMappingforProjectAndConsent(String projectId, String consent_group) { - String consentVal = (consent_group != null && !consent_group.isEmpty()) ? projectId + "." + consent_group : projectId; - logger.info("getFENCEMappingforProjectAndConsent() looking up {}", consentVal); - - return this.fenceMappingUtility.getFENCEMapping().get(consentVal); - } - } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OktaOAuthAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OktaOAuthAuthenticationService.java index cfc7482bb..29622abeb 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OktaOAuthAuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OktaOAuthAuthenticationService.java @@ -5,9 +5,10 @@ import com.fasterxml.jackson.databind.node.ObjectNode; 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.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.authorization.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; import jakarta.persistence.NoResultException; import org.apache.commons.lang3.StringUtils; @@ -15,7 +16,6 @@ import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; @@ -24,7 +24,7 @@ import java.util.*; @Service -public class OktaOAuthAuthenticationService { +public class OktaOAuthAuthenticationService implements AuthenticationService { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @@ -35,6 +35,7 @@ public class OktaOAuthAuthenticationService { private final String connectionId; private final String clientId; private final String spClientSecret; + private final boolean isOktaEnabled; private final AccessRuleService accessRuleService; private final RestClientUtil restClientUtil; @@ -48,23 +49,28 @@ public class OktaOAuthAuthenticationService { * @param spClientSecret The client secret */ @Autowired - public OktaOAuthAuthenticationService(UserService userService, RoleService roleService, - @Value("${okta.idp.provider.uri}") String idp_provider_uri, - @Value("${okta.connection.id}") String connectionId, - @Value("${okta.client.id}") String clientId, - @Value("${okta.client.secret}") String spClientSecret, AccessRuleService accessRuleService, RestClientUtil restClientUtil) { + public OktaOAuthAuthenticationService(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) { this.userService = userService; this.roleService = roleService; this.idp_provider_uri = idp_provider_uri; this.connectionId = connectionId; this.clientId = clientId; this.spClientSecret = spClientSecret; + this.isOktaEnabled = isOktaEnabled; + logger.info("OktaOAuthAuthenticationService is enabled: {}", isOktaEnabled); logger.info("OktaOAuthAuthenticationService initialized"); logger.info("idp_provider_uri: {}", idp_provider_uri); logger.info("connectionId: {}", connectionId); - logger.info("clientId: {}", clientId); - logger.info("spClientSecret: {}", spClientSecret); + this.accessRuleService = accessRuleService; this.restClientUtil = restClientUtil; } @@ -78,7 +84,10 @@ public OktaOAuthAuthenticationService(UserService userService, RoleService roleS * @param authRequest The request body * @return The response from the authentication attempt */ - public HashMap authenticate(String host, Map authRequest) { + @Override + public HashMap authenticate(Map authRequest, String host) { + logger.info("OKTA LOGIN ATTEMPT ___ {} ___", authRequest.get("code")); + String code = authRequest.get("code"); if (StringUtils.isNotBlank(code)) { JsonNode userToken = handleCodeTokenExchange(host, code); @@ -99,6 +108,16 @@ public HashMap authenticate(String host, Map aut return null; } + @Override + public String getProvider() { + return "aimAheadOkta"; + } + + @Override + public boolean isEnabled() { + return this.isOktaEnabled; + } + private User initializeUser(JsonNode introspectResponse) { if (introspectResponse == null) { logger.info("FAILED TO INTROSPECT TOKEN ___ "); @@ -135,8 +154,8 @@ private HashMap createUserClaims(User user) { } private void clearCache(User user) { - userService.evictFromCache(user); - accessRuleService.evictFromCache(user); + userService.evictFromCache(user.getEmail()); + accessRuleService.evictFromCache(user.getEmail()); } /** diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationService.java index 2edd9b259..9c7d6b969 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationService.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationService.java @@ -2,36 +2,41 @@ 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.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.authorization.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.HashMap; import java.util.Map; @Service -public class OpenAuthenticationService { +public class OpenAuthenticationService implements AuthenticationService { private final Logger logger = LoggerFactory.getLogger(OpenAuthenticationService.class); private final UserService userService; private final RoleService roleService; private final AccessRuleService accessRuleService; + private final boolean isOpenEnabled; @Autowired - public OpenAuthenticationService(UserService userService, RoleService roleService, AccessRuleService accessRuleService) { + public OpenAuthenticationService(UserService userService, RoleService roleService, AccessRuleService accessRuleService, + @Value("${open.idp.provider.is.enabled}") boolean isOpenEnabled) { this.userService = userService; this.roleService = roleService; this.accessRuleService = accessRuleService; + this.isOpenEnabled = isOpenEnabled; } - - public Map authenticate(Map authRequest) { + @Override + public HashMap authenticate(Map authRequest, String host) { String userUUID = authRequest.get("UUID"); User current_user = null; @@ -51,8 +56,8 @@ public Map authenticate(Map authRequest) { //clear some cache entries if we register a new login // I don't see a clear need to caching here. - accessRuleService.evictFromCache(current_user); - userService.evictFromCache(current_user); + accessRuleService.evictFromCache(current_user.getEmail()); + userService.evictFromCache(current_user.getEmail()); } HashMap claims = new HashMap<>(); @@ -65,4 +70,14 @@ public Map authenticate(Map authRequest) { return responseMap; } + + @Override + public String getProvider() { + return "open"; + } + + @Override + public boolean isEnabled() { + return this.isOpenEnabled; + } } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleService.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleService.java deleted file mode 100644 index 61ed3bf62..000000000 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleService.java +++ /dev/null @@ -1,477 +0,0 @@ -package edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization; - -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jayway.jsonpath.InvalidPathException; -import com.jayway.jsonpath.JsonPath; -import com.jayway.jsonpath.PathNotFoundException; -import com.mysql.cj.xdevapi.JsonArray; -import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; -import edu.harvard.hms.dbmi.avillach.auth.entity.Application; -import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege; -import edu.harvard.hms.dbmi.avillach.auth.entity.User; -import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.cache.annotation.CacheEvict; -import org.springframework.cache.annotation.Cacheable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.*; - -@Service -public class AccessRuleService { - - private final AccessRuleRepository accessRuleRepo; - private final Logger logger = LoggerFactory.getLogger(AccessRuleService.class); - private final ObjectMapper objectMapper = new ObjectMapper(); - - @Autowired - public AccessRuleService(AccessRuleRepository accessRuleRepo) { - this.accessRuleRepo = accessRuleRepo; - } - - public Optional getAccessRuleById(String accessRuleId) { - return accessRuleRepo.findById(UUID.fromString(accessRuleId)); - } - - public List getAllAccessRules() { - return accessRuleRepo.findAll(); - } - - public List addAccessRule(List accessRules) { - accessRules.forEach(accessRule -> { - if (accessRule.getEvaluateOnlyByGates() == null) - accessRule.setEvaluateOnlyByGates(false); - - if (accessRule.getCheckMapKeyOnly() == null) - accessRule.setCheckMapKeyOnly(false); - - if (accessRule.getCheckMapNode() == null) - accessRule.setCheckMapNode(false); - - if (accessRule.getGateAnyRelation() == null) - accessRule.setGateAnyRelation(false); - }); - - return this.accessRuleRepo.saveAll(accessRules); - } - - public List updateAccessRules(List accessRules) { - return this.accessRuleRepo.saveAll(accessRules); - } - - @Transactional - public List removeAccessRuleById(String accessRuleId) { - this.accessRuleRepo.deleteById(UUID.fromString(accessRuleId)); - return this.accessRuleRepo.findAll(); - } - - public AccessRule save(AccessRule accessRule) { - return this.accessRuleRepo.save(accessRule); - } - - public AccessRule getAccessRuleByName(String arName) { - return this.accessRuleRepo.findByName(arName); - } - - @Cacheable(value = "mergedRulesCache", key = "#user.getEmail()") - public Set getAccessRulesForUserAndApp(User user, Application application) { - try { - Set privileges = user.getPrivilegesByApplication(application); - - if (privileges == null || privileges.isEmpty()) { - return null; - } - - Set detachedMergedRules = new HashSet<>(); - for (AccessRule rule : preProcessAccessRules(privileges)) { - detachedMergedRules.add(objectMapper.readValue(objectMapper.writeValueAsString(rule), AccessRule.class)); - } - - return detachedMergedRules; - } catch (Exception e) { - logger.error("Error populating or retrieving data from cache: ", e); - } - - return null; - } - - @CacheEvict(value = "mergedRulesCache", key = "#user.getEmail()") - public void evictFromCache(User user) { - // This method is used to clear the cache for a user when their privileges are updated - } - - public Set preProcessAccessRules(Set privileges) { - Set accessRules = new HashSet<>(); - for (Privilege privilege : privileges) { - accessRules.addAll(privilege.getAccessRules()); - } - - return preProcessARBySortedKeys(accessRules); - } - - public Set preProcessARBySortedKeys(Set accessRules) { - Map> accessRuleMap = new HashMap<>(); - - for (AccessRule accessRule : accessRules) { - - // 1st generate the key by grabbing all related string and put them together in order - // we use a treeSet here to put orderly combine Strings together - Set keys = new TreeSet<>(); - - // the current accessRule rule - keys.add(accessRule.getRule()); - - // all gates' UUID as strings - keys.add(accessRule.getType().toString()); - - if (accessRule.getGates() != null) { - for (AccessRule gate : accessRule.getGates()) { - keys.add(gate.getUuid().toString()); - } - } - - // all sub accessRule rules - if (accessRule.getSubAccessRule() != null) { - for (AccessRule subAccessRule : accessRule.getSubAccessRule()) { - keys.add(subAccessRule.getRule()); - } - } - Boolean checkMapKeyOnly = accessRule.getCheckMapKeyOnly(), - checkMapNode = accessRule.getCheckMapNode(), - evaluateOnlyByGates = accessRule.getEvaluateOnlyByGates(), - gateAnyRelation = accessRule.getGateAnyRelation(); - - keys.add(checkMapKeyOnly == null ? "null" : Boolean.toString(checkMapKeyOnly)); - keys.add(checkMapNode == null ? "null" : Boolean.toString(checkMapNode)); - keys.add(evaluateOnlyByGates == null ? "null" : Boolean.toString(evaluateOnlyByGates)); - keys.add(gateAnyRelation == null ? "null" : Boolean.toString(gateAnyRelation)); - - String key = String.join("", keys); - if (accessRuleMap.containsKey(key)) { - accessRuleMap.get(key).add(accessRule); - } else { - Set accessRuleSet = new HashSet<>(); - accessRuleSet.add(accessRule); - accessRuleMap.put(key, accessRuleSet); - } - } - - return mergeSameKeyAccessRules(accessRuleMap.values()); - } - - private Set mergeSameKeyAccessRules(Collection> accessRuleMap) { - Set accessRules = new HashSet<>(); - for (Set accessRulesSet : accessRuleMap) { - AccessRule accessRule = null; - for (AccessRule innerAccessRule : accessRulesSet) { - accessRule = mergeAccessRules(accessRule, innerAccessRule); - } - if (accessRule != null) { - accessRules.add(accessRule); - } - } - return accessRules; - } - - private AccessRule mergeAccessRules(AccessRule baseAccessRule, AccessRule accessRuleToBeMerged) { - if (baseAccessRule == null) { - accessRuleToBeMerged.getMergedValues().add(accessRuleToBeMerged.getValue()); - return accessRuleToBeMerged; - } - - if (baseAccessRule.getSubAccessRule() != null && accessRuleToBeMerged.getSubAccessRule() != null) { - baseAccessRule.getSubAccessRule().addAll(accessRuleToBeMerged.getSubAccessRule()); - } else if (baseAccessRule.getSubAccessRule() == null && accessRuleToBeMerged.getSubAccessRule() != null) { - baseAccessRule.setSubAccessRule(accessRuleToBeMerged.getSubAccessRule()); - } - - baseAccessRule.getMergedValues().add(accessRuleToBeMerged.getValue()); - if (baseAccessRule.getMergedName().startsWith("Merged|")) { - baseAccessRule.setMergedName(baseAccessRule.getMergedName() + "|" + accessRuleToBeMerged.getName()); - } else { - baseAccessRule.setMergedName("Merged|" + baseAccessRule.getName() + "|" + accessRuleToBeMerged.getName()); - } - - return baseAccessRule; - } - - public boolean evaluateAccessRule(Object parsedRequestBody, AccessRule accessRule) { - logger.debug("evaluateAccessRule() starting with: {}", parsedRequestBody); - logger.debug("evaluateAccessRule() access rule: {}", accessRule.getName()); - - Set gates = accessRule.getGates(); - boolean gatesPassed = true; - - // depends on the flag getGateAnyRelation is true or false, - // the logic of checking if apply gate will be changed - // the following cases are gate passed: - // 1. if gates are null or empty - // 2. if getGateAnyRelation is false, all gates passed - // 3. if getGateAnyRelation is true, one of the gate passed - if (gates != null && !gates.isEmpty()) { - if (accessRule.getGateAnyRelation() == null || !accessRule.getGateAnyRelation()) { - // All gates are AND relationship - // means one fails all fail - for (AccessRule gate : gates) { - if (!evaluateAccessRule(parsedRequestBody, gate)) { - logger.error("evaluateAccessRule() gate {} failed: {} ____ {}", gate.getName(), gate.getRule(), gate.getValue()); - gatesPassed = false; - break; - } - } - } else { - // All gates are OR relationship - // means one passes all pass - gatesPassed = false; - for (AccessRule gate : gates) { - if (evaluateAccessRule(parsedRequestBody, gate)) { - logger.debug("evaluateAccessRule() gate {} passed ", gate.getName()); - gatesPassed = true; - break; - } - } - - if (!gatesPassed) { - logger.debug("all OR gates failed"); - } - } - } - - if (accessRule.getEvaluateOnlyByGates() != null && accessRule.getEvaluateOnlyByGates()) { - logger.debug("evaluateAccessRule() eval only by gates"); - return gatesPassed; - } - - if (gatesPassed) { - logger.debug("evaluateAccessRule() gates passed"); - if (!extractAndCheckRule(accessRule, parsedRequestBody)) { - logger.debug("Query Rejected by rule(1) {} :: {} :: {}", accessRule.getRule(), accessRule.getType(), accessRule.getValue()); - return false; - } else { - if (accessRule.getSubAccessRule() != null) { - for (AccessRule subAccessRule : accessRule.getSubAccessRule()) { - if (!extractAndCheckRule(subAccessRule, parsedRequestBody)) { - logger.debug("Query Rejected by rule(2) {} :: {} :: {}", subAccessRule.getRule(), subAccessRule.getType(), subAccessRule.getValue()); - return false; - } - } - } - } - } else { - logger.debug("evaluateAccessRule() gates failed"); - return false; - } - - return true; - } - - public boolean extractAndCheckRule(AccessRule accessRule, Object parsedRequestBody) { - logger.debug("extractAndCheckRule() starting"); - String rule = accessRule.getRule(); - - if (rule == null || rule.isEmpty()) - return true; - - Object requestBodyValue; - int accessRuleType = accessRule.getType(); - - try { - requestBodyValue = JsonPath.parse(parsedRequestBody).read(rule); - - // Json parse will always return a list even when we want a map (to check keys) - if (requestBodyValue instanceof JsonArray && ((JsonArray) requestBodyValue).size() == 1) { - requestBodyValue = ((JsonArray) requestBodyValue).get(0); - } - - } catch (PathNotFoundException ex) { - if (accessRuleType == AccessRule.TypeNaming.IS_EMPTY) { - // We could return accessRuleType == AccessRule.TypeNaming.IS_EMPTY directly, but we want to log the reason - logger.debug("extractAndCheckRule() -> JsonPath.parse().read() PathNotFound; passing IS_EMPTY rule"); - return true; - } - logger.debug("extractAndCheckRule() -> JsonPath.parse().read() throws exception with parsedRequestBody - {} : {} - {}", parsedRequestBody, ex.getClass().getSimpleName(), ex.getMessage()); - return false; - } - - if (accessRuleType == AccessRule.TypeNaming.IS_EMPTY - || accessRuleType == AccessRule.TypeNaming.IS_NOT_EMPTY) { - if (requestBodyValue == null - || (requestBodyValue instanceof String && ((String) requestBodyValue).isEmpty()) - || (requestBodyValue instanceof Collection && ((Collection) requestBodyValue).isEmpty()) - || (requestBodyValue instanceof Map && ((Map) requestBodyValue).isEmpty())) { - return accessRuleType == AccessRule.TypeNaming.IS_EMPTY; - } else { - return accessRuleType == AccessRule.TypeNaming.IS_NOT_EMPTY; - } - } - - return evaluateNode(requestBodyValue, accessRule); - } - - private boolean evaluateNode(Object requestBodyValue, AccessRule accessRule) { - logger.debug("evaluateNode() starting: {} :: {} :: {}", accessRule.getRule(), accessRule.getType(), accessRule.getMergedValues().isEmpty() ? accessRule.getValue() : ("Merged " + Arrays.deepToString(accessRule.getMergedValues().toArray()))); - logger.trace("evaluateNode() requestBody {} {}", requestBodyValue.getClass().getName(), requestBodyValue instanceof Collection ? - Arrays.deepToString(((Collection) requestBodyValue).toArray()) : - requestBodyValue.toString()); - - return switch (requestBodyValue) { - case String s -> decisionMaker(accessRule, s); - case Collection collection -> evaluateCollection(collection, accessRule); - case Map map when accessRule.getCheckMapNode() != null && accessRule.getCheckMapNode() -> - evaluateMap(requestBodyValue, accessRule); - default -> true; - }; - } - - private boolean evaluateMap(Object requestBodyValue, AccessRule accessRule) { - switch (accessRule.getType()) { - case (AccessRule.TypeNaming.ANY_EQUALS): - case (AccessRule.TypeNaming.ANY_CONTAINS): - case (AccessRule.TypeNaming.ANY_REG_MATCH): - for (Map.Entry entry : ((Map) requestBodyValue).entrySet()) { - if (decisionMaker(accessRule, (String) entry.getKey())) - return true; - - if ((accessRule.getCheckMapKeyOnly() == null || !accessRule.getCheckMapKeyOnly()) - && evaluateNode(entry.getValue(), accessRule)) - return true; - } - return false; - default: - if (((Map) requestBodyValue).isEmpty()) { - switch (accessRule.getType()) { - case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE): - case (AccessRule.TypeNaming.ALL_EQUALS): - case (AccessRule.TypeNaming.ALL_CONTAINS): - case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE): - return false; - case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY): - case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE): - default: - return true; - } - } - for (Map.Entry entry : ((Map) requestBodyValue).entrySet()) { - if (!decisionMaker(accessRule, (String) entry.getKey())) - return false; - - if ((accessRule.getCheckMapKeyOnly() == null || !accessRule.getCheckMapKeyOnly()) - && !evaluateNode(entry.getValue(), accessRule)) - return false; - } - - } - - return true; - } - - private Boolean evaluateCollection(Collection requestBodyValue, AccessRule accessRule) { - switch (accessRule.getType()) { - case (AccessRule.TypeNaming.ANY_EQUALS): - case (AccessRule.TypeNaming.ANY_CONTAINS): - case (AccessRule.TypeNaming.ANY_REG_MATCH): - for (Object item : requestBodyValue) { - if (item instanceof String) { - if (decisionMaker(accessRule, (String) item)) { - return true; - } - } else { - if (evaluateNode(item, accessRule)) { - return true; - } - } - } - return false; - default: - if (requestBodyValue.isEmpty()) { - switch (accessRule.getType()) { - case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE): - case (AccessRule.TypeNaming.ALL_EQUALS): - case (AccessRule.TypeNaming.ALL_CONTAINS): - case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE): - return false; - case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY): - case (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE): - default: - return true; - } - } - - for (Object item : requestBodyValue) { - if (item instanceof String) { - if (!decisionMaker(accessRule, (String) item)) { - return false; - } - } else { - if (!evaluateNode(item, accessRule)) - return false; - } - } - } - - return true; - } - - public boolean decisionMaker(AccessRule accessRule, String requestBodyValue) { - if (accessRule.getMergedValues().isEmpty()) { - String value = accessRule.getValue(); - if (value == null) { - return requestBodyValue == null; - } - return _decisionMaker(accessRule, requestBodyValue, value); - } - - // recursively check the values - // until one of them is true - // if there is only one element in the merged value set - // the operation equals to _decisionMaker(accessRule, requestBodyValue, value) - boolean res = false; - for (String s : accessRule.getMergedValues()) { - // check the special case value is null - // if value is null, the check will stop here and - // not goes to _decisionMaker() - if (s == null) { - if (requestBodyValue == null) { - res = true; - break; - } else { - continue; - } - } - - // all the merged values are OR relationship - // means if you pass one of them, you pass the rule - if (_decisionMaker(accessRule, requestBodyValue, s)) { - res = true; - break; - } - } - return res; - } - - private boolean _decisionMaker(AccessRule accessRule, String requestBodyValue, String value) { - logger.debug("_decisionMaker() starting"); - logger.debug("_decisionMaker() access rule:{}", accessRule.getName()); - logger.debug(requestBodyValue); - logger.debug(value); - - return switch (accessRule.getType()) { - case AccessRule.TypeNaming.NOT_CONTAINS -> !requestBodyValue.contains(value); - case AccessRule.TypeNaming.NOT_CONTAINS_IGNORE_CASE -> !requestBodyValue.toLowerCase().contains(value.toLowerCase()); - case (AccessRule.TypeNaming.NOT_EQUALS) -> !value.equals(requestBodyValue); - case (AccessRule.TypeNaming.ANY_EQUALS), (AccessRule.TypeNaming.ALL_EQUALS) -> value.equals(requestBodyValue); - case (AccessRule.TypeNaming.ALL_CONTAINS), (AccessRule.TypeNaming.ANY_CONTAINS), (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY) -> requestBodyValue.contains(value); - case (AccessRule.TypeNaming.ALL_CONTAINS_IGNORE_CASE), (AccessRule.TypeNaming.ALL_CONTAINS_OR_EMPTY_IGNORE_CASE) -> requestBodyValue.toLowerCase().contains(value.toLowerCase()); - case (AccessRule.TypeNaming.NOT_EQUALS_IGNORE_CASE) -> !value.equalsIgnoreCase(requestBodyValue); - case (AccessRule.TypeNaming.ALL_EQUALS_IGNORE_CASE) -> value.equalsIgnoreCase(requestBodyValue); - case (AccessRule.TypeNaming.ALL_REG_MATCH), (AccessRule.TypeNaming.ANY_REG_MATCH) -> requestBodyValue.matches(value); - default -> { - logger.warn("evaluateAccessRule() incoming accessRule type is out of scope. Just return true."); - yield true; - } - }; - } -} 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 b801dfe9f..940d622cc 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 @@ -7,6 +7,7 @@ import edu.harvard.hms.dbmi.avillach.auth.entity.Privilege; import edu.harvard.hms.dbmi.avillach.auth.entity.User; import edu.harvard.hms.dbmi.avillach.auth.rest.TokenController; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -85,23 +86,26 @@ public AuthorizationService(AccessRuleService accessRuleService, @Value("${stric * @see AccessRule */ public boolean isAuthorized(Application application, Object requestBody, User user) { + // create timer + long startTime = System.currentTimeMillis(); String applicationName = application.getName(); String resourceId = "null"; String targetService = "null"; //in some cases, we don't go through the evaluation if (requestBody == null) { - logger.info("ACCESS_LOG ___ {},{},{} ___ has been granted access to application ___ {} ___ NO REQUEST BODY FORWARDED BY APPLICATION", user.getUuid().toString(), user.getEmail(), user.getName(), applicationName); + logger.debug("ACCESS_LOG ___ {},{},{} ___ has been granted access to application ___ {} ___ NO REQUEST BODY FORWARDED BY APPLICATION", user.getUuid().toString(), user.getEmail(), user.getName(), applicationName); return true; } + long parseTimeFrame = System.currentTimeMillis(); try { Map requestBodyMap = (Map) requestBody; Map queryMap = (Map) requestBodyMap.get("query"); resourceId = (String) queryMap.get("resourceUUID"); targetService = (String) queryMap.get("Target Service"); } catch (RuntimeException e) { - logger.info("Error parsing resource and target service from request body."); + logger.debug("Error parsing resource and target service from request body."); } String formattedQuery; @@ -114,10 +118,11 @@ public boolean isAuthorized(Application application, Object requestBody, User us } } catch (ClassCastException | JsonProcessingException e1) { - logger.info("ACCESS_LOG ___ {},{},{} ___ has been denied access to execute query ___ {} ___ in application ___ {} ___ UNABLE TO PARSE REQUEST", user.getUuid().toString(), user.getEmail(), user.getName(), requestBody, applicationName); - logger.info("isAuthorized() Stack Trace: ", e1); + logger.debug("ACCESS_LOG ___ {},{},{} ___ has been denied access to execute query ___ {} ___ in application ___ {} ___ UNABLE TO PARSE REQUEST", user.getUuid().toString(), user.getEmail(), user.getName(), requestBody, applicationName); + logger.debug("isAuthorized() Stack Trace: ", e1); return false; } + logger.info("Parse timeframe {} ms", (System.currentTimeMillis() - parseTimeFrame)); Set accessRules; String label = user.getConnection().getLabel(); @@ -128,12 +133,13 @@ public boolean isAuthorized(Application application, Object requestBody, User us return false; } - accessRules = this.accessRuleService.preProcessAccessRules(privileges); + accessRules = this.accessRuleService.cachedPreProcessAccessRules(user, privileges); if (accessRules == null || 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 { + logger.info("User Email: {}", user.getEmail()); accessRules = this.accessRuleService.getAccessRulesForUserAndApp(user, application); if (accessRules == null || 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); @@ -168,6 +174,7 @@ public boolean isAuthorized(Application application, Object requestBody, User us .map(ar -> (ar.getMergedName().isEmpty() ? ar.getName() : ar.getMergedName())) .collect(Collectors.joining(", ")) + "]"); + logger.info("Login time: {}ms", System.currentTimeMillis() - startTime); return result; } diff --git a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/RestClientUtil.java b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/RestClientUtil.java index 6c9dacaf6..e96dc76c1 100644 --- a/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/RestClientUtil.java +++ b/pic-sure-auth-services/src/main/java/edu/harvard/hms/dbmi/avillach/auth/utils/RestClientUtil.java @@ -4,9 +4,11 @@ import org.slf4j.LoggerFactory; import org.springframework.http.*; import org.springframework.http.client.ClientHttpRequestFactory; +import org.springframework.http.client.SimpleClientHttpRequestFactory; import org.springframework.stereotype.Component; import org.springframework.util.MultiValueMap; import org.springframework.web.client.HttpClientErrorException; +import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; @Component @@ -64,4 +66,11 @@ public ResponseEntity retrievePostResponse(String uri, HttpEntity(); authRequest.put("access_token", accessToken); authRequest.put("redirectURI", redirectURI); + + authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost"); } // Tests missing parameters in the authentication request @Test(expected = IllegalArgumentException.class) public void testGetToken_MissingParameters() throws IOException { - authenticationService.getToken(new HashMap<>()); // Empty map should trigger the exception + authenticationService.authenticate(new HashMap<>(), "localhost"); // Empty map should trigger the exception } // Tests the failure in retrieving user information, expecting an IOException to be converted into a NotAuthorizedException @@ -72,7 +72,7 @@ public void testGetToken_MissingParameters() throws IOException { public void testGetToken_UserInfoRetrievalFails() throws IOException { when(this.restClientUtil.retrieveGetResponseWithRequestConfiguration(anyString(), any(HttpHeaders.class), any(ClientHttpRequestFactory.class))) .thenThrow(new NotAuthorizedException("Failed to retrieve user info")); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Tests the scenario where the user ID is not found in the user info retrieved @@ -80,7 +80,7 @@ public void testGetToken_UserInfoRetrievalFails() throws IOException { public void testGetToken_NoUserIdInUserInfo() throws IOException { when(this.restClientUtil.retrieveGetResponseWithRequestConfiguration(anyString(), any(), any())) .thenReturn(new ResponseEntity<>("{}", HttpStatus.OK)); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Tests a successful token retrieval scenario @@ -91,7 +91,7 @@ public void testGetToken_Successful() throws Exception { // return null for matching user when(matchingService.matchTokenToUser(any())).thenReturn(null); - HashMap token = authenticationService.getToken(authRequest); + HashMap token = authenticationService.authenticate(authRequest, "localhost"); assertNotNull(token); } @@ -110,23 +110,23 @@ public void testRetrieveUserInfo_WithRetries() throws Exception { @Test(expected = NotAuthorizedException.class) public void testGetToken_NoUserMatchingAndCreationFails() throws Exception { setupNoUserMatchScenario(); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Test scenario where denied access email is triggered @Test public void testGetToken_SendDeniedAccessEmail() throws Exception { setupDeniedEmailScenario(); - this.authenticationService.setDeniedEmailEnabled("true"); + this.authenticationService.setDeniedEmailEnabled(true); try { - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } catch (Exception e) { verify(basicMailService).sendDeniedAccessEmail(any()); } } private void setupSuccessfulTokenRetrievalScenario() throws IOException { - this.authenticationService.setDeniedEmailEnabled("false"); + this.authenticationService.setDeniedEmailEnabled(false); JsonNode mockUserInfo = mock(JsonNode.class); when(mockUserInfo.get("user_id")).thenReturn(mock(JsonNode.class)); when(mockUserInfo.get("user_id").asText()).thenReturn(userId); 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 cedb3c30f..583f98899 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 @@ -4,12 +4,10 @@ import edu.harvard.hms.dbmi.avillach.auth.enums.SecurityRoles; import edu.harvard.hms.dbmi.avillach.auth.model.CustomUserDetails; import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; -import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AccessRuleService; import edu.harvard.hms.dbmi.avillach.auth.service.impl.authorization.AuthorizationService; import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming; import org.junit.Before; import org.junit.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; @@ -37,7 +35,7 @@ public void setUp() { MockitoAnnotations.initMocks(this); SecurityContextHolder.setContext(securityContext); - accessRuleService = new AccessRuleService(accessRuleRepository); + accessRuleService = new AccessRuleService(accessRuleRepository, "false", "false", "false", "false","false", "false"); authorizationService = new AuthorizationService(accessRuleService, "fence,okta,open"); } diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessServiceTest.java index 9317b2b0c..2c219c91a 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/StudyAccessServiceTest.java @@ -20,7 +20,7 @@ public class StudyAccessServiceTest { private StudyAccessService studyAccessService; @Mock - private FENCEAuthenticationService fenceAuthenticationService; + private RoleService roleService; @Mock private FenceMappingUtility fenceMappingUtility; @@ -46,7 +46,7 @@ public void testAddStudyAccess() { studyMetaData.setConsentGroupCode(""); when(fenceMappingUtility.getFENCEMapping()).thenReturn(Map.of(studyIdentifier, studyMetaData)); - when(fenceAuthenticationService.upsertRole(null, "MANUAL_testStudy", "MANUAL_ role MANUAL_testStudy")).thenReturn(true); + when(roleService.upsertRole(null, "MANUAL_testStudy", "MANUAL_ role MANUAL_testStudy")).thenReturn(true); String status = studyAccessService.addStudyAccess(studyIdentifier); assertEquals("Role 'MANUAL_testStudy' successfully created", status); @@ -60,7 +60,7 @@ public void testAddStudyAccessWithConsent() { studyMetaData.setConsentGroupCode("c2"); when(fenceMappingUtility.getFENCEMapping()).thenReturn(Map.of(studyIdentifier, studyMetaData)); - when(fenceAuthenticationService.upsertRole(null, "MANUAL_testStudy2_c2", "MANUAL_ role MANUAL_testStudy2_c2")).thenReturn(true); + when(roleService.upsertRole(null, "MANUAL_testStudy2_c2", "MANUAL_ role MANUAL_testStudy2_c2")).thenReturn(true); String status = studyAccessService.addStudyAccess(studyIdentifier); assertEquals("Role 'MANUAL_testStudy2_c2' successfully created", status); 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 2b3204861..674b6eeaa 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 @@ -12,7 +12,6 @@ import edu.harvard.hms.dbmi.avillach.auth.utils.RestClientUtil; import org.junit.Before; import org.junit.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.http.HttpHeaders; @@ -46,7 +45,6 @@ public class AuthenticationServiceTest { @Mock private RestClientUtil restClientUtil; - @InjectMocks private Auth0AuthenticationService authenticationService; private final String accessToken = "dummyAccessToken"; @@ -61,12 +59,14 @@ public void setUp() { authRequest = new HashMap<>(); authRequest.put("access_token", accessToken); authRequest.put("redirectURI", redirectURI); + + authenticationService = new Auth0AuthenticationService(matchingService, userRepository, basicMailService, userService, connectionRepository, restClientUtil, true, false, "localhost"); } // Tests missing parameters in the authentication request @Test(expected = IllegalArgumentException.class) public void testGetToken_MissingParameters() throws IOException { - authenticationService.getToken(new HashMap<>()); // Empty map should trigger the exception + authenticationService.authenticate(new HashMap<>(), "localhost"); // Empty map should trigger the exception } // Tests the failure in retrieving user information, expecting an IOException to be converted into a NotAuthorizedException @@ -74,7 +74,7 @@ public void testGetToken_MissingParameters() throws IOException { public void testGetToken_UserInfoRetrievalFails() throws IOException { when(this.restClientUtil.retrieveGetResponseWithRequestConfiguration(anyString(), any(HttpHeaders.class), any(ClientHttpRequestFactory.class))) .thenThrow(new NotAuthorizedException("Failed to retrieve user info")); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Tests the scenario where the user ID is not found in the user info retrieved @@ -82,7 +82,7 @@ public void testGetToken_UserInfoRetrievalFails() throws IOException { public void testGetToken_NoUserIdInUserInfo() throws IOException { when(this.restClientUtil.retrieveGetResponseWithRequestConfiguration(anyString(), any(), any())) .thenReturn(new ResponseEntity<>("{}", HttpStatus.OK)); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Tests a successful token retrieval scenario @@ -93,7 +93,7 @@ public void testGetToken_Successful() throws Exception { // return null for matching user when(matchingService.matchTokenToUser(any())).thenReturn(null); - HashMap token = authenticationService.getToken(authRequest); + HashMap token = authenticationService.authenticate(authRequest, "localhost"); assertNotNull(token); } @@ -112,23 +112,23 @@ public void testRetrieveUserInfo_WithRetries() throws Exception { @Test(expected = NotAuthorizedException.class) public void testGetToken_NoUserMatchingAndCreationFails() throws Exception { setupNoUserMatchScenario(); - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } // Test scenario where denied access email is triggered @Test public void testGetToken_SendDeniedAccessEmail() throws Exception { setupDeniedEmailScenario(); - this.authenticationService.setDeniedEmailEnabled("true"); + this.authenticationService.setDeniedEmailEnabled(true); try { - authenticationService.getToken(authRequest); + authenticationService.authenticate(authRequest, "localhost"); } catch (Exception e) { verify(basicMailService).sendDeniedAccessEmail(any()); } } private void setupSuccessfulTokenRetrievalScenario() throws IOException { - this.authenticationService.setDeniedEmailEnabled("false"); + this.authenticationService.setDeniedEmailEnabled(false); JsonNode mockUserInfo = mock(JsonNode.class); when(mockUserInfo.get("user_id")).thenReturn(mock(JsonNode.class)); when(mockUserInfo.get("user_id").asText()).thenReturn(userId); diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationServiceTest.java index ca04f97ea..2183fb665 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authentication/OpenAuthenticationServiceTest.java @@ -4,10 +4,9 @@ import edu.harvard.hms.dbmi.avillach.auth.entity.User; 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.authorization.AccessRuleService; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import org.junit.Before; import org.junit.Test; -import org.mockito.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -29,12 +28,13 @@ public class OpenAuthenticationServiceTest { @Mock private AccessRuleService accessRuleService; - @InjectMocks private OpenAuthenticationService openAuthenticationService; @Before public void setUp() { MockitoAnnotations.initMocks(this); + + openAuthenticationService = new OpenAuthenticationService(userService, roleService, accessRuleService, true); } @Test @@ -53,7 +53,7 @@ public void testAuthenticate_ValidUUID() { when(userService.findUserByUUID(uuid.toString())).thenReturn(user); when(userService.getUserProfileResponse(any(Map.class))).thenReturn(claims); - Map authenticate = openAuthenticationService.authenticate(authRequest); + Map authenticate = openAuthenticationService.authenticate(authRequest, "localhost"); verify(userService, never()).createOpenAccessUser(any(Role.class)); verify(userService).findUserByUUID(uuid.toString()); verify(userService).getUserProfileResponse(any(Map.class)); @@ -75,7 +75,7 @@ public void testAuthenticate_InvalidUUID() { when(userService.createOpenAccessUser(any(Role.class))).thenReturn(createUser(UUID.randomUUID())); when(userService.getUserProfileResponse(any(Map.class))).thenReturn(new HashMap<>()); - Map authenticate = openAuthenticationService.authenticate(authRequest); + Map authenticate = openAuthenticationService.authenticate(authRequest, "localhost"); assertNotNull(authenticate); assertEquals(0, authenticate.size()); } @@ -88,7 +88,7 @@ public void testAuthenticate_NoUUID() { when(userService.createOpenAccessUser(any())).thenReturn(createUser(UUID.randomUUID())); when(userService.getUserProfileResponse(any(Map.class))).thenReturn(new HashMap<>()); - Map authenticate = openAuthenticationService.authenticate(authRequest); + Map authenticate = openAuthenticationService.authenticate(authRequest, "localhost"); assertNotNull(authenticate); verify(userService).createOpenAccessUser(any(Role.class)); diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleServiceTest.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleServiceTest.java index 7594ca4c5..5cd22f2b4 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleServiceTest.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AccessRuleServiceTest.java @@ -2,6 +2,7 @@ import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import org.junit.After; import org.junit.Assert; import org.junit.Before; 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 4410dc563..8a46e3954 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 @@ -5,6 +5,7 @@ import edu.harvard.hms.dbmi.avillach.auth.enums.SecurityRoles; import edu.harvard.hms.dbmi.avillach.auth.model.CustomUserDetails; import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import edu.harvard.hms.dbmi.avillach.auth.utils.AuthNaming; import org.junit.Assert; import org.junit.Before; @@ -311,7 +312,7 @@ public void setUp() { MockitoAnnotations.initMocks(this); SecurityContextHolder.setContext(securityContext); - accessRuleService = new AccessRuleService(accessRuleRepository); + accessRuleService = new AccessRuleService(accessRuleRepository, "false", "false", "false", "false","false", "false"); authorizationService = new AuthorizationService(accessRuleService, "fence,okta"); } diff --git a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTestByUseCases.java b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTestByUseCases.java index 60af75dda..34931dd89 100644 --- a/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTestByUseCases.java +++ b/pic-sure-auth-services/src/test/java/edu/harvard/hms/dbmi/avillach/auth/service/impl/authorization/AuthorizationServiceTestByUseCases.java @@ -3,6 +3,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import edu.harvard.hms.dbmi.avillach.auth.entity.AccessRule; import edu.harvard.hms.dbmi.avillach.auth.repository.AccessRuleRepository; +import edu.harvard.hms.dbmi.avillach.auth.service.impl.AccessRuleService; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -418,7 +419,7 @@ public static void init() { @Before public void setUp() { AccessRuleRepository accessRuleRepository = Mockito.mock(AccessRuleRepository.class); - accessRuleService = new AccessRuleService(accessRuleRepository); + accessRuleService = new AccessRuleService(accessRuleRepository, "false", "false", "false", "false","false", "false"); } @Test diff --git a/scripts/build_images.sh b/scripts/build_images.sh deleted file mode 100755 index 6419c5076..000000000 --- a/scripts/build_images.sh +++ /dev/null @@ -1,65 +0,0 @@ -#!/bin/sh -logger() { - MSG=$* - TIMESTAMP=`date +%c` - echo "${TIMESTAMP} ${MSG}" -} -# -# This script will build psama image to be pushed to the Docker hub, from the local depository. -# This script will NOT push the generated images automatically. -# This script will tag the generated images as: -# "dbmi/pic-sure-auth-services:${GITHUB_BRANCH}_${GIT_COMMIT_HASH}" -# for the PSAMA back-end image -# -# Currently, the script requires Java 11 (11.0.2-open) and Maven to be -# installed on the machine that is executing the build process. - -# Check if Java is 11.0.2 -CURRENT_JAVA_VERSION=$(java --version | head -1 | cut -d " " -f 2) -if [ "${CURRENT_JAVA_VERSION}" != "11.0.2" ]; -then - logger "Incorrect Java version. It is ${CURRENT_JAVA_VERSION}, but it should be 11.0.2." - logger "Cannot proceed with docker image build." - exit 255 -else - logger "Current Java version has been verified. Proceed with Maven build" -fi - -# TODO: Could use a check on the docker version and wether it is running or not. - -# Do the build from the root directory of the repo -cd .. -# Just in case this is built on a Mac -find ./ -name ".DS_Store" -exec rm -f {} \; 2>/dev/null - -mvn clean install -MAVEN_COMPLETION_STATUS=$? - -# The maven build will generate the PSAMA back-end .war file -if [ $MAVEN_COMPLETION_STATUS -eq 0 ]; -then - logger "Building .war file was successful." - find ./ -name "*.war" -ls -else - logger "Error building .war files." -fi - -# Build the PSAMA back-end docker image. -# sub-task 1., Get the current GitHub branch and commit hash -GITHUB_BRANCH=$(git branch | grep "*" | cut -d " " -f 2) -GITHUB_COMMIT_HASH=$(git log | head -1 | cut -d " " -f 2 | cut -c 1-12) -cd pic-sure-auth-services -docker build . --rm --tag "dbmi/pic-sure-auth-services:${GITHUB_BRANCH}.${GITHUB_COMMIT_HASH}" -CMD_STATUS=$? -if [ $CMD_STATUS -eq 0 ]; -then - logger "Successfully built PSAMA back-end docker image dbmi/pic-sure-auth-services:${GITHUB_BRANCH}.${GITHUB_COMMIT_HASH} locally." -else - logger "Failed to build PSAMA back-end docker image locally." - exit 255 -fi -cd .. - -echo "Images:" -echo "dbmi/pic-sure-auth-services:${GITHUB_BRANCH}.${GITHUB_COMMIT_HASH}" -logger "Done." diff --git a/scripts/docker-compose.yml b/scripts/docker-compose.yml deleted file mode 100644 index 3108529db..000000000 --- a/scripts/docker-compose.yml +++ /dev/null @@ -1,53 +0,0 @@ -version: '3' -services: - - picsureauth: - build: - context: pic-sure-auth-services - dockerfile: Dockerfile - command: --debug *:8787 - depends_on: - - picsureauthdb - environment: - - AUTH_CLIENT_SECRET=${AUTH_CLIENT_SECRET:-secret} - - AUTH_CLIENT_ID=${AUTH_CLIENT_ID:-client_id} - - AUTH_USER_ID_CLAIM=sub - - AUTH_MYSQLADDRESS=picsureauthdb - - AUTH0TOKEN=${AUTH0TOKEN:-nothing} - - AUTH0HOST=https://avillachlab.auth0.com - - systemName=${systemName:-PICSURE} - - EMAIL_TEMPLATE_PATH=${EMAIL_TEMPLATE_PATH:-/opt/jboss/wildfly/emailTemplates}/ - - DENIED_EMAIL_ENABLED=${DENIED_EMAIL_ENABLED:-false} - - COMMA_SEPARATED_EMAILS=${COMMA_SEPARATED_EMAILS:-dummyemail} - - accessGrantEmailSubject=Testing - - MAIL_USERNAME=${MAIL_USERNAME:-emailuser@avillach.lab} - - MAIL_PASSWORD=${MAIL_PASSWORD:-email_password} - - TOS_ENABLED:false - ports: - - 8787:8787 - expose: - - 8080 - volumes: - - ./pic-sure-auth-services/src/main/resources/emailTemplates:${EMAIL_TEMPLATE_PATH:-/opt/jboss/wildfly/emailTemplates} - networks: - - public - env_file: - - .env - - picsureauthdb: - build: - context: pic-sure-auth-db - dockerfile: Dockerfile - environment: - - MYSQL_ROOT_PASSWORD=${AUTH_MYSQL_ROOT_PASSWORD:-password} - - MYSQL_DATABASE=auth - restart: always - expose: - - 3306 - ports: - - 3306:3306 - networks: - - public - -networks: - public: diff --git a/scripts/fence-deployment/db/Dockerfile b/scripts/fence-deployment/db/Dockerfile deleted file mode 100644 index 4b292e6d4..000000000 --- a/scripts/fence-deployment/db/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM mysql:5.7 - -COPY fence_db_setup.sql /tmp/fence_db_setup.sql - -EXPOSE 3306 - -ENTRYPOINT ["docker-entrypoint.sh"] -CMD ["mysqld","--init-file","/tmp/fence_db_setup.sql"] diff --git a/scripts/fence-deployment/db/fence_db_setup.sql b/scripts/fence-deployment/db/fence_db_setup.sql deleted file mode 100644 index 9da53a517..000000000 --- a/scripts/fence-deployment/db/fence_db_setup.sql +++ /dev/null @@ -1,366 +0,0 @@ -GRANT ALL PRIVILEGES ON *.* TO 'root'@'%' IDENTIFIED BY 'password'; - -DROP DATABASE IF EXISTS `auth`; -CREATE DATABASE IF NOT EXISTS `auth` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */; -USE `auth`; --- MySQL dump 10.13 Distrib 5.7.17, for macos10.12 (x86_64) --- --- Host: 127.0.0.1 Database: auth --- ------------------------------------------------------ --- Server version 5.7.20 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `privilege` --- - --- DROP TABLE IF EXISTS `privilege`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `privilege` ( - `uuid` binary(16) NOT NULL, - `description` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `application_id` binary(16) DEFAULT NULL, - `queryTemplate` varchar(8192) DEFAULT NULL, - `queryScope` varchar(512) DEFAULT NULL, - PRIMARY KEY (`uuid`), - UNIQUE KEY `UK_h7iwbdg4ev8mgvmij76881tx8` (`name`), - KEY `FK61h3jewffk70b5ni4tsi5rhoy` (`application_id`), - CONSTRAINT `FK61h3jewffk70b5ni4tsi5rhoy` FOREIGN KEY (`application_id`) REFERENCES `application` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `role` --- - --- DROP TABLE IF EXISTS `role`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role` ( - `uuid` binary(16) NOT NULL, - `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `description` varchar(255) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `role_privilege` --- - --- DROP TABLE IF EXISTS `role_privilege`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `role_privilege` ( - `role_id` binary(16) NOT NULL, - `privilege_id` binary(16) NOT NULL, - PRIMARY KEY (`role_id`,`privilege_id`), - KEY `FKdkwbrwb5r8h74m1v7dqmhp99c` (`privilege_id`), - CONSTRAINT `FKdkwbrwb5r8h74m1v7dqmhp99c` FOREIGN KEY (`privilege_id`) REFERENCES `privilege` (`uuid`), - CONSTRAINT `FKsykrtrdngu5iexmbti7lu9xa` FOREIGN KEY (`role_id`) REFERENCES `role` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `user` --- - --- DROP TABLE IF EXISTS `user`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user` ( - `uuid` binary(16) NOT NULL, - `auth0_metadata` varchar(8000) COLLATE utf8_bin DEFAULT NULL, - `general_metadata` varchar(9000) COLLATE utf8_bin DEFAULT NULL, - `acceptedTOS` datetime COLLATE utf8_bin DEFAULT NULL, - `connectionId` binary(16) DEFAULT NULL, - `email` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `matched` bit(1) NOT NULL DEFAULT FALSE, - `subject` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `is_active` bit(1) NOT NULL DEFAULT TRUE, - `long_term_token` varchar(4000) COLLATE utf8_bin DEFAULT NULL, - `isGateAnyRelation` bit(1) NOT NULL DEFAULT TRUE, - PRIMARY KEY (`uuid`), - UNIQUE KEY `UK_r8xpakluitn685ua7pt8xjy9r` (`subject`), - KEY `FKn8bku0vydfcnuwbqwgnbgg8ry` (`connectionId`), - CONSTRAINT `FKn8bku0vydfcnuwbqwgnbgg8ry` FOREIGN KEY (`connectionId`) REFERENCES `connection` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `userMetadataMapping` --- - --- DROP TABLE IF EXISTS `userMetadataMapping`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `userMetadataMapping` ( - `uuid` binary(16) NOT NULL, - `auth0MetadataJsonPath` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `connectionId` binary(16) DEFAULT NULL, - `generalMetadataJsonPath` varchar(255) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`), - KEY `FKayr8vrvvwpgsdhxdyryt6k590` (`connectionId`), - CONSTRAINT `FKayr8vrvvwpgsdhxdyryt6k590` FOREIGN KEY (`connectionId`) REFERENCES `connection` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `user_role` --- - --- DROP TABLE IF EXISTS `user_role`; -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user_role` ( - `user_id` binary(16) NOT NULL, - `role_id` binary(16) NOT NULL, - PRIMARY KEY (`user_id`,`role_id`), - KEY `FKa68196081fvovjhkek5m97n3y` (`role_id`), - CONSTRAINT `FK859n2jvi8ivhui0rl0esws6o` FOREIGN KEY (`user_id`) REFERENCES `user` (`uuid`), - CONSTRAINT `FKa68196081fvovjhkek5m97n3y` FOREIGN KEY (`role_id`) REFERENCES `role` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -CREATE TABLE `termsOfService` ( - `uuid` binary(16) NOT NULL, - `dateUpdated` timestamp, - `content` varchar(9000) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `connection` ( - `uuid` binary(16) NOT NULL, - `label` varchar(255) COLLATE utf8_bin NOT NULL, - `id` varchar(255) COLLATE utf8_bin NOT NULL, - `subprefix` varchar(255) COLLATE utf8_bin NOT NULL, - `requiredFields` varchar(9000) COLLATE utf8_bin NOT NULL, - PRIMARY KEY (`uuid`), - UNIQUE KEY `id` (`id`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - -CREATE TABLE `application` ( - `uuid` binary(16) NOT NULL, - `description` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `enable` bit(1) NOT NULL DEFAULT b'1', - `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `token` varchar(2000) COLLATE utf8_bin DEFAULT NULL, - `url` varchar(500) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - -INSERT INTO `application` (`uuid`, `description`, `enable`, `name`, `token`, `url`) -VALUES - ( - X'8B5722C962FD48D6B0BF4F67E53EFB2B', - X'5049432D53555245206D756C7469706C6520646174612061636365737320415049', - 1, - X'50494353555245', - NULL, - X'2F706963737572657569' -); - - -CREATE TABLE `access_rule` ( - `uuid` binary(16) NOT NULL, - `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `description` varchar(2000) COLLATE utf8_bin DEFAULT NULL, - `rule` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `type` int(11) DEFAULT NULL, - `value` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `checkMapKeyOnly` bit(1) NOT NULL, - `checkMapNode` bit(1) NOT NULL, - `subAccessRuleParent_uuid` binary(16) DEFAULT NULL, - `isGateAnyRelation` bit(1) NOT NULL, - `isEvaluateOnlyByGates` bit(1) NOT NULL, - PRIMARY KEY (`uuid`), - KEY `FK8rovvx363ui99ce21sksmg6uy` (`subAccessRuleParent_uuid`), - CONSTRAINT `FK8rovvx363ui99ce21sksmg6uy` FOREIGN KEY (`subAccessRuleParent_uuid`) REFERENCES `access_rule` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - -CREATE TABLE `accessRule_privilege` ( - `privilege_id` binary(16) NOT NULL, - `accessRule_id` binary(16) NOT NULL, - PRIMARY KEY (`privilege_id`,`accessRule_id`), - KEY `FK89rf30kbf9d246jty2dd7qk99` (`accessRule_id`), - CONSTRAINT `FK7x47w81gpua380qd7lp9x94l1` FOREIGN KEY (`privilege_id`) REFERENCES `privilege` (`uuid`), - CONSTRAINT `FK89rf30kbf9d246jty2dd7qk99` FOREIGN KEY (`accessRule_id`) REFERENCES `access_rule` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - -CREATE TABLE `accessRule_gate` ( - `accessRule_id` binary(16) NOT NULL, - `gate_id` binary(16) NOT NULL, - PRIMARY KEY (`accessRule_id`,`gate_id`), - KEY `FK6re4kcq9tyl45jv9yg584doem` (`gate_id`), - CONSTRAINT `FK6re4kcq9tyl45jv9yg584doem` FOREIGN KEY (`gate_id`) REFERENCES `access_rule` (`uuid`), - CONSTRAINT `FKe6l5ee7f207958mm3anpsmqom` FOREIGN KEY (`accessRule_id`) REFERENCES `access_rule` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; - - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Add temporary config user - -SET @uuidUser = REPLACE(uuid(),'-',''); - -INSERT INTO user ( - `uuid`, - `general_metadata`, - `subject`, - `email` -) VALUES ( - unhex(@uuidUser), - '{"description":"Temporary user entry, for configuraiton"}', - 'configurator|temporary_account', - 'configurator@avillach.lab' -); - -# Add the initial ADMIN role for the user. -# Assuming, that all superuser privileges have been -# assigned to this role, already, during creation -# of the database. -INSERT INTO user_role ( - `user_id`, - `role_id` -) VALUES ( - UNHEX(@uuidUser), - (SELECT uuid FROM role WHERE name = 'PIC-SURE Top Admin') -); - - -DROP DATABASE IF EXISTS `picsure`; -CREATE DATABASE IF NOT EXISTS `picsure` /*!40100 DEFAULT CHARACTER SET utf8 COLLATE utf8_bin */; -USE `picsure`; --- MySQL dump 10.13 Distrib 5.7.17, for macos10.12 (x86_64) --- --- Host: 127.0.0.1 Database: picsure --- ------------------------------------------------------ --- Server version 5.7.20 - -/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; -/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; -/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; -/*!40101 SET NAMES utf8 */; -/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; -/*!40103 SET TIME_ZONE='+00:00' */; -/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; -/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; -/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; -/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; - --- --- Table structure for table `query` --- - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `query` ( - `uuid` binary(16) NOT NULL, - `query` longtext COLLATE utf8_bin, - `readyTime` date DEFAULT NULL, - `resourceResultId` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `startTime` date DEFAULT NULL, - `status` int(11) DEFAULT NULL, - `resourceId` binary(16), - `metadata` blob, - PRIMARY KEY (`uuid`), - KEY `FKhgiwd8kmi6pjw16txfhyqk2w0` (`resourceId`), - CONSTRAINT `FKhgiwd8kmi6pjw16txfhyqk2w0` FOREIGN KEY (`resourceId`) REFERENCES `resource` (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `resource` --- - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `resource` ( - `uuid` binary(16) NOT NULL, - `targetURL` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `resourceRSPath` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `description` varchar(8192) COLLATE utf8_bin DEFAULT NULL, - `name` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `token` varchar(8192) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; - --- --- Table structure for table `user` --- - -/*!40101 SET @saved_cs_client = @@character_set_client */; -/*!40101 SET character_set_client = utf8 */; -CREATE TABLE `user` ( - `uuid` binary(16) NOT NULL, - `roles` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `subject` varchar(255) COLLATE utf8_bin DEFAULT NULL, - `userId` varchar(255) COLLATE utf8_bin DEFAULT NULL, - PRIMARY KEY (`uuid`), - UNIQUE KEY `subject_UNIQUE` (`subject`), - UNIQUE KEY `userId_UNIQUE` (`userId`) -) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin; -/*!40101 SET character_set_client = @saved_cs_client */; -/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; - -/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; -/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; -/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; -/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; -/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; -/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; -/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; - --- Dump completed on 2018-05-24 16:46:11 - -START TRANSACTION; - -DELETE FROM `resource` WHERE `name` = 'hpds'; - -SET @uuidResource = REPLACE('02e23f52-f354-4e8b-992c-d37c8b9ba140','-',''); - -INSERT INTO `resource` ( - `uuid`, - `targetURL`, - `resourceRSPath`, - `description`, - `name`, - `token` -) VALUES ( - unhex(@uuidResource), - NULL, - 'http://localhost:8881/hpds', - 'Basic HPDS resource', - 'hpds', - NULL -); - -COMMIT; - - - - - diff --git a/scripts/fence-deployment/docker-compose.yml b/scripts/fence-deployment/docker-compose.yml deleted file mode 100644 index ac7d78043..000000000 --- a/scripts/fence-deployment/docker-compose.yml +++ /dev/null @@ -1,159 +0,0 @@ -version: '3.3' -services: - - httpd: - build: - context: ./httpd - dockerfile: Dockerfile - ports: - - 80:80 - - 443:443 - depends_on: - - psama - - picsure - volumes: - - ./httpd/psamaui_settings.json:/usr/local/apache2/htdocs/psamaui/settings/settings.json - - ./httpd/psamaui_settings.json:/usr/local/apache2/htdocs/picsureui/settings/settings.json - networks: - - public - - picsure: - image: jboss/wildfly:17.0.0.Final - environment: - - JAVA_OPTS=-Xms1024m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true - restart: always - environment: - - PICSURE_CLIENT_SECRET=nQWNixJHJ_jRWd6ZuFY9XJNdt9-gDvqBkpN9b80qHn7ySpCUfTdwIm0F85UZgbB4 - - PICSURE_USER_ID_CLAIM=sub - - RESULT_FORMAT=JSON - - IRCT_TARGET_URL=http://httpd/irct - - PICSURE_DB_HOST=db - - PICSURE_DB_USERNAME=root - - PICSURE_DB_PASSWORD=password - - PICSURE_DB_NAME=picsure - - PICSURE_INTROSPECTION_URL=http://psama:8080/pic-sure-auth-services/auth/token/inspect - - PICSURE_INTROSPECTION_TOKEN=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJQU0FNQV9BUFBMSUNBVElPTnxtaXNzaW5nUGljU3VyZUFwcFVVSUQiLCJ1c2VyX2lkIjoiUFNBTUFfQVBQTElDQVRJT058UElDU1VSRSIsImV4cCI6MTU3MTEyMDQxNywiaWF0IjoxNTcwMjIwNDE3fQ.8a6XEJBHsb6uAWBdU1MddM5NuAHPMMl_mhQ7h42gV0E - expose: - - 8080 - networks: - - public - entrypoint: /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug 0.0.0.0:8787 - user: root - volumes: - - ./picsure/standalone.xml:/opt/jboss/wildfly/standalone/configuration/standalone.xml - - ./picsure/modules/system/layers/base/com/sql/:/opt/jboss/wildfly/modules/system/layers/base/com/sql/ - - ./picsure/deployments:/opt/jboss/wildfly/standalone/deployments - depends_on: - - db - - copy-pic-sure-backend-war - - copy-pic-sure-resource-war - - psama: - image: jboss/wildfly:17.0.0.Final - environment: - - JAVA_OPTS=-Xms1024m -Xmx2g -XX:MetaspaceSize=96M -XX:MaxMetaspaceSize=256m -Djava.net.preferIPv4Stack=true - restart: always - environment: - - SYSTEM_NAME=DataStageDev2 - - AUTH_CLIENT_ID=ywAq4Xu4Kl3uYNdm3m05Cc5ow0OibvXt - - AUTH_CLIENT_SECRET=nQWNixJHJ_jRWd6ZuFY9XJNdt9-gDvqBkpN9b80qHn7ySpCUfTdwIm0F85UZgbB4 - - PSAMA_DB_HOST=db - - PSAMA_DB_NAME=auth - - PSAMA_DB_USERNAME=root - - PSAMA_DB_PASSWORD=password - - IDP_PROVIDER=fence - - IDP_PROVIDER_URI=https://staging.datastage.io - - FENCE_CLIENT_ID=3YkHUAoPSwaRWzSuNN0DyDbJeU1AxrMVkXBczDo6 - - FENCE_CLIENT_SECRET=W7JGecNQ91fMFRb0YVTRnqJ6fytPK4FIK2ZCsAbiQMbHaoTENHGzLFD - - EMAIL_TEMPLATE_PATH=/tmp/config/emailTemplates/ - - MAIL_USERNAME=gkorodi@gmail.com - - MAIL_PASSWORD=Trump!2020 - expose: - - 8080 - - 8787 - ports: - - 8787:8787 - - 8080:8080 - networks: - - public - entrypoint: /opt/jboss/wildfly/bin/standalone.sh -b 0.0.0.0 -bmanagement 0.0.0.0 --debug 0.0.0.0:8787 - user: root - volumes: - - ./psama/standalone.xml:/opt/jboss/wildfly/standalone/configuration/standalone.xml - - ./psama/modules/system/layers/base/com/sql/:/opt/jboss/wildfly/modules/system/layers/base/com/sql/ - - ./psama/modules/system/layers/base/com/oracle/:/opt/jboss/wildfly/modules/system/layers/base/com/oracle/ - - ./psama/emailTemplates/:/tmp/config/emailTemplates/ - - ./psama/deployments:/opt/jboss/wildfly/standalone/deployments - depends_on: - - db - - copy-pic-sure-auth-war - - copy-pic-sure-auth-war: - image: dbmi/pic-sure-auth-services:fence-integration_72ef997e3346 - user: root - volumes: - - ./wildfly/deployments:/var/tmp - entrypoint: cp /opt/jboss/wildfly/standalone/deployments/pic-sure-auth-services.war /var/tmp/pic-sure-auth-services.war - networks: - - public - - copy-pic-sure-backend-war: - image: dbmi/picsure2:master.7a9b0c5 - user: root - volumes: - - ./picsure/deployments:/var/tmp - entrypoint: cp /opt/jboss/wildfly/standalone/deployments/pic-sure-api-2.war /var/tmp/pic-sure-api-2.war - networks: - - public - - copy-pic-sure-resource-war: - image: dbmi/pic-sure-irct-resource:picsure310.ef69613 - user: root - volumes: - - ./picsure/deployments:/var/tmp - entrypoint: cp /opt/jboss/wildfly/standalone/deployments/pic-sure-irct-resource.war /var/tmp/pic-sure-irct-resource.war - networks: - - public - -# hpds: -# image: dbmi/pic-sure-hpds:master_fbf04e7 -# entrypoint: java -Xdebug -Xrunjdwp:transport=dt_socket,address=0.0.0.0:8000,server=y,suspend=n -XX:+UseParallelGC -XX:SurvivorRatio=250 -Xms1g -Xmx2g -server -jar hpds.jar -httpPort 8080 -DCACHE_SIZE=10 -DSMALL_TASK_THREADS=1 -DLARGE_TASK_THREADS=1 -DSMALL_JOB_LIMIT=100 -DID_BATCH_SIZE=2000 -# environment: -# - CACHESIZE=500 -# - HEAPSIZE=2048 -# - ID_BATCH_SIZE=50000 -# - LARGE_TASK_THREADS=1 -# - SMALL_JOB_LIMIT=100 -# - SMALL_TASK_THREADS=1 -# - SURVIVOR_RATIO=255 -# volumes: -# - /scratch/hpds_symlink/:/opt/local/phenocube -# - /scratch/hpds_symlink/:/opt/local/hpds -# - /scratch/hpds_symlink/:/opt/local/hpds/variants -# - /scratch/hpds_symlink/:/opt/local/hpds/all -# restart: always -# networks: -# - public -# expose: -# - 8080 -# ports: -# - 8080:8080 - - db: - build: - context: db - dockerfile: Dockerfile - image: mysql - environment: - - MYSQL_ROOT_PASSWORD=${AUTH_MYSQL_ROOT_PASSWORD:-password} - - MYSQL_DATABASE=auth - restart: always - expose: - - 3306 - ports: - - 3306:3306 - networks: - - public - -networks: - public: diff --git a/scripts/fence-deployment/fence.sh b/scripts/fence-deployment/fence.sh deleted file mode 100755 index 0e4a1ef9f..000000000 --- a/scripts/fence-deployment/fence.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/sh - -export REF_REPO_DIR=/Users/gabor/pic-sure-i2b2-transmart - -config() { - # Create directory for HPDS container - mkdir -p /scratch/hpds_symlink/ -} - -build() { - # Build HTTPD container, with settings and directory structure - docker-compose build -} - -up() { - # Start up all containers - docker-compose up -d -} - -down() { - docker-compose down -} - -logs() { - docker-compose logs $* -} - -ps() { - docker-compose ps -} - -rebuild() { - docker-compose down - #docker system prune --force --all --volumes - docker-compose pull - docker-compose build - docker-compose up -d -} - -$* diff --git a/scripts/fence-deployment/httpd/Dockerfile b/scripts/fence-deployment/httpd/Dockerfile deleted file mode 100644 index 6c47bbc72..000000000 --- a/scripts/fence-deployment/httpd/Dockerfile +++ /dev/null @@ -1,34 +0,0 @@ -FROM dbmi/pic-sure-hpds-ui:master.0800720 AS picsureui -FROM dbmi/pic-sure-auth-ui:fence-integration_72ef997e3346 AS psamaui -FROM httpd:2.4.27-alpine - -RUN apk add --update openssl sed curl jq python3 -RUN pip3 install --upgrade pip && pip install PyJWT - -# Replace virtual host config file with ours -COPY httpd-vhosts.conf ${HTTPD_PREFIX}/conf/extra/httpd-vhosts.conf - -# Enable virtual hosting config file -RUN sed -i '/^#Include conf.extra.httpd-vhosts.conf/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf - -# Enable necessary proxy modules -RUN sed -i '/^#LoadModule proxy_module/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf -RUN sed -i '/^#LoadModule proxy_http_module/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf -RUN sed -i '/^#LoadModule proxy_connect_module/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf - -#### SSL #### -# enable ssl -RUN sed -i '/^#LoadModule ssl_module modules\/mod_ssl.so/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf -RUN sed -i '/^#LoadModule rewrite_module modules\/mod_rewrite.so/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf -RUN sed -i '/^#LoadModule socache_shmcb_module modules\/mod_socache_shmcb.so/s/^#//' ${HTTPD_PREFIX}/conf/httpd.conf -RUN mkdir /usr/local/apache2/logs/ssl_mutex - -COPY ./cert/server.key ${HTTPD_PREFIX}/cert/ -COPY ./cert/server.chain.crt ${HTTPD_PREFIX}/cert/ -COPY ./cert/server.crt ${HTTPD_PREFIX}/cert/ -COPY ./cert/server.chain ${HTTPD_PREFIX}/cert/ - -COPY --from=psamaui /usr/local/apache2/htdocs ${HTTPD_PREFIX}/htdocs -COPY --from=picsureui /usr/local/apache2/htdocs ${HTTPD_PREFIX}/htdocs - -COPY index.html ${HTTPD_PREFIX}/htdocs/ diff --git a/scripts/fence-deployment/httpd/cert/server.chain b/scripts/fence-deployment/httpd/cert/server.chain deleted file mode 100644 index 0d9f49e20..000000000 --- a/scripts/fence-deployment/httpd/cert/server.chain +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICpDCCAYwCCQCgfYbxmSm3hjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMTkwMzI5MTg1NDA0WhcNMjAwMzI4MTg1NDA0WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw -i3MoGmKL3EUk2en4bx1YSUR9ZfOmhB65r9schwdfSf1gugJb9YrGPWt2wwBltXQu -JzZ75GtsgxtDPQZSwUrBxoUi0Aps0SU1yoErFRAT4ChYDDBpy0tMHNBpdCrH1Tk4 -oJV1BtTpW781BFvScx7TiAZucAnjxYNEdXToqtpiUP2IBATYc5eJMv9jrPSK2F8C -AohbodPXsLcEX6JtiRYk7hDoKm+Xy7+zz3go+9Tynv2AjM8IBRsZ2enyXBXVIC/q -k6dCzza4weMzikKQFgDXF+nDm9tFe8/rVXNPeAYek1osluTtjqzBYqffCAbLxR6N -fI392T+C3nRG+oVTkPbLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIFymuTGjusg -aUiACo7cr/QvCQN8f5m7CtZUZ4NnAb8B8mq/gUFY/bzKO7tbyZipZIdndRtJfrxC -1D3cV3ofVb3GrmFs1bbAYM1+qyaEnidZxly9Srbf8aoUgafnNXk6dRJGLRKIWN5V -3upVbCWUDqRC409bSk6sdcr0LbWUMF8dP8GeySV0MeyhM1jd4SVZzOQ3IS0JXeiQ -rn7C7TQmG4vcbHoaoTmgQh39iJRyxL9QyHqhAbfFLdN5bSyqOck7GaVhIFhhAl3y -qmprQVjRSlxfnDHq1PCGY+SPLW8QpUFP4abzPY7kJ+V/lBWyrFm3bwyFQIqN01zD -5Zg/Jyl24+Y= ------END CERTIFICATE----- diff --git a/scripts/fence-deployment/httpd/cert/server.chain.crt b/scripts/fence-deployment/httpd/cert/server.chain.crt deleted file mode 100644 index 6078724c0..000000000 --- a/scripts/fence-deployment/httpd/cert/server.chain.crt +++ /dev/null @@ -1 +0,0 @@ -PLACEHOLDER FILE, replace with real cert \ No newline at end of file diff --git a/scripts/fence-deployment/httpd/cert/server.crt b/scripts/fence-deployment/httpd/cert/server.crt deleted file mode 100644 index 0d9f49e20..000000000 --- a/scripts/fence-deployment/httpd/cert/server.crt +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN CERTIFICATE----- -MIICpDCCAYwCCQCgfYbxmSm3hjANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls -b2NhbGhvc3QwHhcNMTkwMzI5MTg1NDA0WhcNMjAwMzI4MTg1NDA0WjAUMRIwEAYD -VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCw -i3MoGmKL3EUk2en4bx1YSUR9ZfOmhB65r9schwdfSf1gugJb9YrGPWt2wwBltXQu -JzZ75GtsgxtDPQZSwUrBxoUi0Aps0SU1yoErFRAT4ChYDDBpy0tMHNBpdCrH1Tk4 -oJV1BtTpW781BFvScx7TiAZucAnjxYNEdXToqtpiUP2IBATYc5eJMv9jrPSK2F8C -AohbodPXsLcEX6JtiRYk7hDoKm+Xy7+zz3go+9Tynv2AjM8IBRsZ2enyXBXVIC/q -k6dCzza4weMzikKQFgDXF+nDm9tFe8/rVXNPeAYek1osluTtjqzBYqffCAbLxR6N -fI392T+C3nRG+oVTkPbLAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIFymuTGjusg -aUiACo7cr/QvCQN8f5m7CtZUZ4NnAb8B8mq/gUFY/bzKO7tbyZipZIdndRtJfrxC -1D3cV3ofVb3GrmFs1bbAYM1+qyaEnidZxly9Srbf8aoUgafnNXk6dRJGLRKIWN5V -3upVbCWUDqRC409bSk6sdcr0LbWUMF8dP8GeySV0MeyhM1jd4SVZzOQ3IS0JXeiQ -rn7C7TQmG4vcbHoaoTmgQh39iJRyxL9QyHqhAbfFLdN5bSyqOck7GaVhIFhhAl3y -qmprQVjRSlxfnDHq1PCGY+SPLW8QpUFP4abzPY7kJ+V/lBWyrFm3bwyFQIqN01zD -5Zg/Jyl24+Y= ------END CERTIFICATE----- diff --git a/scripts/fence-deployment/httpd/cert/server.key b/scripts/fence-deployment/httpd/cert/server.key deleted file mode 100644 index eeedd0a8d..000000000 --- a/scripts/fence-deployment/httpd/cert/server.key +++ /dev/null @@ -1,28 +0,0 @@ ------BEGIN PRIVATE KEY----- -MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCwi3MoGmKL3EUk -2en4bx1YSUR9ZfOmhB65r9schwdfSf1gugJb9YrGPWt2wwBltXQuJzZ75GtsgxtD -PQZSwUrBxoUi0Aps0SU1yoErFRAT4ChYDDBpy0tMHNBpdCrH1Tk4oJV1BtTpW781 -BFvScx7TiAZucAnjxYNEdXToqtpiUP2IBATYc5eJMv9jrPSK2F8CAohbodPXsLcE -X6JtiRYk7hDoKm+Xy7+zz3go+9Tynv2AjM8IBRsZ2enyXBXVIC/qk6dCzza4weMz -ikKQFgDXF+nDm9tFe8/rVXNPeAYek1osluTtjqzBYqffCAbLxR6NfI392T+C3nRG -+oVTkPbLAgMBAAECggEAcsUlRuPoPr4i4TMdTJmHvTZcZR0bSZxIkTSGwEP0AfmK -1A/4qqm03u1c6Gca4gQVlE9/twkm4PNWjN6mNrRcEh8pvBj9PgK1KwQL9uMJgbJO -5/Z8nro/qCpvPq77hM/UFEEpCFw5m78+TYwY2XZniuK6M594fm+Px7iIGR3BPPoP -WBquXLHb2dLRk6DmFzCKpnkgZDf8IbgS1I0094U5T9952YCUI1qwckIXA+hxzFf2 -Llf8opGJAvpvOl58lNFlbzLBSFtgIlJPcGx/X95RDa4TsDc0qwTixCPkjyxRMWom -k8ajMMbnTu0nD1o64Xe2iCR9ENyOTLId1ShPGRsRmQKBgQDbtK8bcvHBD1daRyVm -kn5UtqvIqMniT0K7ARM9YoVDSKt/vMVnifnIP3je6B8pSjNc1ng/lzsY3i/BjLSJ -Z7ElCp08GXQ8imarlwvhc+fwCNPiQHQokRnaJOjHwWWAtPTG1Z/4FOQfZZoDalCK -yGbb3Ti7BEr4N4tUPKUFTXO5ZQKBgQDNtX4ObolykMVl/lhT978+1Yo53XeFIEMH -8slM1GUUzJKXpyvFVwfxns3ve+gVMhlOMwkGFbC+pnp3M8ELL1J2snazfMan7Ivp -vYegvvBkmlT3Mm3VBl4p8sbR+uiMXDfPHtw0a1f5bP1nnW4J8733gj0AxVrG72nS -ooVmWKYEbwKBgFcC6PKjBDGkhMDhOmO0Eso0CjaO+l1hbJkpJNiDwylRh3IwbbHC -yCTRVmpZXbX5h/v1iGwO1b3UiO9LbViZs2NrH7hkkE9FtMGyMWdZgvriVB/fzOEg -DjABvBsYcb2WbPauNguHIo14rJU7rqg4E9xxcX1HhvVk8g4rcmN2OCWtAoGABWyD -pl3DwgGPMuFB8vTVVhLLUjtEq33uRodgR2ZSQ/og417FCK8CxpwpUecyd1yazjUK -R97KijxZfksfC+xzgC20c4cWtdbG2aLlsJdYP07SbrIlszg3w2NjWqYC+7ByyENI -CxnJeAonpFPCUwDaQWxtr1eEzToC0Er4uXoc5oECgYBz5wPLICW8rbVaONcXLmTK -Z5s1qruKWzDwXz7vPPyT7IJIoEteInnTbjkbE9Bec/FBJ6xq1inh/g0mZVVZ/IAw -bI6T0C+3HsEoxRFloNQhJcGnwNOqwZM0YTrHJPkWCiNt5oq97xwwjjPtXg3fGi8H -WQ/t6CL85CL3Ck1tFPH0YQ== ------END PRIVATE KEY----- diff --git a/scripts/fence-deployment/httpd/httpd-vhosts.conf b/scripts/fence-deployment/httpd/httpd-vhosts.conf deleted file mode 100644 index 07309bd05..000000000 --- a/scripts/fence-deployment/httpd/httpd-vhosts.conf +++ /dev/null @@ -1,79 +0,0 @@ -Listen 443 - -## -## SSL Global Context -## -## All SSL configuration in this context applies both to -## the main server and all SSL-enabled virtual hosts. -## - -# -# Some MIME-types for downloading Certificates and CRLs -# -AddType application/x-x509-ca-cert .crt -AddType application/x-pkcs7-crl .crl - -SSLCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES -SSLProxyCipherSuite HIGH:MEDIUM:!MD5:!RC4:!3DES - - -SSLHonorCipherOrder on - -SSLProtocol all -SSLv2 -SSLv3 -SSLProxyProtocol all -SSLv2 -SSLv3 -SSLPassPhraseDialog builtin - -SSLSessionCache "shmcb:${HTTPD_PREFIX}/logs/ssl_scache(512000)" -SSLSessionCacheTimeout 300 - -Mutex "file:${HTTPD_PREFIX}/logs/ssl_mutex" - - - ServerName localhost - RewriteEngine On - ProxyPreserveHost On - RewriteCond ${HTTPS} off [OR] - RewriteCond %{HTTP_HOST} ^(?:)?(.+)$ [NC] - RewriteRule ^ https://%1%{REQUEST_URI} [L,NE,R=301] - - - - ServerName localhost - - SSLProxyEngine On - SSLProxyCheckPeerCN off - - SSLCertificateFile "${HTTPD_PREFIX}/cert/server.crt" - SSLCertificateKeyFile "${HTTPD_PREFIX}/cert/server.key" - SSLCertificateChainFile "${HTTPD_PREFIX}/cert/server.chain" - - RewriteEngine On - ProxyPreserveHost On - - RewriteRule ^/static/(.*)$ /static/$1 [L] - - RewriteRule ^/picsure/(.*)$ "http://picsure:8080/pic-sure-api-2/PICSURE/$1" [P] - RewriteRule ^/picsure-irct/(.*)$ "http://picsure:8080/pic-sure-irct-resource/pic-sure/v1.4/$1" [P] - RewriteRule ^/psama/(.*)$ "http://psama:8080/pic-sure-auth-services/auth/$1" [P] - - RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-f - RewriteCond %{DOCUMENT_ROOT}/%{REQUEST_FILENAME} !-d - - RewriteRule ^/psamaui/(.*)$ /psamaui/index.html [C] - RewriteRule ^/picsureui/(.*)$ /picsureui/index.html [C] - - RewriteRule (.*) / [L] - ErrorDocument 404 /index.html - - DocumentRoot "${HTTPD_PREFIX}/htdocs" - - ErrorLog "${HTTPD_PREFIX}/logs/error_log" - TransferLog "${HTTPD_PREFIX}/logs/access_log" - CustomLog "${HTTPD_PREFIX}/logs/ssl_request_log" \ - "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" - - BrowserMatch "MSIE [2-5]" \ - nokeepalive ssl-unclean-shutdown \ - downgrade-1.0 force-response-1.0 - - diff --git a/scripts/fence-deployment/httpd/index.html b/scripts/fence-deployment/httpd/index.html deleted file mode 100644 index 773013e5a..000000000 --- a/scripts/fence-deployment/httpd/index.html +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - - If you are note redirected automatically, please click the below link, - to be redirected.
-
- PIC-SURE UI -
- - - diff --git a/scripts/fence-deployment/httpd/picsureui_settings.json b/scripts/fence-deployment/httpd/picsureui_settings.json deleted file mode 100644 index 28a52b1e2..000000000 --- a/scripts/fence-deployment/httpd/picsureui_settings.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "resources": [ - { - "id" : "datastage", - "name" : "datastage", - "basePath" : "/picsure", - "findPath" : "/PIC-SURE/search" - } - ], - "picSureResourceId":"02e23f52-f354-4e8b-992c-d37c8b9ba140", - "applicationIdForBaseQuery":"8b5722c9-62fd-48d6-b0bf-4f67e53efb2b", - "helpLink": "mailto:sample@email.com", - "advancedSearchLink": "/transmart/login/callback_processor" -} diff --git a/scripts/fence-deployment/httpd/psamaui_settings.json b/scripts/fence-deployment/httpd/psamaui_settings.json deleted file mode 100644 index b3f0d264b..000000000 --- a/scripts/fence-deployment/httpd/psamaui_settings.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "resources": [ - { - "id" : "datastage", - "name" : "datastage", - "basePath" : "/picsure", - "findPath" : "/PIC-SURE/search" - } - ], - "picSureResourceId":"02e23f52-f354-4e8b-992c-d37c8b9ba140", - "applicationIdForBaseQuery":"8b5722c9-62fd-48d6-b0bf-4f67e53efb2b", - "helpLink": "mailto:sample@email.com", - "client_id":"NA", - "auth0domain":"__PSAMA_AUTH_DOMAIN__", - "psamaServiceUrl":"/psama", - "customizeAuth0Login": true, - "basePath":"/psama", - "idp_provider": "fence", - "idp_provider_uri":"https://staging.datastage.io", - "fence_client_id": "3YkHUAoPSwaRWzSuNN0DyDbJeU1AxrMVkXBczDo6", - "fence_redirect_url": "https://datastage-i2b2-transmart-stage.aws.dbmi.hms.harvard.edu/psamaui/login/" -} diff --git a/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/module.xml b/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/module.xml deleted file mode 100644 index e7362db95..000000000 --- a/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/module.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/mysql-connector-java-5.1.38.jar b/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/mysql-connector-java-5.1.38.jar deleted file mode 100644 index be09493c0..000000000 Binary files a/scripts/fence-deployment/picsure/modules/system/layers/base/com/sql/mysql/main/mysql-connector-java-5.1.38.jar and /dev/null differ diff --git a/scripts/fence-deployment/picsure/standalone.xml b/scripts/fence-deployment/picsure/standalone.xml deleted file mode 100644 index b671a4555..000000000 --- a/scripts/fence-deployment/picsure/standalone.xml +++ /dev/null @@ -1,550 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE - h2 - - sa - sa - - - - - jdbc:mysql://${env.PICSURE_DB_HOST}/picsure?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&autoReconnectForPools=true - mysql - - 5 - 50 - true - - - ${env.PICSURE_DB_USERNAME} - ${env.PICSURE_DB_PASSWORD} - - - - SELECT 1 - true - false - - - - - - - com.mysql.jdbc.Driver - - - org.h2.jdbcx.JdbcDataSource - - - - - - - - - - false - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ${jboss.bind.address:127.0.0.1} - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/scripts/fence-deployment/psama/emailTemplates/accessEmail.mustache b/scripts/fence-deployment/psama/emailTemplates/accessEmail.mustache deleted file mode 100644 index 8b699bc69..000000000 --- a/scripts/fence-deployment/psama/emailTemplates/accessEmail.mustache +++ /dev/null @@ -1,17 +0,0 @@ -Access Changed: - -{{#rolesExists}} -You have been granted access to {{systemName}} with the following roles: -{{/rolesExists}} -{{#roles}} -{{name}} : {{#privileges}} {{description}} {{/privileges}} -{{/roles}} -{{^roles}} -Currently NO roles have been granted for you to {{systemName}}. -{{/roles}} - {{#documentation}} -You can find documentation at the following links: - {{.}} -{{/documentation}} - -Have a good day! \ No newline at end of file diff --git a/scripts/fence-deployment/psama/emailTemplates/deniedAccessEmail.mustache b/scripts/fence-deployment/psama/emailTemplates/deniedAccessEmail.mustache deleted file mode 100644 index abab886ba..000000000 --- a/scripts/fence-deployment/psama/emailTemplates/deniedAccessEmail.mustache +++ /dev/null @@ -1,6 +0,0 @@ -The following user has been denied access to the {{systemName}} environment because their user has not been configured. - -First Name : {{given_name}} -Last Name : {{family_name}} -Email : {{email}} -ID : {{nickname}}