From b68f5b3e1b2d00663ab341e0d20f643f100263ff Mon Sep 17 00:00:00 2001 From: Martin Weise <martin.weise@tuwien.ac.at> Date: Mon, 13 Mar 2023 22:48:20 +0100 Subject: [PATCH] Removed all the java code as it is unnecessary --- fda-authentication-service/.gitignore | 39 - .../.mvn/wrapper/MavenWrapperDownloader.java | 118 -- .../.mvn/wrapper/maven-wrapper.jar | Bin 50710 -> 0 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 - fda-authentication-service/Dockerfile | 52 +- fda-authentication-service/README.md | 31 - .../dbrepo-keycloak-realm.json | 0 fda-authentication-service/dbrepo-realm.json | 1848 +++++++++++++++++ .../docker-entrypoint.sh | 5 - fda-authentication-service/mvnw | 310 --- fda-authentication-service/mvnw.cmd | 182 -- fda-authentication-service/pom.xml | 223 -- fda-authentication-service/report/pom.xml | 56 - .../rest-service/pom.xml | 44 - .../FdaAuthenticationServiceApplication.java | 17 - .../java/at/tuwien/config/ReadyConfig.java | 25 - .../java/at/tuwien/config/SwaggerConfig.java | 45 - .../endpoints/AuthenticationEndpoint.java | 87 - .../tuwien/endpoints/TimeSecretEndpoint.java | 93 - .../at/tuwien/endpoints/TokenEndpoint.java | 111 - .../at/tuwien/endpoints/UserEndpoint.java | 267 --- .../tuwien/handlers/ApiExceptionHandler.java | 204 -- .../src/main/resources/application-docker.yml | 62 - .../src/main/resources/application-local.yml | 66 - .../src/main/resources/application.yml | 65 - .../templates/mail-password-changed.txt | 13 - .../templates/mail-request-password-reset.txt | 13 - .../resources/templates/mail-verify-email.txt | 15 - .../main/resources/templates/mail-welcome.txt | 17 - .../src/test/java/at/tuwien/BaseUnitTest.java | 358 ---- .../at/tuwien/auth/AuthTokenFilterTest.java | 112 - .../java/at/tuwien/auth/JwtUtilsTest.java | 34 - .../java/at/tuwien/config/DockerConfig.java | 147 -- .../test/java/at/tuwien/config/H2Utils.java | 37 - .../java/at/tuwien/config/RabbitMqConfig.java | 85 - .../java/at/tuwien/dto/AmqpUserBriefDto.java | 17 - .../AuthenticationEndpointUnitTest.java | 417 ---- .../endpoint/TimeSecretEndpointUnitTest.java | 240 --- .../endpoint/TokenEndpointUnitTest.java | 310 --- .../tuwien/endpoint/UserEndpointUnitTest.java | 992 --------- .../gateway/BrokerServiceGatewayUnitTest.java | 180 -- .../handlers/ApiExceptionHandlerTest.java | 50 - .../AuthenticationServiceIntegrationTest.java | 89 - .../AuthenticationServiceUnitTest.java | 45 - .../tuwien/service/MailServiceUnitTest.java | 64 - .../service/QueueServiceIntegrationTest.java | 114 - .../at/tuwien/service/TimeSecretUnitTest.java | 60 - .../tuwien/service/TokenIntegrationTest.java | 89 - .../service/UserServiceIntegrationTest.java | 240 --- .../tuwien/service/UserServiceUnitTest.java | 257 --- .../src/test/resources/application.properties | 30 - .../src/test/resources/schema.sql | 20 - .../templates/mail-password-changed.txt | 13 - .../templates/mail-request-password-reset.txt | 13 - .../resources/templates/mail-verify-email.txt | 15 - .../test/resources/templates/mail-welcome.txt | 17 - .../rest-service/src/test/resources/view.sql | 5 - fda-authentication-service/service_ready | 6 - fda-authentication-service/services/pom.xml | 42 - .../java/at/tuwien/auth/AuthEntrypoint.java | 23 - .../java/at/tuwien/auth/AuthTokenFilter.java | 70 - .../main/java/at/tuwien/auth/JwtUtils.java | 58 - .../java/at/tuwien/auth/MariaDbPassword.java | 24 - .../tuwien/auth/UserPermissionEvaluator.java | 50 - .../java/at/tuwien/config/AmqpConfig.java | 20 - .../tuwien/config/AuthenticationConfig.java | 21 - .../java/at/tuwien/config/GatewayConfig.java | 27 - .../java/at/tuwien/config/JacksonConfig.java | 31 - .../java/at/tuwien/config/MailConfig.java | 54 - .../java/at/tuwien/config/SecurityConfig.java | 25 - .../at/tuwien/config/WebSecurityConfig.java | 123 -- .../AuthenticationInvalidException.java | 20 - .../AuthenticationMalformedException.java | 20 - .../BrokerUserCreationException.java | 20 - .../tuwien/exception/NotAllowedException.java | 21 - .../exception/OrcidMalformedException.java | 20 - .../exception/RoleNotFoundException.java | 20 - .../tuwien/exception/RoleUniqueException.java | 20 - .../exception/SecretInvalidException.java | 20 - .../exception/TokenNotEligableException.java | 20 - .../exception/TokenNotFoundException.java | 20 - .../exception/TokenRevokedException.java | 20 - .../UserEmailAlreadyVerifiedException.java | 20 - .../exception/UserEmailExistsException.java | 20 - .../exception/UserEmailFailedException.java | 20 - .../UserEmailNotVerifiedException.java | 20 - .../exception/UserNameExistsException.java | 20 - .../exception/UserNotFoundException.java | 20 - .../tuwien/gateway/BrokerServiceGateway.java | 40 - .../impl/BrokerServiceGatewayImpl.java | 109 - .../java/at/tuwien/mapper/AmqpMapper.java | 19 - .../tuwien/mapper/AuthenticationMapper.java | 22 - .../java/at/tuwien/mapper/UserMapper.java | 130 -- .../tuwien/repositories/ImageRepository.java | 9 - .../repositories/TimeSecretRepository.java | 14 - .../tuwien/repositories/TokenRepository.java | 23 - .../tuwien/repositories/UserRepository.java | 18 - .../tuwien/service/AuthenticationService.java | 46 - .../java/at/tuwien/service/MailService.java | 21 - .../java/at/tuwien/service/QueueService.java | 32 - .../at/tuwien/service/TimeSecretService.java | 34 - .../java/at/tuwien/service/TokenService.java | 70 - .../java/at/tuwien/service/UserService.java | 117 -- .../impl/AuthenticationServiceImpl.java | 123 -- .../tuwien/service/impl/MailServiceImpl.java | 62 - .../tuwien/service/impl/QueueServiceImpl.java | 52 - .../service/impl/TimeSecretServiceImpl.java | 78 - .../tuwien/service/impl/TokenServiceImpl.java | 123 -- .../service/impl/UserDetailsServiceImpl.java | 51 - .../tuwien/service/impl/UserServiceImpl.java | 275 --- 110 files changed, 1852 insertions(+), 8591 deletions(-) delete mode 100644 fda-authentication-service/.gitignore delete mode 100644 fda-authentication-service/.mvn/wrapper/MavenWrapperDownloader.java delete mode 100644 fda-authentication-service/.mvn/wrapper/maven-wrapper.jar delete mode 100644 fda-authentication-service/.mvn/wrapper/maven-wrapper.properties delete mode 100644 fda-authentication-service/README.md delete mode 100644 fda-authentication-service/dbrepo-keycloak-realm.json create mode 100644 fda-authentication-service/dbrepo-realm.json delete mode 100644 fda-authentication-service/docker-entrypoint.sh delete mode 100755 fda-authentication-service/mvnw delete mode 100644 fda-authentication-service/mvnw.cmd delete mode 100644 fda-authentication-service/pom.xml delete mode 100644 fda-authentication-service/report/pom.xml delete mode 100644 fda-authentication-service/rest-service/pom.xml delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/FdaAuthenticationServiceApplication.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/AuthenticationEndpoint.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TimeSecretEndpoint.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TokenEndpoint.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java delete mode 100644 fda-authentication-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java delete mode 100644 fda-authentication-service/rest-service/src/main/resources/application-docker.yml delete mode 100644 fda-authentication-service/rest-service/src/main/resources/application-local.yml delete mode 100644 fda-authentication-service/rest-service/src/main/resources/application.yml delete mode 100644 fda-authentication-service/rest-service/src/main/resources/templates/mail-password-changed.txt delete mode 100644 fda-authentication-service/rest-service/src/main/resources/templates/mail-request-password-reset.txt delete mode 100644 fda-authentication-service/rest-service/src/main/resources/templates/mail-verify-email.txt delete mode 100644 fda-authentication-service/rest-service/src/main/resources/templates/mail-welcome.txt delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/JwtUtilsTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/config/H2Utils.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/dto/AmqpUserBriefDto.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/AuthenticationEndpointUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TimeSecretEndpointUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TokenEndpointUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/UserEndpointUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/MailServiceUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java delete mode 100644 fda-authentication-service/rest-service/src/test/resources/application.properties delete mode 100644 fda-authentication-service/rest-service/src/test/resources/schema.sql delete mode 100644 fda-authentication-service/rest-service/src/test/resources/templates/mail-password-changed.txt delete mode 100644 fda-authentication-service/rest-service/src/test/resources/templates/mail-request-password-reset.txt delete mode 100644 fda-authentication-service/rest-service/src/test/resources/templates/mail-verify-email.txt delete mode 100644 fda-authentication-service/rest-service/src/test/resources/templates/mail-welcome.txt delete mode 100644 fda-authentication-service/rest-service/src/test/resources/view.sql delete mode 100644 fda-authentication-service/service_ready delete mode 100644 fda-authentication-service/services/pom.xml delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthEntrypoint.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/auth/MariaDbPassword.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/auth/UserPermissionEvaluator.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/AmqpConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/AuthenticationConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/GatewayConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/JacksonConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/MailConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/SecurityConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationInvalidException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationMalformedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/BrokerUserCreationException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/OrcidMalformedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleNotFoundException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleUniqueException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/SecretInvalidException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotEligableException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotFoundException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenRevokedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailAlreadyVerifiedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailExistsException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailFailedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailNotVerifiedException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNameExistsException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/mapper/AmqpMapper.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/mapper/AuthenticationMapper.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/mapper/UserMapper.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/repositories/ImageRepository.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/repositories/TimeSecretRepository.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/repositories/TokenRepository.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/repositories/UserRepository.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/MailService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/QueueService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/TimeSecretService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/MailServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TimeSecretServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserDetailsServiceImpl.java delete mode 100644 fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java diff --git a/fda-authentication-service/.gitignore b/fda-authentication-service/.gitignore deleted file mode 100644 index caf6b59191..0000000000 --- a/fda-authentication-service/.gitignore +++ /dev/null @@ -1,39 +0,0 @@ -HELP.md -target/ -!.mvn/wrapper/maven-wrapper.jar -!**/src/main/**/target/ -!**/src/test/**/target/ - -### Generated ### -ready -*.p12 -*.key -*.pem - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ -build/ -!**/src/main/**/build/ -!**/src/test/**/build/ - -### VS Code ### -.vscode/ diff --git a/fda-authentication-service/.mvn/wrapper/MavenWrapperDownloader.java b/fda-authentication-service/.mvn/wrapper/MavenWrapperDownloader.java deleted file mode 100644 index a45eb6ba26..0000000000 --- a/fda-authentication-service/.mvn/wrapper/MavenWrapperDownloader.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * Copyright 2007-present the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import java.net.*; -import java.io.*; -import java.nio.channels.*; -import java.util.Properties; - -public class MavenWrapperDownloader { - - private static final String WRAPPER_VERSION = "0.5.6"; - /** - * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. - */ - private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" - + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; - - /** - * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to - * use instead of the default one. - */ - private static final String MAVEN_WRAPPER_PROPERTIES_PATH = - ".mvn/wrapper/maven-wrapper.properties"; - - /** - * Path where the maven-wrapper.jar will be saved to. - */ - private static final String MAVEN_WRAPPER_JAR_PATH = - ".mvn/wrapper/maven-wrapper.jar"; - - /** - * Name of the property which should be used to override the default download url for the wrapper. - */ - private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; - - public static void main(String args[]) { - System.out.println("- Downloader started"); - File baseDirectory = new File(args[0]); - System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); - - // If the maven-wrapper.properties exists, read it and check if it contains a custom - // wrapperUrl parameter. - File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); - String url = DEFAULT_DOWNLOAD_URL; - if (mavenWrapperPropertyFile.exists()) { - FileInputStream mavenWrapperPropertyFileInputStream = null; - try { - mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); - Properties mavenWrapperProperties = new Properties(); - mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); - url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); - } catch (IOException e) { - System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); - } finally { - try { - if (mavenWrapperPropertyFileInputStream != null) { - mavenWrapperPropertyFileInputStream.close(); - } - } catch (IOException e) { - // Ignore ... - } - } - } - System.out.println("- Downloading from: " + url); - - File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); - if (!outputFile.getParentFile().exists()) { - if (!outputFile.getParentFile().mkdirs()) { - System.out.println( - "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); - } - } - System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); - try { - downloadFileFromURL(url, outputFile); - System.out.println("Done"); - System.exit(0); - } catch (Throwable e) { - System.out.println("- Error downloading"); - e.printStackTrace(); - System.exit(1); - } - } - - private static void downloadFileFromURL(String urlString, File destination) throws Exception { - if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { - String username = System.getenv("MVNW_USERNAME"); - char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); - Authenticator.setDefault(new Authenticator() { - @Override - protected PasswordAuthentication getPasswordAuthentication() { - return new PasswordAuthentication(username, password); - } - }); - } - URL website = new URL(urlString); - ReadableByteChannel rbc; - rbc = Channels.newChannel(website.openStream()); - FileOutputStream fos = new FileOutputStream(destination); - fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); - fos.close(); - rbc.close(); - } - -} diff --git a/fda-authentication-service/.mvn/wrapper/maven-wrapper.jar b/fda-authentication-service/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index 2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 50710 zcmWIWW@Zs#;Nak3U|>*WKn9!)3=F=mA&$D9es22A3<2KkAT<n3P&K$UiXdzBb@cOe za}5sB^L3lr@5pt?K*05T;~hnDj}@Y>yEd=W;%GX$wk4&@`L>Er^3mJ3!$MYQmmmIr zxcX0XS%jnbQ^^(1?~OJFaHhLTOJy8%m2D50CLw*oWp#DICqFBe1@o8lD+X$BeU{_> ztjqSoc8f{x3oVZBex$QMQ8@LV#KUz;8|KVql2~)$&(gAMlXY^`F05XW?R(;8pM(1b z;k|Q=0xcHD1!;fZCU-qAq{wsn%RAQbhi?jleCoaMuHgy>1_o_L1_s<d<wy2uZem$# z9!UWuiK#s^U#}!FJFzH}B!krP83cBhZh28+K|yK}NyeHIZ)`z+u3kY=enDzcNoH!X zwzsa<xijZCn_e)!YW&zs|CGM&2cL~+&s^8`JmaTx>WP<*)=3|4A3a8{XKK@yK6^P$ zTvbeDiC7^cIF<QqabUG#U|_Ihz!&x`3=9nUMd>7Yh#RUWu^=%yBb6krf~Z=-fl87A zpvVTf7iu@e0AgZ=5iMUDV{-}2AgBENlH!u0!~);M%)H=|#G;baBE96C#Ny(qVJFLk z-9`4cwuvp8$-?#|K{@HbB=*t*4#w^o2OAZXq?4N+XYlB*%-K3|*VK2DHX5r>sEm7> zGBb2rqVn$+;Z5iJpXg88leIO+b6T<AmKUq4LQC)OUjJ_OrTO*$zlj|1{IeiI`tXKS z{fsKt^Wr<hu1(*u(ssjFuh!eQR8I@_EjZYcH^bOzJL`7QAClj6UU}~dyL6WIZB^dH z=d8{j<|Ilb^&AxUKV$Uc_*y3Z!|iXim^h5yoRd(Szh+wToqa2xd(BatwDD6_X4rOK z%Wpk(fhIC+x}Wzqc^R_E+)T9)n}0gT#aub(aE!-Q-L|E%hF&(RkGbRH&VBAO(77Ba zka%PJ#`Ib>p0>YQy9yc(%{M>e<hFj6$b?$o?UGfGb62c&W^I??+od*{`|Y)ov^`6# z{f=GYHtM{~ll3`8qW$9P1<Mn^C4|U0_MG5+eXV5HC%Nx?u9PngPPFMx=K2+N@sd{G z<Cg8Zn=_-^wjDKGpm6ZQ)`Pn>?L?a;ls<9CoLOVhG=26niAB6SzUAuW1+}V~=Py4S zyz8v5;4#NJQ;Z}MJxuzR3!W15=)7s*_i#&-c+{1$%^Age7d&#ElXzq1`ddQ#^Oh7u zKUx(XER+`F%JsZs&EcavQn~i&L~)cJ-`=CdGU<z_P_mJKlisUP^+?T+sac<j?lPa& z6P=TCQrK<NLIKa8O<mnPTsk!bXLO$`u-O+mXZEC-Tn|%&R%riDxu{jF*{7`)cQ8~) zY3bLba~r)v4=yhW%vt`nv8|9-l6C#Il=XGfwA2hvZfKrlm>L}tl+^mrr~SY`>yYyz zheUp6__^IPdhR?!%`?mL@Rd`_>s*U=Xu1}3FI>O2#L8mPk^9STFYqy4r{KNicF~fA zzOHS~E87d%*RQOQ&A*~*anSXmQen&shl0G|XR|qP)$DT#pB?6|kRV&hYT4!-y*4nA z;clYi;h0VDf}iJXKL5-0=A+BTs%Eo0cID|$NZ7VpYloLo!i}r5Oxq?{zr5!*v4%a? z-8lM=y{7mL1KZGdcc-w2*R|dBi2HsmGtuHgRsZ@Bsq&xy%5D@hCod5%dU5`us-9?I z=*AZIO>R8y9$`kaDw5VsT^X};(WBgz^{wYhg7iNN+*`Wi)mDo;D*GQkIk5N1B0td| zc6K2j9J3jBJYV-nxyHvMH8x=Fq~q)SLMy`$Du3EwXc%kS8u;w>d1sFKMm_z}SA!JO zqLjiGnQ+JnyUNX;@LVYS4%fzP@#V>FFC)2+Trn#O<rI8v|L%)=>WcZRe=x;QDto3o z`%%_qKlZ(47tU3&+8nvQ>C(QNO*3oeJ?E`-dMLR%>-kU7a)GyBHmN-=lUX|b0{hR; zf&KT2e`%RqT9JB%jV(;D%4cpa$7S;uQoA=c@5`!X+-kj2RWR9n?Jeh8``&L}5@juA z9v0~d8KS?{=08Zd_TPF@<oyEP^H&N=(s=)x@~nTfGw{FEy#8O=7Sdnc<5oTVlc8W_ z7jpQi>5_c+b@m4zz6p48Oe@VM*hFBa>I&<~_$PWd-}SOBc64Klzh>_7*gW69%<|*{ zcTJNC7n)uy{7_ZcG4Ed7tM7_#>mLSIx&1O|OWx5ad}6V@YSn9>tX!TQF6*ypR=A|* z%N6TYvU6()y;L}5u;RJ$5>1^)c1s_KoD&Y3DKtfWy_!x`=*LT8^Q*g`Z98eN7<aCI zN&bXeTU4wiHpXsJvX-i-y7gZAH|xcW1?eeGMVn+Qm$!Ydb4qw|-QDim<){CD&fh=p zL893wPi4mJA337&e>c|Uv7Zn%@L4k1<HAF}p#LInF;#})3@=NoGN+3y-n#q4TJQdc z*5WUJ*E53J1($C8Ip)O5z>vU&TsNT?(MI@+XqVKq#L}D+m;Ca)oczR;)FKr_XbByh z>o4pmP&e(G+?@6+S=~R_ypC>5=$G1`cwyPmBTHDFM3a6bOe@PgA5%ViuWn2IQhAZZ zo%RRhQ|IgrX?9SGDJtGveBS1KO4_&Q>+|gyLOxtMC~<hwrP%rW6}#2{*x3tQvcFPe zAaHAacdZ?dZ>;R8fRB9_es(phIKN}If3Q#F*m^!+e-Gg;I|FAardByliB~&!?5JnB z#`{VI=FO)bi0e&`xRd5_EyUfY;o+)wZI7KE3749ivw1q3Sba>HG_SS)wOx4s!`=Xc z6sc8b59h99u9jn+ysf0|lj@hxqFS}fw9lyZFLGXLt-ec8<D@~nhS|SuuWBxIHOm}+ zQy=hn$5DfucjE3>{@=EB+t#?;bvt+T?&f=Eu;lTR38&ZHx9aM7QN5%6_Z5NYnPDNj zER!NMO#B}7@THV5tuQm`+iCAL`)XKrdh+Ubj}$-gJ^LTp^-xYOv2jYzs;@iNWu5iZ zjMR8{EhZ&!L$QXD&wS;*(VI6HMrW<K{U|g+eS-Dn2b+8sbFMpCwnE7(o9lA0EPHC( zrK8WEB;H(ixFq`I-v!oJ9(|d!=XwxVEceRqN{3{0gPY#5doO?MHqqQ|eRE-#|Ag;7 z=gb~W6SJRe+4f!Yn9oFCLAD6jS!=|9>#b9EzORz^;918**12!?zbQ|-pSo;+lA5vi zGj2JV#~+Uer|A1nNOe!)oRV}!X<E`N^$#ztq7B6!zxc@uN^z!doEHf(F)%!3W<Y9* zqNliE(o$S-WpPPrZUDT&7EqL5QR!G(l98HMl9`-Xk`HY(tep~@FXSlFcAt5R_x2k~ zS`quBem~XIS{*kt!H1)_lk?Uwt<INMMcg7bpP6uT;fa2umFC+n9+7|0Z*-19Ypcpz zSM!^e#>MCMJfCT~`S<7R+vOSL9(OjgJe(S`wxKj~@!4iR>jz3Nw6DD_63zL@zefC} z?oIKDDZcBjm9uYrHdj`)H@e-%Y_ET_m;d&i7ZRH@SIR8m_}Txafwk%NgO+}S-#Q`- z>-;>b>gG;ZbxN&#;*l6O;~A<t*PIT<f4BM_vp4m(p!$=nO$QfQ1uvBCR}bDLd#vT{ zt=2`;IlXn)l}E^YUz+9j!%34PdtUtS-RoaErm|1xJe}^cw6Jx<g=@Q_UqyA48;0wx zTlhf1{{&ySyU&&;!6LTn8(n5>&rtrwn0R@$?~zQ=uCSZ-wo{M!7PtHl`pf9`*o@Ka z<)*3eiZ*kVD%OeUg>8J7@K)vLh3N;`%y@TByJ{9Jx^#x3f91cptFB`6*6#Gz@Hx3B zeCzecz2yg4zrEJuC}t@S{}$4`pW|)rsri?*A{CsTEft6fRW8o3Df`e7U}Mjp_%%mq zjl?{aH<Mis)iI~NnGu+<W~D${m%B&3;oO#$Vr_X3jGy_=mSmNvK6yh#Y=cEyjN#^M z;XnG`{Em4i5ngOww7swJx%NA`hxZ=V7qtr)wN8w=#wC8dNcx!CF`e(rH><S0Z%+Iz zpn79>s=2_7{XM^>K`HUHbUYU)69Yp53%-<SKvGJCmV6QH3DQ?3|IO3=dG3)x-yDlT zr%n#ej}z9;nkX>a;qej1=C>>-%T}>?od0ywZbIp^l1-2Kb}eXayX3^4IwdH|E&A3Z zv6RUhZe6<I`~6<^`@Ju|&r6BiCd$Qmaqq|d^>zRM-#fqjU-gebhTKQ2Qd%stWXesl z{AQdq)V9j=i+cHDO17U*L|#d|X;ND7J-&3UxkYCZPhWi?ykhT}IVOuWjcujwnwHF* z`o$!Kb52o?-qFWQrn{4OpX)9?`uO+u`Suse7dE^Qv#8rW`CLWXEoqbB$kjr(tPe9C zFXel4=cedw`S<s3hjQku5w&Ri6I6ETOOE>8^uB1OgYzn5AI-~N?ryy!!pSUSr(_+= zs&m^eZ!7ORaI9@<`m}W>d|W@aSsxB#m>Q9{KunuOOInJ3wc>_L2R^QtxOhUNaDecG zjNV3J)w7+R>b6P!X|dVcU(3w6H|^`OHCw_jxCzZmNm#2A6R8s8;cQ&}X3N#-vhthm z)D>$iJyS9%E$c!_uBE=tuRVs=$^tJdzdd|?VPm7A#OFPo*Em<+b2wJSe51pSmzSGK zdj7eJYdad%OpHYqPjk;cacQ=g#nv8X>FIiz8G<irzwKIkzFX(S#<gdX{##x#X<E^9 z^fPM`@A-r8(htrS<bPl?XW_$=#;R1`Tb~wjXYkDF^kr)7c`E8+@QyWKM^VjLHeK+} z-salsZ~RAgC)M7r+Fi2cyzbk3V*T%Lir!xMFvsnqrNm`12ljIl>ny?*e#QJ*kg%3t z{25cW_Z0b#Qt?j*$`)C_owBrpIhK9WwU#5PZio3Acm1>wFK4>Z`Pp=idcu@><`;Uo zXH;Bcw0@i#^6kykCAMr;7v1$eLT2j8s;%tY*LJM+lj_GjW^qlu$J6<5=eel`+;Onq zHnHwp-@7g8J2Q<{7tB0-c-_>68)mh(2_8Nic5L(IR*}__I+^-<*EZyzYdU1eJpDls z<NoJ7N2<I;&g6d=c%+|n=zQU;GkrY~>rD4(zxGYBO>0c;%a#y|2-R9$V$L9Z*r;zy zUF6}U=j<Eg7Z;WHl%~~fpVGK!7mp_Ibp4RIc5>}oCO7zR-nA@p>C}g-#cvwyzphwo zo%n!jgU|KL6X&KK^V3`B+nXx5dCJTXp997dj;g#CUmb2V+sM{iY)<atCmI{}EcCg0 zp|D}W<PhC1od)s8OQqt1W=-Z+k*tuM7RXd=lomTNb;5J4sW!ignAacBe#xG+);ji! z^y+-h8(wBse<svdFuvR|YkF`t!}_CJrPd{`lQBM9HRobr_tA%?7AIEaEO(b_6n)yI zQv0cDkK^I5Ci`M6c=rgJ3*-l{dn+gub$H*ocX^esd@H)!%+IIZcbdw(N7Ot}@A#Pv z`|~DcuXjW;e7duiEpPJnUi+CvM$@L$Ya3rYdFlVxr;4s&duB?!3O(Mn)}i(6U(r-q zk9U#gTQwM~cf?Gd%e{Bk)0vl?wGvO{#;f=GrXNo})goMB+<Qvr-n*34G-Dp|jEee| zUw3<D^&ep8z0>dPvyfHeby#7<c8>L_ir*7|x!>!QjAWNHnJ{_ATD6o%Jl`z#nspzR zl8dTvTy>bO??GbZ<8>!ytvs`6!*h9e+1~0>8?Az?^Kv)td$;fNA!bQAp85Ch@3&qP zq_fB1_@Q%4p0=GWx#5?-MrA(x!<36(6VG#=58Z3P-&(CIaQLv!hIO`6rmC!ccvtiE znH9m3vrX;i=$`7<wy@88pv<*0c-ic09}UBjRyIDqr60zX@x$VC^2VieBduNYtmZA* zS-F?Ff@%H~)>jL94lU=cakCTaQS`cB;udbTGS1)sK{4CBL(eO{(!6Ie=2eFAcQhT~ zJNc-aZJ$c=qH4do$)-w?L07-zC_ebK^_wYk{BiRgCm8blXMQqU5gm|qMd@c9_x+0> za+&fjPHTMssQTxuh0knTw`XT)PM^0k{VG4#gN#&xptW;Pu`>SQu)p{r`Ou;_hRpUy z&vTeao^4tr{jWd!)2i1$SY;i)e)20@s=43jL-rpv{@Zcg50@;C4`{o+PxX%1&)wHN zy8L>dSq5Bq&viX~W$bJ2q|!f)aht9z*)Jf$`<?AW`k&Cl@z3=38TIh~;jLF!dh=yT zv8DIHX-1iw*ZZjH#$Nj9ulC;I|G8<KQ)d0UeW3os{hBw{%Wb>$*6KgMR6Ez|G}ob` z=MJwMKZd-XFjp?`ZerCkbKAs?Hw-x=t4j|4%ecwHUs-wE`e#eQa?^um8DDK~OK?xM z%@bWcN$-Y0tj+g#lU{B&2{eAFd0V==%_<@NQ08&&^i9ITe5I0n#ksdm?s~}0Rvpc} zDCor*+rYE`-p;h1_0RD7%t^Y<RTo0fmL_TUH>NUG%0(7?MAhc>7(D(gFk|u6Pr>Dh z<zXG-jLO%~-FaFzGw;;25N)p&U2-W^c_OEIH#$w5qNSCxR9!M*<0XOBE*(y4PgOT% zrvzFDv1zcjZ|LFpmf_zi{4*u{#y_90Y3nUc3S_75o!EQ+Pwb)YvwBx%ZFyz)m2+3_ zslxWsjq{FZ`Aa-YY~NwI^@GCWrdJN77Xwz9teyP$bGNU_(UV)e>UMP)thUt5+xE=d z*W7X$_cxntvy?^BMi1L~XYzfGPd*u7_1wgHj^4Qi``u*9?|cz^8ak7yAg4S~c-rfv z?=llunon(MT6lDA>gVXsXSYp0`ThA-zhxzb(z=S9Edu9k`LzDuFI8W!)hQnDOE>S^ z%{9}g_xj)eVtnexOSONV=a&z2Za!CNI=@)7hHLGyLe*ks$D4<WWv1}j?B6td{ZXTT zY?hlngHOIcwM_Dgsi}R(uVp8iBaYt8`d58V@u<p~`&U;y%AdkC-(M^@rBq6CPG8j= z$5IEWqinAWx~d}Ag(S5~c^(w~Q>_#xa^zjeE9TS9uhR=(1zwSHR8dU3VAZ{?a;9n5 z^R!vTVy`Z&dm{0(nAKABQ@x=`1)o^OI{v&c8-K36!24CIk3BUPD?2XUHf`cY(bWZf zmhUtsZrrEPe4TsS#9fn<<<oCZHR*mn<>*A2U3qQq1@?<S+mP)aT%5wTv6AuY1pl|6 zC6<29Ji+><mj8NH|HCbevnH0`lG_ttxwd{~!N=fFHuJRGUFT<fH-A1MW4%?=-%e!% z;VJXx9Q24^p~b%Y?)57&pUxgwcz0%*?h|gm)-5v2ocGDB%lWQ<JL30-d+WmU`R+7( z)F!xoz548#Lv4a(oN3(OXSV~T_hgB!4=m0uUZA=peBFw34|h$lU-G;lIVrgGsH}he zGr2Xn=1uv^i6`nss!q&5tgGw3aO;x~-zI##VOYke`DI^Q{qsL1s?MBmp6MK$Teql! zA^Syd=@%E}T0z$)gB#t&8~Z=(Y_a{m^vjdFpnV<t-{>2*h}3V6sW){i;B~vn((}+Q z>5p1WU-Zu3d`k+|ufKozt2V~|$XE8A{{&=2`(KB)$><$>Two~I-u-dL(YH^6J!d7} zX)HI=DmQxlX>q&9Z26xjzx(a!zW=y@;r+2#pZOZ4duI!oyxKq2>W;U~mkZmze~9j# z{yC)Xitmd3;_sEObKP7r&E>xx>)zQR_lyl^%}W2!xNn+z=e`H+9Zl>-Gj_68eV7<& zBgR?xg=3xvOaHkC!oIr3KSKECpMA*P^Vi12f05RL2CWp0GmBSEX1OQwxkPIAjS#Qt zCQn^OV>j)5G3o3NBj2?TUYgaoJ`HTGk$Ap2(B))wSWmOBWsPZj{x`R#`{y6FADka< z*OdSC!|8n<AGR0$tCoM>dFp}u<CvW9TRGl^Kl`w}tp0@fr{c{<fj4FTxbyv2IyhhM z_?y4gN(Yx$Zu0B6w76Dxkxb!z!N8d-g#S>xjdYTB`I$DJ`()?w6^5p2=Qx`RsV) zMNV$`Lxre9*=+*0;(~&P-&Q5>XuM)><dz@g*Ew<0GSRt30hSHlRpNA+9$6H{w(1@d zIDT1kt%d8Af+xzyLm#mOo_^80Fm7$=q{WKUIu{hlRquAv+&!)M<u|iwMuFE)JE^_; zrR2VG+RM#uZKg)ruOx5W`Zg!x?xg-Vf6rQe-1A#}>fG6q&CwC(^HPuQ;T3c$w_BF$ z`t`O;#L+EX`4c9Gt~J}>xJLVkiFe48tG|pZy$&CrC6{nHM%^zl_U&3>=^rmw3rqjZ z{C>65?dR9)ooPSUt`gS$wEAji#@cmEM-G4e*s|Vo-iP)-A6-IMXwAFe6lJr}{ZZ$w zRbu}(1RPCwy}ifu^^`l-n*Z-!d8>Dit$ee^9p}IARUh3uT>WCNYNMof#54DxIV+ix zA0%z`j0|gM+!z_TjcxBEoxYkSZ-t^QK25$JcJW-GsO3COtB_4$9h;ZRC9Sq_k6kSK z!r`Aki_@I$1OFL7y%}fImi_`B28J{Ve7zY>yuBIt5Dugp<JxzS@1TP~+y8}ACQYbd zU>DajJGWP9qK8V$?=1B#EAOU-zP>4A)6gyy-QuZcT>md$?f>J--OMu<yj|oZkx?vQ zH1*)bqn^*!xFn{`+9VhpyTax4gkJ9U3%#j&u1Z%O*L*W%ms}`VC+Jkaqvm&)@y1yF zKmA=>X1H_yUMzcXb}X~sW{=gIyVVwc|Np&MXX{0A;etxn7k`v{ym$F2ganG+bTPfM zwrp+Do17cp-o!b+W(5U9OW}8+4Gatnk5MLD(1#Z^@CAcsUU5lcP7Z9MVQxfag3Qy> z|F)S+BiA0A-jmaPScmQBgwh1*lnK2b6(wC%A|s|9xs_mit?$Xv-PWh&rlqWF$Q9yv z$l-agt-$&mBg^Ftn*<Z0m@<QJnY?tpWs;jY>z?l3_dE8RS7(Gz5<V;+Rcn0z_r1S$ z?`uEboxk^=Sc7QM8Hdh<iNf+(o06^_klU@9^mg-N-@}F4Vw)fPzP;#jiL3qX4#xh3 zZ@Sm)R%_<Cw$Ix3X5Udc53a1J+h+MM-rm@@dh6cw+t<GJ?!A3$d&%2Z_cpwZatuAr zQtNLd*TuJ1@XU=T8s{@Mo#$Gfqp$TtOI=sOOGTI||AJ&+o2<6RinkVvv(`o`S+9#p z4l6p}=5na-&9Y5ROVjPH$9kMx{50G7VnET{1;3xmXbF^Un=}0@r_Jm+3*I_wuJSo^ zB*yXOrPNwcfsolB76yfX_MI_9amH1_u3NDyPvlweJFCQ8I^Q^<_4k?zkNKPRZBN=N z?s_rFIC~<$WbS9)Be~j{+b^weT3z2)`*-I5p0f!V8lu}LDXQ;Ei&U#qT%K91k)yqP z+eAj|W!0R_bqDWs{&-ftCql)*`uDD1<+-`9?_Jux^>DlQh8qsKf&aers7)$6azscc zm80#=64|@z5qnCqPU+14b*kO!igZNWnTvOgFE+R}3VLmEO*$EP{qb$y7_Yf8msZTp zd3E<zUV>_@{m&1_LNv|@X#YO#6VsPwbZdF$uGE4_c{9JvkJ~P~!)bA?>SCL@mbGyg zre0do5<WAcef3L*uXR&?-YW>>JMn7gv2Na`(%`09l^G{&)@0@6#e{xYb+Gp825XVr zJMvvCrDpqVS=8G)y~K!#<+;w>YunUf9<q8@S86MpP1w@>?~uoD$s+-GI^Eu6T-vWc zdq<u8I_LIAe@;D{<bS0W&dz0bT$b-TE8N9rWtVgCE8DJ@ll!M`Rok|x{ppb@m$vk` zmo5Kd8myWgXvX?tZ`{E{F{zjB>X#RNzib#*bH!ewT&eD>-+s22ZJrw~lbN0*E&i2n zzR`JeVVTB8MVp*nwW32MpVfq|ON-O;Hk*4%fB#irTqeC?MnO8eN!Evkg!jkSzuWw{ zzLVRzb>EU)vo@X$8tZ;6vGIB1*wJywAeaBBNIaL)6lWRvZ%RoDbF}ZhsTK|_joE3t z=&w^}l3HG9nemBrx4Vr0PVp-jQkyjE7F+qQH5}4>iObd)rX8KU%XZOJuPHk!GB-YX z`6}#XzH#AhZR?EoNXEE3o_D!|R|w5NX3O?Y?eyjhwOg!w8}z1$rKfGwlRVk(bEo_K z2DePvw>O?~Jo5>vx>?41rdI7;czlTOs)lO<jlLfmI?q3L{KQ$@|Ne&0pUFQ{<SMo9 zTyb1wI<+F=<2OOI?2mnUHd&9le+WsoO)?8Uo%nt3_AL`!tH0Uav}rrcCbv8<xZGFf zpzxZ%D{Lpt;jzD&^!d^0H<oK(RhE_-h&+#OU%KjF<*g&eQW?5`cy|l?Sxntx%6|3P zb03@a`&OTD=?z=0<?dT^^sQ+18W-`URUbpuQu3!PkO=-4S2Oq5kI8-Um3uNCsqQ&* z$5Zp5%b70YG^I4QIKesAiHEO+m`HZrV6FWWdT<@jBNpyMPvV1SM?B)UDmx@!I_V#y z`m8NZf%`>e&q~W|67K!CZDIsl!^-;!8d6_wD_?UkvUwG6_;7~X+m!o8AEhq&+;miz z+xX(P_VSm8*5@{DuN4l@IO9L7LiTaax{__%c7DrTwA8Qq@{wo@)rbijCg&=DbI@8T zyYP0OyHjUNpWtPc!uec3M1}WvmB0AslCl4-*~c>dd!lzL)Y?=3PFY|rQ~zXgW%m!m z0FH@G%lbQ4`s{bQTJ^YP-OQs~byxJf>E_Gdn(UW$Tk93i9G-2$L2H`&9OPTo4=-Qv zbn;>IpSyWJAFwVywP5<}1G!pHyxo#2gOq19^0ukHXgdDpWWvD=*3$a8k3X!WPWNv3 z=c#^8%sbwlPt<nd!WC0}j(XO;)OT2}d7Sr8VV+58r`eIm*H#4m`k)-58TMyor~Tn} zk;M`#mfx|7*_NF3{by(A{(znpJgQN}m5YA<oUZndtKK(wZ9>7NIn%Y}+b$ZvC{sLm zf98+s)L=ocFUG&;JZusF?~z_~B_qj9;)nXR6D-~Db{Hq93*8Jn)LpOmhUZPEg`@tz zUZe2%x}C4S$*cVf7R)#6E|@G@x=8VtHFKr>!~I(~{_S`A*!Qyfruz?jVf(|Go1WDw zzMEE<D4Jdx@mP3bkaLCi9OKl>BK=}34Rcy2&7Qj>{FLNfIqQ?JPF>w)Zx;BbPAQ=K zzJUJKQ<YW8Sp^sRZXaB`?VGc6&7PoV-)=457&1xgc9_cMwYz34J2PSCnaHSEhXa}W z5~?(AEMOEhntgAA<(H3_GCg1L8>XxintS-lOodY&p(fKST`jlXJbG!#s*ql-^;ZmT zZ0)?IA^+5dY26(Ch~FCvw#|4e6`HY7Q+!vl<y4+mUjtfmKb=(Cd1OL&@vjxS(VudU zPKmNzR2-_VoWlF@g3!|Err8f$-h9(&JrZo_^vK11l9l6KHFZ(GU2D}pax<OW8TZOJ zv%vjZqP8EKZ<FZV#M@ps-XzrRU8N<qyh2v?YdhQD#eS#k&#d0RsG!S4FDEl{M|bvI zj?;HbuFsys!M|yX&#IS4HZSAwO4@xhbosK(yjN;9o6OJ8RqL+|(?7>;wd}mjUx5i8 zY;&iG9oizeaAM-I2B9}Lo*Um^f3opyk6qm3sb6&@)Gw}ZmdQW1>EaaMD)x0(E}Fi( zaQfs$uFT2VJU8d;zMr<i@8K1*r%{>9b@$r-e6P*AyUKW)|IAB@bDzv8v6_>(O`|RO z(XS{ETi@>Qr^Iw$@OWr$f9-oy%eHE9AKP2Oy&V44tC#vc^;>k>G;95rnV0m<cgT6C zOh}ayNlM~>lX%18f>Fw4A&%z}rq?}|u$gqVy;0iEX>HMIni@UXzpQKNf^x5L=4C9V zer)TRtQZACyV5Rg%g{<npIx?;$MeJlTh}?<96mc;)h~O^^|^g$TU;J5=W~NK0sUrN z(qB1mxBYu@)+K4#omX)ZO4VEASLJcK8dNP^X`s|9bIpf8rR~G3qJK}m+@0k0%#%m* z<kyKg!rFJfp4<>&=Xo-ht7Q9X?@vWsFYkO)C|a&resa0xMIMWd7nPRe&XIFHf5hi+ zb-yP272eGj+2vM+*00^K%eVXN4By{7mA6Iq?3JMMecxDeedqp6=a6#VZErsL%=}l! z-^c_ndnkW4{BMZ=?<b0tM<%paKAz!M9lTGWf7024In$-PVs<4bSMM(C)<2ot_9sY@ z#hshaR<Av?VUgMSPiHoj*%o@V$_B}Fi7|RKa%b-8cKE#PyXxhP?b2mNH#bf^lBg>i z-#@vF|HjQgz5hSg2Sy4w++G}*<Z$DW!;0i{Qwjyd3mr6Be}p$1P5FHCK``f@6_@&_ z#Du?8_xks}C|@n@W4YOUrX?4PH@`Y&usrW)w#D*w|4h$Vzmto5Aouyh_KEv_R~%Ss zv#Uvoq5he~xuca0yMqiLtqTd@y#D+#)4%u=<#KkhE10~+0}NLw+OOF4f<^CLRt5iS z=}Qr1Ea82NH4XYi8{32<Oxv!8>SnH48+CrZiCJG_@$z?}v4`ijo!Kp%VbAvdlgTcl z=-WF&=exzHb-!mmEBEi-jhL|Sx@R6|g?m@}R!SWdTfF6F^jpiOQ&P&k(R~lv3g7U) zxE1KOt0TSC<LMFp?IBk}mTcUAU$oSHqQ)ls<@=UBQhB%NyY`nK2kZPdho{zVTe@i7 zvLv4Q*&joz`xmU*^@bzu$`8LsiU$*Au3i<d;5F!9xbT<_r`qG&H*N>L<f@$Hl%;jK zLMuNn*7vEzytm<}#97?^>d!B=URL7Pw(eE-pFme`e#u{tq%CbfT@vqkJiBq#<9X(R zQW|UDTQ<ha9lo)0msI>q&p8@_uE##K1?@j;Rbl1*@2%$()+_6Gyj*ViSwZjF`=>sK z)6N{s`a7d5ET%L2LKEMbqk1u{(yuN4t#jv+F0?j#s260@U?y`st!Lt=-8;ng_m};V zxL$bf{gWm4juvj8efZVaBD-y;w7z(7UtjxTs_l!(62DSe%um>IoJhW*7=O^WNXGi9 z?VUHzUN@hKNsHBs-&w_fKQV0G0h>Qkb#p6kIkKEybLwgBDgEW9+gv_KSIqypJG||~ z?3k&Fs^$;3f3SUbU$6E@Zq0nj+UZ{&u;2L_^xylVbxpWq?|-)auh-cfYX4yy^?30! z?vLy>;hp>|T0DQw+$+DS%InK)zWV<c*6y`0I{i6x-IfX0`yabE8vfZaIkV<;;+iM> z?K!th*?%=A?A9C0w$E9oR@-Xtj*zIYlbHIISN-E<p)c}ZJhm?1_2%Yf`<3%M&N@wx zpD^pznk$Z1k~qHJXg<9ou6@Jn)mGb|U4Ncky*HaZ+V=5^vMC>~=tO<5{dDH$#GAhr zzk7Wtc4Uit!d$hoNkm>C?%y{9o8wzAO$-0a%)Kgj*RhX*3;y_CVf9-5Z>Rc$mA|gf zbC5j!>VEaH5AnM5r2M7+-wp~3o|d-mG1J+vJA<aJ+_RiZ;lZ*(L#D?;f&r&a8O}6S z3)Ox4I7n7v@=x7kh9TD_ic=lc3bXgvW?F8azg1J5C2G%o%fG?X`hM*0OPr@2sP|}- z(dL*nFP@w~5gVoxKJENew|{cWw_dGaJAYc~`~TCMRJvzd8_wTvSoJ&Xms;aak4Z`L zVTm(B4Gyi5SN-wsc<+v0k?qB$>hBUnZI1kRFEFo^JrF2!<g`Ugc!7S0YK83gi_gCs zH_H_~-ZFpQ^8C+*zZ~>RADf83KgG2!WP5qa(%C0h3rq|xl2pFk<hV0t*36UJ7kP%A z7QJ)7|7elw>yr!kLN~SL@2TJsy?He->`2tkuJriA@Yg%EE}kyf)_LcJo2*6sx|mug zvprgCGk30Q)_djUU4D{V<MW<~UyqjbeG=Gx;BZY{%Psp?QO5d*m9x+CoSBrHr>uSP zeO;vIm6j^z)zf#cytl(fckkS#ulo`c=gyA&D!wKqOHgIDE!T(CrTTI@Q@LN=uln|- z-DYFGQ>Uv5x5>o=k4qjU9=am9ubRpGgUYmzm8$cKr|!~G)_ecxYTnOH%9j%vZ~EmK zTmFAPLHgw*R|5{-_q)%&Q)Sb;|7(U!6wg$hPeuDI4fL#;Pal8s%xBiyv}3H&eq75x zZBNZtd#K!Q7|*-vdb@_NSNG+5o^a9q&5PH+&a(NXW~Zk)?O?%zjx-;)<0<t~?-n%d z-kL5GHj!QZ*KaTL_I++w_c^M@C%xO_vo-!;lG4qrNB-P#%Ac0a+^cVB?)N}$PKoSh zy=1R@hU`y_*Cm&HjWqwBrhM?{UpuQ~?SVVv?cBq4RD$dk<}VL+Wxsq<Z_#7hb%(Q` zSA1v-_ujZ~;g(ra_B-|{>c*$I*MIur&hu;HuPru)i7sqc?k(_I&S9Q!JZt$3kM{*0 zKh5+G?q=n>{!Db;RQ0E)*B#pWX^PF-4gK6tB`c?Kg&)v9Z@5fup2_CbLX~k(PjVMc z`Tw<fLhtOGX;1!xm%uz-7|ni%pMjx69^b%;D!vf|pZxT6*l<B>aIXL5014Z2>Fn@R z2Ob72GZHwGtMyg&Bli&rPBk^w9%I#q-8)xb54yDV^ETO!%YPUjVd<6s!}w3*aq;dp z@%D|L)%oX(=Ux6hXWRa||35e%2+qsUFfdVm8Ci4SE_347p8+k`CO&CyoG?wca^3_f zFCOFeniFdq+UIS(Z7q^_Xn|#Pe%za~?GiWan)f|qt9zk-UZQL5w!;CpdhT({PTu03 z;<a0ot@!rMo7L05OU^hZ%4;lsWcU0>E?<1F7@Q9H=Av{+@o?5@J9p0|@>Q?j7M^gB zy>4<QHsnl}+PdU<1s@HcI{rDut*mKQsUz`fdglA^0|keVu$<z#yiLAq?w0$)pLE}| zUJ2f;Je{3y`o3M~4}Nkp+ux`p=BwinK3V>U;o(Vt=XYH1eAO<$sD4MGmdMprox3vv zcLjPqyzXvdzbQ^wU2)liTCN4nzop(8o{0-G`^cdE<sKUsm(!$I$LC3<z3EJO-r=1j zaaUvNx{8;p8P(m>u8YWeiv&#Er_|Qw)mf(c;=+rR6&LPGaRs<<O}ldD{fc=HUWJ?N z69_usyK7n}`<aJfZ5nf1FUNI<h0c9Z<NW#kw5n@KeH<x|&DM(QB&+7v`Q?PJmRtN$ z@|fYf62A*}!7J0YJ@;JOsOfa>%BqxGr>@A&TV;Fp>K8*<<;{1OOcXz#`C=#A;``o) z@{r`zqOSe;4<iFZF5dMm`uLKQFKFd;IAnn{Xx%GhG;V4HdyRCs<p2FQHl{q?^u(&u zSW5NmhSca&8JA2v+jF#!d|dPPM(L$%F;6zH4cQg!x4egsYpFwI>Utxtr2%G{+?!Tp z28VH{z7PC%PuZc7srCQv$y;@#KmS(URsDY7_p|@M?|uLIvA_KfgNC<B{oLFvQar^r zE7Y!>Xc6mKt!`>taVkWZ^DIx<$*U7KzX)2qi+d{5>AuzKR}*}$9&ffi-j^-tosb&+ z!{J52^8+5?83uQ3i+0%GF)!M%TwCYC+cUx6qtim<@2hXv){?Vi`}*bPcgziJE(yws zMcm1H;pu&Kt)uC*OLsnS=xqJUwPeyP$r%QZq+F+c;wa}o^*B#Q?I(}zlywK+Eu46; zAl}=*{zk9rl%)3_limf0Z=dFy|F!Mc@Aj%+x9)uKoBx{o`!|_)uS*^?HM-uJ*z@al z^{%@Ssv8Q@6Uq~oH$>OH;N_JmN$x%H)OgM-omq2RYnNrLzIiXpPV6ePxr=bhc6*`j zSAnmK8oPeAIX0dOOAaYM=ziO~_SU4XV{WFGdwZF#TB=uW;EayB5hJqcpRuQRxAu*$ zs#`|uBKIyk_#!HuW!shWJYk2==<@%`C|bC2#@fHWj>b$(>n0l&*e0v$$ZnaEzEkeD zNX~J-##MhKS7)DIY5d~Y(Gph!f%}U5Z^H^WzkQg{vcgX5f{niPrhl8Bz6;&J|NCYB z=>j{hn1Fb<<5>nPxWpE9{dm>$Dym$o&c~SjO0uEvqdOfdHacGIIkk1~OGnnIC4sM3 zY%mq{<uWNdXMJ^s+SEcj*03w`y3aWn{eP6Wct-@Lb#uREGOb*7N1T7_yMPOwdY95u zWrKW=<=r~ALeNH_H}khjYJXE)jOqp(@0my4LKeRhzb1Zpy5+s~2@Z1QGuslky87-k zUy<OktK4(i(*<d2E2m5Qe7VK=wW{h&`t5h8rQUyjps}v>p;Y77%~h)}OUPgMVZH8o zB*g8jp>xyUnOq?<$#TyVb64-nx^-=eXfNyM_?)Hgq8SPy*HuG|S6aTDsBW1c^>Kow zpW5tGZ3`Rnr_WM2%qdf@=*2Ttc*TRNBOTXVPH`Rm@o25S<$VK<&`Y8c37jhe-QB%K zlM1|;4I_MSzwA+o=+f>Hsn0G}*?Z;kqHVLJmY1m)-f9hI`ntKwOuF?$rtYLoJVmz6 z(k~7g9+uHM+A>A&Li8fP#>q})L8oFCEPke4IG@{`GiT1!J+b$AH_k}g^+AX6|C*BA zTQg>LMdlvUv`t%4R$8;|%*x5XH2L$6tu->9uf1Wb&q0mpd3rYePjkXD_pUM(^itbu z(Yv+qKNIWr(@UarF3YC*+GR|Ca{Gglrsczfy%n7`)hpi~x_S1*%l-Lfi<Dk&+oYeY zIcMsg)|WH4?3mEH?0%51_p2KAC>4jel@CHIOeaTdW<JE>ep<1<EVYPnR`aqsY-PTy zL*AyW)>NKox<n%Lj>nXS!pI`GU28Pv|I&H1ysOdO;H><TJ?Xy}Ki-wK)ThhVr2p22 zdOM5Vb6(W1cE|{`y&=rv?d`N$uz&Th6?-P%u$+5kPQkraG4XqP0y8FuAG{eC`M=^r zUT5>}4}PrQAH{AlPI{4ivGDe}xy1}yb<B4!xZC?(qeB0lI(L*#+8nDj@#}@J+Mc_r zx?yo$->ZOmYg2wPvdSNc{1#vKP>f?b|Dic^a?2L8{`u~C`F;2Y2D>979Tx9IGJeaq zoquxKVDra9#j{lbEL_L;`ki2jdM;)(e~0jl)5=TV3ElYi-|9wd|E8b1KIZ0fX?2#h z6?SiJW*k!9)Kly8VA0L|rWnbu4}^bA;hw+5Yjaje)^o>IVw1Q3U0l&4@#v}D=b)JQ zeb$;gW-R|8$=ZKgHF6vKs+BLNsZ5QxTmInlCeM}UYA5cF5BZ?F^Yx>=?NZCx_Z`~( zd)9%%t9PCpyMDt?-11O=zQzx=eXjdwMky_G+rk`Z?D^r%%=@Cj>vZQGS}SMM_<rYy zSg|!Wj@drGn`Zy8t9xM4vTg&1;j`e`4@-KbmsihI(Vix`^&E?L=81pqKRUVX)AG&D zmp09Rl-_s$3D2ecC1*H<^WO3Z{#>8(kNeN5*7!~3GkV<)uKy^V&N1=s<|*M9roWmc z`s9qO+y2AyF+cvA$ZY=2^{>O;@TczMg81J($!}Rc%x4XIZhLL|rL`F`+8I6PAFmhM z;eYDih6CT3g_jkcdKuYqZU19U@qd9jyJHX9FTZ%n|A$w~pV{HpXY{Js|69z#;kY70 zJt#{j(&(g*zoYw|MNPu4t5>~ldn$c(#-74w^YxXSXFq6tmwf)|>1Qqdw!8Ct_O8%( z`Yszhc~<`GXHTv%9PMoFS|Oy@rCb}MdQVTcdHdp26AktqpKrJ=Zgm&zUbawwHh1Td zT)l%ED}5I#|2qF-T9wuFV7|bOF$&W~jyLNmrd%sMS6DMYx?tZu**9GLEwgU#=1)28 zbcG}0+nfh$W-mYOa;QW6>3!j(R;946P(QaXnV!-q3zuB}!)0>DPHI!ume)C_HrH;7 zUZYmFi+8^6h3VplCM@}KvEj|9N_W4G(yko=Ytmn&wof}A$Z5^DHmP||*W8Ql^X@$o zo1LNMnqpqN%hvbTBu-r++3u{x`&PWORy!Utf2He|j6;tuZ`61o811sxJ7?~^A6-W^ z7diUP;Z`<OUb&O?_!r-`Ze2B9iDtZO*3{f8i8C>lXL(-!{|@VF1Lql)&v^W=Pb)qA zCBBgBbXhLrZ5uv|)1BR-8@>N^F6!EN<;&zbadQ_v{8G7f>Ej}YPY2n|?ujz23RGt= z-FNliQT4`?-&w!?Thz4m(AP;#UxU_77hN;G_^ocl^@3W*gS)1^7G-@sHRihLn()Ji zw{ML&K9_rEaqC*y4QX7j+xJQv?a*j$^JLj;efh@JV@vrTZ2JACIsC)*!|D%|C#S_^ zzHiFe_Iw8GPmvnEFaAnX?88#?*KP0D;A4!sm{zUY!@Q)5?Y{QUrwh(L)69DD$avbp zh3Ag(?Ara~^7Xj~AMTyHZB}^j!ChYN2YLEZ3^tV~-gT;Hn`m-uwsD8~-F{)oCEvoX zhW=b$@c4sBp4_*4LE)C2&wU<ld&o1-zEj-q(b3=?$rVES{*Of~bD4YCH}w9>Szc!? z(cAs<Y3z+I?+Y!qHMhIHH_Geo=-K@F>9$aI+1ig%d#-IuVVYYZ`*x)p%lhZHnTmGu zG1shCSf9vqQGM!@GSzzx)-QX-uDvjS<a_7qiHq~U<Zt_0+5NU(#Q&s5?%YGMA>VW7 z2Je`CzIvC`>`N8w|Myp~*m!dOvEP?>ycewbk#lZ}XbNx3i*sU4E2cikNPcr@+bO&D z)UZ_Z!^WLFp>b{}YFPLuHM{ryxp_iPRb6ri+pj9F;7iT-+SLm6n)@$5z3E$jFznuZ z?;OXsB6S>JUuZZuHoq0*T+J9WRdkCs_d?e#&o7_bzsHtu!Y=LzolUa3kzbxgv}`KT z&YgSdhtB`{gTGVXhhP5jOJ!f|sdeic?w*p_?N^d|^X}mrUJK+ub(!p&e2o9{k4mZi zZ+D1yOLlxd+&lTlY0Iu3v%kk@+SIDQdXm-~5%5z$t4;gQU&%mA%{$L!QeD0-2{?Ca z)3ig{V(F%lx#4ZAcbfau$RyUqImgF(m-Ieq+A1QNv@+t@)hL!t`KBwxPPbk^_f9eC z(#0R1N#1P#I>N7X%-XWz?Faid)xN0GK0V>XlaqYYl|`d&M<~~1Zrr$9)4h1&lFVDt zC)dw@t#!Wabo%F=e9sr(x7VC?E>K?H)IQ;QU)Zl#3io%vS)M$RMaJM1&xH*$I^vGb z`S8%f%s744OwHSZ%lENm`hVDS@G@suqwtY-Pd!hzo11jMU!In=rE|fJwBHLObJCA- zd|Dwa{bDQYl8_h8vSnBD&byvn(01wc?Y&pmWXk??UwA`s!n^NUk{gS6EH3cgcc=gK zn+FRtG}3=mGe5cSmQ^6EefR8*rs$#xE1i^Ey{r_=PEUR`=|59|H#>)5LGj*LZU%<a zcxTY<@U`3n5=$~1i;GiplX6mvRDAN26LVZLi%W_!lS)f6^Yb7L`QD&df8js@+s(SW z1HvP=ePy<H<j52+d3sjEP*I@QbFtbiyB`Tfx8rWz&MBY0JNi%btQ#|aJm7!eF8C}? zDygx3-<vZtXBwx!J2`LO-@oh${Ilg`4z2e2o@6LAcVAEYBbQ|#`E}itS3W+!=!N`~ zoif{mS8ksb>8|3grN1SspL^<3xl=6(3VtqgI<|<{XYr>jSEz`U`?bui<>|7wt90s? z3;j&HWfe5*fmrR-{JknyW`BNZeEV+rO09cB_s;vyvP}>(jcWOncx|fpgWy`7q?JuV zO!A^@guK4Goi15rXR||X_v2(ny~L#}gMJp;<aqxL-N2)`<=K{&FgE}0)Xy%5UR&nx z%`>pKd~@>A|4Co<R+z7oihgLh?T!Ll{HBfXPH+pH>)2yp)AV1_cd4BZ%k^S@>6W)Y z`&8D%Sqn?Q@$LM-LdYpuAbsIR9uB7DRc}9p?l>Ah|6;j?M)4X~YvDV6{}NPh%~j}` z=O6I<XeoR7TR!8<sr!x?^#!`}f8VhwZ)?_$e$xYKZl^cS3^b48|4^_>v{<E&`(l2J zz5evw+bp(l|Ltyk>2zqZ^vSKOpFCpQ)O`HgZx(;cFr8Z2^A68J$wVgqyytF428I&6 z9XuVPlL;hI%nkclFZ^AwHrsoeju;cyj9Up>=T1%1ZB^UC!rZC9A?S2jZDXPIrfFi8 zj`u>#!r8WG&X!Zjz4Y79yXyY(%W9LP%@=;>|GW6szQ~i4a$W3}*?auHd-vzLJ(Zu+ zzWsjxf6q^|hRHv|8GO0?BFgkuif%d6G%x1Lv=ZaW%h$MA@A~y+zjK=Z_(k{U%Da3A zmM45K&GqztW@<cLX7>Jd=`pOiFZaz{cyaaR$<D&z?Z+=UhpR7F3tB8L+T#A|*yMG& zGj_(YTC?k1E;PM7_u?m4+m0|+{@lU^3D<udxe@Hl92^?%xPpu6YI@1T#yMpr)i<K9 zWX5(TN1Wt#i%ajBwz(~H$8%8=_uXkCKiJl+k#jPZ3;QTgX8fQd@zvs^M=S5nyEJ2# z!&alHI=9WdJJ;KN*~#6uSix$OZJk7ge~Iywbu+^>UIc6u-YsFae0Ou3v)Z%nb%`^% z^7b^p_nxZ#a#z}uu5D`8RlB!!KAkcB{IP_+iZV_r4dvP|Di_Sq;(K7gE&q1aleAZP zMO8|!^;~_cEZQD(m4z5fUtJ>SzG$}Y?sr!kbL#rLG<JD(uhoz8{&hh&++4PQn()Qy z@;k>pV_eOvIu2~paoV8Gu}s>cB4OG4j{*)sjk`7;IP}s}^!?GrTypn<?$(`EoV54i zZwC9Nk7_ISHwkR-)ZBP|X7+?1yOuCj#2oy$Htesf^8Bm02QROFl%s63s9)gR<C6^Y zZd^Go%CGh7!g;;uN!w(0Z(GVSH7-A<<+8+-eMz_9WwX?)ahzjP)ojxg^?%5m6QFqK zOi<VDRVAj@`e9K=S1b`PS$pcVfRKbZgZvV??O)=REe>5=(xq9b^d#Pp>(G9id4YwQ zhm#X^8X{Sj|G&SC`=QY@mfP(jF?YVnWKMCeT~zQd)l4hY@e|`x-%rnG7A_5Kx*2t! zE#>9Q3(*rB1vYuGFIazi->r*_;<?fscZKipO84i@KIbHGUGzu4^~uM59J=e11Y52b zo!umETBjkGAMj>pZf8$NSJ^(H$B}(szdTqeAS1%O;5PHTor}}EwmsBSzr5>lhUK?l zzw#JS?;Vjd{Wafjo}8$s{$79AvA8PLxv8S^2i-kqn7rAv`t#`r-DPLLa!=@A`NP8Q zU`TiCnrB8W_Ey*Ln+hLLw`8<Au9Bd5Gyh4@venO*1j^P#9Zf%y=zWb_=+Ng1$1R_f z<XwX|#mm+Defc-(OW3N19=~k%>wi5i?0VT$%+lMq$}vm!h`6YX%!aO?IY+kKIl-ja zBayc7g`3QIXQh_uA5%Ghi?VKXOWyo1CF|g}yi?0Jh6nwYa`*WgtE+S{+~#z7m2xMS z)%}Udlg#25Zq4j#_E7Bp?Iy+Yg7*<$y!sNg`@tV;|1^9(o6W~P=W71RCs`J4K}r9W zMeUFIi)^tv`m=OO?!^%0w8+@Yuep{Ui1)I3I9;nw^|7J@>l**d_dAYHUnOgt`#gL3 z#IODGe??YpTfg!L^FNQKFX`P;_ii$k#QqI$s%X~I{!}sfgj9%^WIb0x>E{!1WihpZ zM`vGZRC-r<J8Aar(?&e(%@y5_DJ$l7vJ_4i6Fl50a#ng->E$U^{6bgue6Qs4jdHbK zd^+QyV#Z|&FAoc|x>F{W4jvnnx85v1-*Y(8uQ^RlefC8@`=|=>2eHCUd-!vA+&?IP z-Z6
isKzxNOCF5aWH=T~xSOZ;b_@1N|CXur7Au6*P8)F?wI>2II4`0j<47Bo&v zk``j$_(M8lef7<!j@EAa$1ipX&s)B&tMuyRH7wH_EE<|a=l@qNXsops(_gr`NBLz$ zxum*Vvh7Esr1Y9>k29xMyPvAa;a#4-(`Iv5aVk%X{uF~d?;gi<DyR0${kZ<N-<90u z+iz8^e)ntHG}D-Qy<30Zjy|<xQ;_%a<_!mg{k5w+9GW;)3>(}Fy4x!~b9XT9nszI< zM#A5CX_!s>>N~fO#Clh>txk)Y@#TtB7TX2>UXdR!)778t6!M<8dR0oyb7}eY?MW^- zBAu4?WG_xU{9kNV`s~M(H}9KwGW1Tw&7fVu6O{WFbcL2(50L#NpmERZgXu*hp3}}( zRpw94x?_Du-CFqJA+0wimAg%E-3>leoLy0O&0S}{*5hyQJUHjadai%?^yUZgh{gJM zzuL$z%h|W9b8Fnc`AfOZ2Ub4+`HfTN=w!Kl$7jedzVbo!Ln(he_tOu&HJ2~HtE=I@ zueNV-%>JhL6_dKJO|Uy_uC!(E#}DP+6<_ZhwEl2B{Lxu<?;nYCr*D{kRv^2iF-~vN z>nY;)K{unj_lw)F{dU&x50Cx#q>poAds8n*{VAEyAHQU`tL6H6?^6!EoBGc0zOm|` zU2+@Gp4K`0Kd{K=*qP&<NlDJ3R#}18AEN*C8UCsHUG2}PHC5r`QZ1V`VUO=GYrV2v zUyg6uhEH5g8?Rs7C&d5$!@IKXJBQb=UleleR@W3)6ZwC}PvyiH?%%8TY5$$OOvP<R zdyh<*&Y3Zd^L*}$Yx|_m*!{VxyqvTC^SA%9|39cKFPPs{@AOk1R57>LXwSE2XJEL; zhi@R?3}3|@jJA)^BQY-pu@EpcGFmuXqOPBX?a+>eD;!;~EE3=d6x_;^c{OP%Z+F|G zfZaR~XYi;QKTc7)aQ5Bmx1sr84xRpFvv<*6j-@vKd*AnG{hp^XfhB4CPK)$&+vmQo z-v9K^zvJuoGg|b%H^~vUNZsAJ;i1IGB|OV}e_!G{zq{?Rhq+AjnF=G@ceiXRmYs<) zUUPFwiRllPva^!kdkao&5PG<ArlsU}mRD>(r@yeVW?5OOv(|*aiG1nKD^+r3kzYdk zl`T&)%xsnKawe;(vd&smcE`h4YTe3nu8Yol&ys93zWD!DxJ!b+Q`PL9Ozu{TD)W7< zYp(j7^hkd=<LjA_iX#Opt_v6oPkWded(ZyM_e;zdgPs_kH(l$JqMd56D6#**yGhSX zl_xLRChwc^Az7X;<A(l%>Gzi?H;5g|NV}rR|9DHAGgGy9npfh_LIGZ>K8`o~=0-V| zk^ONV%5UAZ`?TKlUz~i_(O2v5^O7x(Srq+m9$RyuaZ<p{nR{}ytx8H(>0CP&T;`Vb zLMTJ?XsvQ~Wm*)U?vbUBXKq`nxaD1hq~7$+X<1>rzH)HO$QQ-l&YkV=wc0n<zijqq z!PwifRh1X}^X7yJ&JoObcY|y5r!~^)rC%P(GzMQyH0%r5nX%C7`sN*1XLcABIv)T0 zLVU&dH%yZkl;?yl%n@KdwXmD%pcdcMK($HhY}aeeio5ohwah1Nu|fX#k}oXQe|NNK zOX;m=p6b6rTewKkCa+>%iLR`EqK8BG?M(G}p@mrrF7p@425Qzt9yjF?p0m5iP|Y@! z^C_2#tJFs=Hxn1uZQ%wbJxM(?W|*W^Y}4Mb-l=?t$MVM49ho~0w%t2*)v)n;MAEdF z4`z#_&h$zNiHk^WFY!s7#kiceKli+J^=amPlT7Z+*fV`<SWFma^^yfH#oIqFYcpT< zAWG!o$7!!j?#o`D6?&<yPx46QZO-fGtX5rXTdfwawD6(d(GS`mN>mbso=n*y6lZjQ z*I}-|yRCveKF<lbF#kvUE}cnIiI3;8EmxACBWo2?Fzf0DV^+P8jbHWG#=D0duVrnR zW@5DXOm`dWxpTeu<ZovrH_9%Zr{l9!c51WBf{hv*J;bk0xNj5v*YBprz3^jtXE$}M zGFokr$HF9PKE+>&(UE`F;!qn^|6;Dv{fP^`ygw)%_D#8b>C=_Zn_1^(?Rs76`r@X` z)U2bI0vbbYQoGOnD!%r8|2+OfVw*ST#fUlBf9X+Q@4oVp#tW${+k5M4vUg_LwC?17 zxAmatmH$)HfA75Zv~z97lU3p@JMy33YmmL+xiYr$=G{{r6;64!=k3=o%*<k~UVKny zmi*#v*_FKPo)$Kn+OBEdyI^)p_SC~4LSO3S>;2ej{I70nIlMTV!*A`v>QyV#S(mT4 zKl|{{3tUS6v)Sqob}znb854U{o9XXX?;Rp5Vvkz<uU*)n-+odi@7s0OS49<n1#dBz zs_ebHo^^{%+WZAOwHK6Xue9V^cWBi?-=$}-*!?SBn?2J|=J5f(OTQV;Etdb>;;CrR znYPYd^19)%>Nyj)1wT^ppBvL$Be1KWD_j5jXAZ?CIlqmUXB?Bfqx{u&&e7#HC325s z_ANiczeGRSOR;LUhLEn#BiVOfe+a%0npfKDULi04q&DjqbB#y5(vrHC{-5T((LY+u zvNQJc&wp6_V}<Awi$6_2t2)GLI@izWU*B!1edDj%{0Hqnb83H!gUZrsrUcy=EDQ|S zIq{XHc#m!ghfH8O=jWwmrk55aB09iRr`!*g4i!1RU))LPQfmv#s*bIfvThz}({<Vs z)y3<nmFq0z$$aXCfq2B>Ya3?Yyn7(zzp8wR+p#|Wzs!FY%bdS^*J0Bgn=gvhb@PAU ztUmYb{{MfK+zG5_C5}mans)bE#dP_(FK%htU-o@_=h@wLdD+$bbRwUfciWv_e0{?i z2_>GD;eUFvW%IUe{q<1$?}^kInql9&>`nNiwz_Tn@@PTO?NpEO^`B;~%boGoohSIp zxx$?<-+l7)?@ykTn7DgK^h((`Z$+egPn|m$px)+_F#UD))~vk|VtXG4NaT5*{*}qY z^?uIh>wc$~l>csRd~59Ud)=neyfU3SAIsxy&MED-AM-@MclzGF8fAUH!^~9T{*ykr zBGVn!hXj6Keeuh2;m>_9b|p%^V>`TK*3#V_Q`RKgHWt<_c%XLA`+;+f$l<jMcjxRY zi}@RJFt^6%Ywq2vXN}vcUwzF!6IZTUz&`&-!F0Dm(c4?r^quo%o&QK~Z_f1FeR4k} zo~87DS3kHwYsR517mBzh&FFh<=X`1ZyRgacs#fod6?~Y=n(6Ym!lC$We(w%BcNY8b z363Ed*<5M0Z@F2EJAyW^N_@5T-phq~`=7MS#_DvJ9<kni$E-!xLit_iLxYT~<+lP2 z%@1p~x%n0^;GI!eVlLCU;&+nE<-fE39aGUS*0(j8^6A2{C0E{jIM=aVN82OuSiZp= zyTyFz-NnWWm;F;ayK0hR@Fr#NX?;PLZpEK1;J-h6!JWVV<kaUl9`0w!Jh$NDma1<P z|6LI{JZt*dq{xEAiF&DX{uQek_H(+N?3!rWW^uEV@i*JwjL$Z<k{-J}{Vy%5G!i~> z)8SmDk*Dp8-HV?8PWkIr=eB9WZ|ToFOaAbR-Ank$<h}Dt@4k6wm)h@ku$z8OZ^NUP ztLMf2xSai4)%LxB!ixnQiOV)P+<%k%N!RIUtj=Zgt!^9Jr!4%@{!qKdchW<_Ys><@ zOFDd?{B!#D{q(WG^DFs8{A0xIkGTcxalDzVWWC*CM|cG1`Re&upW@nQ*M(hh%y{(f z$ie#_j?)e)@GnvDf1==>IKgz4W)H(mE{CogQyQlH<7l**(iHOHixfxiY7w@`<+F|} zI~)<~Y~Fu#8jFfNTcr|fW!rP32{jT+d`zbbDrCNAX%i2cFxBA1G?67*?2(+!AuH3W zSv=GjZcb}j)8;4@G+~xZiYd>hNm)_K4r$C2c8M$zVEY-zb!kGg$RP#$ptJtU4$qh- zyx%ixE@b+87o&aR3}yxf4ZIm&4__k+n(<vLQj<&JCofD5=d2dKE^5nMq|%)zY?d%B zB$0E|o)t?}QkUOjFh3Tilbpn{G&wVef1>HDj26Q!p0}3dPMY>E`jT31M%1qBTM{nr z;EeU0{jR#+cmKTaX)3)(7#@E9o_Vjj_<HgAy`TSF`yW@$e4y-)d50@k(lXPT7d}N* zdHMTfr+<k_I(F$(<y|kC;(4{{UrR2UtyN&+3Y&B7Tt?@bX|sKJdgoueysc^4y?<9u z6x4seIPu@YgTe0Y?0%~4?UQN`D<^X)F^B$~k}J9Mq%GI9Z!>l_t$q3?P&fLDb4JJp zn`Dt6ReLg|)84I^8oFWKv8?j&RqK<?Sf(~snC{xzv@YT7$A3>&UO920Ab#(kBGy-y z%JY-Nb}2_^+ubxRx}hw$(s=%rH!Gz+mwq}c_qi{}B7bJfIkPWKO;`CiOs4v(Xg_C3 ziVS_VU}9>>X&KMMJ{@}-vfMTox#X_Mc-~!hbjp#<-A7NRZYk8P@V|A&S7I`k5Oeve z^!_P2QL{XHSFKj_=}zoS`X(h-@0%UwXcQ*hz5nqw>niQd?R`wPX>R;U!WTcy;!Esq z`kg)X>AJ&rOT!Ym9^2hnpCp~+Q@MVk@teO~X7{Z^rWdbW?07`f<mXkzp0F+4lV!QY zjz_s4R_I9j@5lPPM)$h-C$S4Xlddtl`N`|}^}oK>VIjhsxNPg26={-5@8!6*?f9S_ zak^;74b|)yibeGvM|W?Wyz_PX$@5J;KPm)vd%nmLIDB+YuFvb*^vhLoS1elga@~rZ zeNk)1Mb4<+yxUPX#LmvR^UTLu>up)vM)~)gj~smWTSu&V;bhL7Fy*q8<WB7}<2&kX zua>iP7EKP3*nL5};a%{BH_5J3k_>M(TdY~|c%6srF~u$BoBH@y-)QJfDs?%UwMKBY zqG<L-rLg`L5$#_W-P06Rwm9|g^sFegmzj5S^Sc@NGW%9mh|H;6{dwBF>pFY4r!A2@ zV)Sg$gv)P_TzvS?R_UCumfT^_prs3L$MLdiCvUxU=(OUTppSJKZ*HB8s!U&FdbFl7 zY(m&XM%HNoXM5-P?z_xhHs9&TOdFP<M6WA(p%0&V9Ql1&OYVDjh>yp|x+vFk{`>#f zt$)PqWbgC$vD?0!Z3_;GWj%B{`%zNVSnlPfiIJkuZ7w~sXyM-^TXiPMOj%PuOK$GR zmwcz^OuKtgbH=ZhgjbWf9&K;WNjYVde&-+mqr+#`{aSZvf=uhTDUMY~->21R>BN6- z=(pU*9d6dDFfVk&z0-59<ea;9E=+|*c3${L&VyH%PFt6%@<VO^uEdxF%bS0MR66Yb zrDdl)Ay;LSCYzP6`o-rVHFEI_SRFMs*`{01-@tY9w61bbo#)Z%t)0)hIz!_8rG?#Q zt=eYry{Svw)q!`LXa5|1*3B79SI<9^b$ZLA)Pqmgf1dr&+~m*8|C>4%g|FGS@=@r* zJ7?5Pb$@pRY6PsfBji}KJI3hPeftnb-H!Jk*dF+<u|KYF6#L><iNy2|wg>y)hwt7t zXWxrkVSfUS_J2se7CWKWc%Rl2L*-Qc$IBi69FcYjbvP>eacY)Fy6?p|iptODgjWQn z_XjUs_ua+&kB_UT!``O%S;v+&z1?VCf8q6de#h9LDIX;^3ft{1^sfH<NNC2{SJ!e= zcKKdT`x5o9Lv503q=sI?uD*D-kKC-Y-{r&|UH>6_o5!pfH;S~hEd+L-w#iren*T9P zz&!7J>Zk75qwhadhiy>U#<lH-QrL0%O>UdjEOo<vatPNS_MCF<9^bV;Zau$Q%5HZr z&KI>1SaKz?c-GeNy9X_;SDegsn}4Az@_F0&n)4Rsf9o}0NSQ1y5}#}MX61{=zt$8p zYg#Zno_Fb;^WH@7eyCOCsVOpi=MH49nmuJMld_-4z1|=3e#%i78mf127j5TIe0O-> zU!ix?tG=Dy5&!V!`X_Z$)9<%*?C>a%6}-;z^NFgHUFFpW!As9qf8@(@*(tr`os!*k zT?fhN1GUK?PdpRarnjJUP0O}zLRQb0+<p5;H15d%RQ?a;TNk&Md&(!N?hz<{v*L!C z#N-LDI?g{nD88|barZH$YNqwcA8$OWcqR31>V@LiAHBbKMXNDZO>mP-dSPvwXMN|& zkyC}-h4N*0w5(TLJTSTYqY&SvFN(9??eF@2?DqO3g?nY9%^&+#du%gYA^ssH`nM_1 z@%0}{ITwGn(3?I{^^sx!_o)VU{hZHpmKLX-*u18A+Zjvar!$|<F||y0tt|T{WcG&d z#F_KfGq-o1iDpu*dGRaNZI7t@(aja7&Q{t_`&`b?^x{dD{^_0lGvB2j<O|ruU05PK zQSQrParKMGxlcI0l0Wp>q9^>rh9ep6lWpyqmaDpnt44LjaAxUD(r&z+^lI%v<1GRY zH1*s6{FS)Rrt!Bh@=#9=bB?0bd0v4{{`)$`JSS^Q%sHN3`N{QCs$Bfc?A2j<PP&e3 z*?1=A^(P1i&3lzwsGO}3xUXyd#p_BRH|{_AeMSuP?3dXJLbHy0n{Oy$6!*9oz<FQe z;Z)0=yDEHEmjBhb+h=(6_>FY+rP)^vpYG!8e!b=R!|h8Be`ayX$d5U&)KKluUFF=j zpMM-wZDId8=k*S~I(_d+>z~Bme7bkjSCe_mPTu5kzA*D8OW^gBw>@9lY3<yz@zCww zO+tr)gx4j_n<nz{VzKmdMmyIXitV1)Uqu@{_tbo{Y{>&togKe8!}mMBxUMa3dx}@| z>HNo~{Br)D#*u8gD?~EOA5|omFIc=*)u8`=%)0DLkxEs46XtvuFbGmiV99^Te<_}& z{>Xe4PNg5l|D5g`2>;M_6f}9f^rUYB|9y^?3444KrLQjk(9-UEuxqx$KehYZ&bzWR z+N=XFeQk19dF|aSYkuK^;q`O(zwNYOKX-K1$I=k?v!<6G1Z_K7^k`#OO3tz3ONn<3 zLkoA@E7b_ApI*FWV&&gvZNa~*r{+w0Cv!*P_yK$AJB9{g0=^;hnoBj~J}lX>@KIs& z>%fEim;R~$#}De|{5ZbYav}!<gN`tAFNXo`fJ#|>jh*Bi{70KIq8VmS$S}tw(0P~1 zCC>S|xruoxKACx`&iQ#|sYURUFXx87_LFjyuvPAU;k8#(F!%M+@Q8y~qi$<mxpHIH zjDTz<#h3f}UW;5m;55zkwfC3EH}XEWmhfL_&-!&%&vlCKBKtDE`!&VqexBnw{NCQa zjxpkx8%Hfqw&?B^pS8^STH~G_KXl1ICvGiQw7J)^eUU3H&CKVl_1bx6ZS$JU&b3{o zIyvq)6g7AjyZ@Q-S~XAfYk#Y9qJ`oYW1Vu=ugu9W&d-?kGRJ`BQKwDwrcg@(EhY&r z<rc;^!I}G`^~GD}K5m_qAaOp7C+<}7?J1wr0t)%gN;z9qzut9;=i=!T%dghI$j|4W zee&rg=@wa|Jsi$9^Y#|xw+E@XTTR@XrI{Oazuwg4s-bq6Zp(|(<=5QyXNtd0O|)G5 zm#6u6+QmnHcb8QK)d?BpZmd0SwpNMLf5IPuWs+jmOYc1{^Sz<9%wS2=Lq`eY8LCyQ z{&z=~ybOPI_wO4AZ}m??1$I5{6-NZtoYFY_t$xlMu6IguTLPCXJ)@r3^H}rn?(8L{ z@4neuwBB~z)hB3QTk<^iZ^_T;d9Rr7&WrQ!OZ@t5>xE2jbFsr0HXboZ>6|RP`dPtg zh26@9^P^IK6=&Y#a{hO$Wp2g3vbe{=>UxKFy}$cn>iYEQCk-;C=B{*h*OU2a^OJ$? z&YXlhr$fJMm);h*y{sns+3vk^-9p_Di`BN@<J^5w^3_kf*vk1+Dy@z&)H?p^=Lqu8 z*WcP~a(l_VlPk*jzFPA#aRlFf&vv}9{nm!*4T(Jg_EnljZniU<c5l&lN^VUPp0OlQ zK1uJO=315&^Z#_N^nbqNyQ8;RLA<w~iNW;)k2i}w-7s5BF;&<_x^(69J!^flEUVfq z0(majF!p*ZnWOPF?plO)_QAE?bzkfbO<K3XHfrMUkX*-Xg{ucX-JJSqc`UEou3MKD z2>s%Y+Hc^LYn*ho{r%F@-+RMEUM2e~mu;P5qNLjE5tsCR&Wwdx>7EDYtakeRZCl8Z zS$-!jYVT2-#u%hx%emu}&GRH(|0G}kJ%3EUXm-`GXgtb{S+4)K&*<!l+517+_Crcw z*<U6Gh7b<qY>S@LJc-R|0q|qvaOb(TVcF5rt|E2qIX6^tZVNPaDYZsr<xLQK(HkJr zc<V~R#w!ZCS<_QFy(ix-EkAtpHvh+$-|MdGzihVq_A<S`dB3aeX3oZx#SS`^GiJ`U ze17KlJj?WRe?DGczn&rHc)!vui8LPV`ICGjZw3XsR=Qse-aRd6)~cyNTv1t7cgu7l z3#aKcFO%`zn%<KXc+Gs%taaxke)0LPn#R%i@Z3rs)8O=s`BzHL&P@8?nrz9Ic<e@i z^9MzZgZ*V~+%a_vLt?_$x__#2OzlYFb=7;4;{EvkRnLPrFXgoDd?Wo}O4^GvtJ|G^ zcQ0EkY2_SlGVSiP@+TpV*FLXIJ$~tnP3N~Nm-JBgYh16`oZ}~(MsM7*>ga-`7qg@G zr3f}|d{OUg&sO5OEyqdnbmxmS4)Y|g#)peqKJPf-zE|i_%@@6U!WWMl6m5%D`SMt3 z?xet08}ZslrA}|IT+sIOXgBEi?iypqJf~|}i?&44gl#F+w@yv(TJ`S2%6_+KhXWrP zTm0V5ay+7PWMYDaf7xrl46|QsHzxh|xjvKU`njqT>47VAuP<D^Tk>vMO6<ZDuWWj5 zPg*P*^m&t~(7{yQ^{4x!E4NEsSKm8luW@ju;Nz4(9-sH+<WDT)Nair=@KT*CG0m?d zXZp)yhH@|2cEtTyxYO3P^yV!kHNRaAeKYNNX3erY{P{oU`ziOb&K_61bF^J)nV**k zzsJ1o@`g4R)qkpdt{Oc{KGQWLQ6k?p{*cVOzy}w5XH4c+oqEYENW3EQY}qB*9sTS| zMpL@JY75!9PE%ZP@vn|G+tMKaxn;|&{KdpJs3c81Qys2a)7BoMQ&M_9bI%3w>)S(* zda{4$XuQk2klT_|`$v;g<Yn_Kdyo3Iy-Ry{hULQyM%jqN;Y#~bEW*n#3GF);td*bE zxowZ^*LN@GW-@g@j-0-rVU^bfy^mIIvy1gpjC@ZCiQj+nsFH8jOqTDH)e@!8cPPYG zbFcGW!67WZvFP?~^*FnGcNW|+$v!$Y{Qa>jy*9z$?=sA|ZLE8B+g+I+6YVv2mle2s zk`^xC_vroX_R#-s-kwJ%WE(#Bw^3=c<uots?%y|c!XeY56Pp4ziF}=ROi<nLJB#GQ zZIWv)+osQooRM?VG&nPH!h+ui{x_K{GMCt@RJ(M=a)WmJe?pD%85i0Fw!VFr!ryAI z5X!Q`o}GiKzG+A8j$7rQ=A|fAtj@ZklDY5Z)t3L|C2M~@O8ug<p6klqy)nnEKc6&W zdp7w?<<_1Tr!KA&<hb(fv69|GerAb9O6LT5Le_)|>{zVQ_OrU9?A>vWOzuAcEB3#; zB~=>ObE!j_|4-Y-tJ8OUEN0%Vef$RNs%?_?X}8{*^|Joj6})vuneM-rR(uiO1#YQ6 z(+q1H{3X;{#Amz`<uf?{K`NH}u;8PgKYi=B|F=AJ^5KbBjPrhM_{6mLWMcgG{XCUB zZtL6VI<M6!xT@PW|6kqICmOYSr%tzUbqM{I*jku0M<K`4=R!tjnw)U*xr8|Zg3D%= zUkh-!n7WlsmTjTDf~WBP33oOA>fBiJ@XC(jl|r`;T|QFVzNCLg@wviFkM8jw6+U@A zLOXf;#r`v{7uj0UUy8_uB`2&9i%5O*e>K<N`sds3hs*vwY!50Vvz{xstz%(eILCvp zkR;d~K`Ig*i_%MTQ}aq(E0R+S5Tm(sCph{aG7vfb-}OexA_Xotq24<zJ>BljiiR%2 zlaDqFyh}A%oc4NJ9`pWb>O%Z=4E9qt%Chtd@qfFp@B6R!@BhZ%w~xEe5cBAQOWy;n zDf4`lU-`|Dy`U-l&F7Lv(xtN6yzhq;79_nXIGDF&I-8Hm_Vaq(LULTfl}*2d=LX$g zerU6N@Wc-dYu|D9Wb0L2Qe836GJaQ`(#{W7XB8dyiTBNH<;@WdY;X*?G*|9Y!jUHp zeKl?Yg-+9dIcx1aH~nah(L+PW<3G3m<ZG1NV6iOj+T-cgP1mH(-`8sH<VnBPE9K%J za8Y;)CzqaY^3gTxt_nr`HJGZm`*Bs@v3N0k69!%Z*X!ELmA-mrnTK8rt@NAgw%Ydi zGw-tem3L3Aa`%cnzO5^ZTeR!ONe#BLz#yKpTKbXSS2e#&+;;1E@teObva8+ICEt`N zMjt$*>f`>~;)G7v=IdMiK@s>n*KA4%BLhP^6TS#ECsfNJMIc%`a%xa4DB||cJ;1X6 zgY`e=Zwsah2&c9jS)iuoZPNH5?pW!xoV!8CzHI;bfd7NLq_?5Rl8YPLm+ejeKBxHh z=Z`<X@iCNIY&)nF(4XoMdFW6262a2}7sO9aVp09->}OplVrckf#{oYbqbZtR(p)U7 zl&2X=83|r!KgjWSO2gap{}cjNSK5Ero0Zrs5PSUh-N!;J?(F+DV~LGevh*pIb)RnS zoOV#fY2vxxoKtq&2<Xi9P$|gTbRlX*Y4Azc2iszoUk|pM<#zgXU$66==VHN8#(QVa z`u==LzfWJ`4ukcPKa)zObnnOS)PEitv(Cq6|D;W)&I|K@`E2@cVWh%^o=c^_w0!PX zDAn$Hyl|0)Ve0pstVq@^SF`nY=j%`RV&PbG@Qe1UE8+`T#BRI_3HVyNV#QomX{nng z3ei$G%@nMqR!U8XZGCfa-#z&kUq47&{$c_}n61V9DL#x047-`}MVJW*5$2Da%|fHY zg~LVu@rkRqcpeDZ8L>LT#aZdd?l6bd3#JMk5Myy%$agYR!YA$0$s^vsy>GSei;S<y z*v+N)Ix4pI)57ep>0k1<{5_X)V(!+g@M~vg7N5I!9|SFbK7Jox&s@NEKO;j>V(VO= zLnowH`p!Hd*}rsZ#UqKIdrE{>u9-PwOR?o)FLht<bI)ThZ!_7#S&}c=-|KR1gHh+t zUfr2RtR}Z_>v5E*?!CJxJ58ATlC1EH2m|A355;6P4I*}1&s*c5yQ42N_1L;g&gOG` z4AphZKSs{;`@MRN+Ge+f4wIR`W<NH3dUNKq)+xzVCplcC9<nyIK5|=iq+2X=W_ik~ zfN!VUk}tN1Et{&9SK#Y=WYvLvg_n))u4dYMQR_%%xYaSYus&XW&Lgwmnk-$u=C<0~ zEj)`v_gX2v5YFi3)6cYUSvU399W$=kYXSxSzIfYkfZgiW#`VnZg+7NH9Ck46OPQT1 zE3f47;^<Us^Qk`zWo0?a9<eNv@|%>ETxZoLD9kY<>)owB2mg{CtDG<Y`xT+?ml$&R zgr3*gb$u*;#v4@U2XV5O39kBLxa?t0Z;sc|7e^{%t`s;bge>NgonA20)bP@q3kNfl z?|fM0^`fiKuqU{Bwp-%4?z39!v{-}I6(7CRRw5?qy_DHHHDx-_@sQ&Bjc4zgth|2t zWVtnC=`HQ<2Ca>?H@{wKTio_{!v9F+D+zA9KJQM?s`m}|TAsC{E|-6SU)b|q^>bLx zU3xwD=c`ZwR^F4bFDoZbPHWtuxNLVo>AI}Y2g?_SRJyxMD0V5PKjH2!+j>4^%f+Yl zDXXV49`?yvRV1JB;N<JI{Dq4%(k@@(e3bWc%Gwtf1cNUBwvtQetquJsa?j&3%Xfw^ zH|iEHUd@vB;)X+4LS$y@9bf;|m$RH#X~c8M^!dH(6=kqJ^u1hPd#0R(Bu7H`_RF)l zCSSeS`<F4G`q$r6Pq!}n@Y!nLAsPOi+0zYXHY;-<)!BV*f4YNP^5x}Odck3ztIV?P zS7taq2&gQa){$nXz1pmKYE5O|-ZYb)IpS{?O`f#fr_`ZJO3G@L(GkN*IZyXrv%5EK z53}WEi)_aRe*K-F_DtsA{k*pQ@5a;ek&1nax7<F+Jy-2#wm9*T_2O-dxbD49{%?4H zg~vVon=8}zw@3N5M9=<X6As+r{?fb0E#^V_oSD6Un+`9JdmO_(ZRd}P2g?8d`6{d! zWq9zrAb;J21$%uK$lF@<NwgI-J`SDpgqf#e%DRu=4Ik8QeQb5Pd*U%(wOdV|>t4lZ zzV-d+{Xi{m>g4}NyzL&YWqzx*RDN$S=lZZL!)L8pjaz;%WXVgOmH)J6;<YS&f$lkq zmm`kapAeK#^;^WH9B}N%7Rh5i#y!PHEbfaWixu0snRfoH5}H1H(hr{bzt3IISj&{T z@rT(n=|}ySGY>Vt(40BddtKte;FbePmXqyk4SuqfYc@YsG2ZoEtSYA0IwJPjj(=ho zfAGoG^(XA9PGEoev_fU^w<#BQeHS#Wz3qN~(fWI4&u6|lux?3JYZL2l`GohoZ~U|? z`=wD`+Y~F8xbpnw2f{NJa;5#8r~JKg%?37p&xKCDzpgMvG&eW7pUsV1Ud>w?l{WX? z+d%cZ>w_m=)oG0Vwp&i^xZ}LZ&olC?Z~oe}u9SaOl(^pZcZHHqC-n;jX-u3cKdZ@R z4HM^7$9X2tx86IT?HgCb9~W!4{@tco`adKyH*U+d$zm|(+!i{uKkVG|%wWUy*Sl|K zX+2AyclA1C$+lVe25&)D28Mau_=;SDJwc>8G9a<2IJHQ{F|8!E$St#|xFonV2`UX) zsCzan8nk-*-!fetoztN^EB1%#>27!s+vTDAeYNM*mg9#vO*>k){ifar??3!@NsAm+ z4foCbu(+rA7SmC&L_uNsTbAk1&b+BHE>1gl@BI4v3_MA0jtvtE?o83!bST33YtIdn z+}hKkH`ly;<#y|mu%DXpo2!<m&EmPt(|0}mA$`6?UB~>ns(Dyf6072&9sbVUk>`uv z{FqkIJ!gr!&E$Z$Ya@4lU`R|jH~*-_Yrg1hPkRM=^7fyqx0`RjC2EoA>qw;*p>wMu zxt->=X@#ujSQOXXB;~Z&X3K_&k%o7}lT^>{h<YaDykvSy_Q~3bl9LiF%k1{9oxyr6 zK3!&>jbX#n8602x&Sn<ubt>Y>zFYh{DfmTZ;_@#`o=25wg`Io(@paw78qZtFTDJ;4 z9oZM&pRdb1y|6Vvndk7Cr-zzbcD%pyT|^<bA}ngY_{|8}br0K`Ud!|=Gx3@ib_@8- zlv}vj#H3sM^|FU=rL|HucRXeeRy5#xC!|(;?90}_*UGlN*>vI5uQ!)2G;wPkdNJ{< zq~e=UuQlh3KF<Dnspj`pTRz$Q&a8X??LMF5n$7i#bEC0nv0|InF9QYVGo?GKR_!j{ zwt92@$7vxhh4FPC7z01enSGB_-hOM5<Lmu;>rXwMkpIc?;j)zPdZ%UjrI+7Oo85c= z;xEbgqdx@h{r|kORIk`AOm@3l)2tG+n8R~6JkiM3zWi>wqH@ONZMnNTx14zDGp|%! z`)115Q<@2mPurWCSQ5YFKj&M%j>%tpZ87WQmW7+IEBQZF-re^&N|IlEomt(iz~IR> zejl_t8fyFu_D?Gh-ZQ;OSLiQ`snFZIjo%u-Gn#y;Z=Mp!6l&6DCFQRC`BucS=NDVj z6!t$dJYaK<DXKf{{>Hxomn)tqUpb%g`ft-}#+u`ATldLl+;pw5JT>VY$J+(f)#4XZ zEAzx_PhX5nds+MYQ1xy0bz3}|O?uO2IclF(^7&j@uGGpeyF>W6vrUrulUIi8na;=` z5VmpMz{yjy;zg3U^W<4xa>Z{1inoTOo$0OY`@{SE|NO+3mjbgD>cVZ({ieLsxU6uw zLZd<4=eX0R*zL(OT52==TA#G!95=ZV4{8=Y+kZM+mzjY<6>lDOB%xV^oJS!|q+&?k zTsk$^U)WLL`2S+vwK3eRqAc=)f|=Sf9lxAv)VzGOCNcOv>5aU#b<VcxvtJqh`t6(G zqI&TN|ABU+bNA*hy}`88Wcr<V=g$2pODik;{^0!i`wY(t^f{Y1JlG-XeZ@7?JIZvU z^3<)byQ*S$g{{nr?)F=&7P2!zd#BQ!wi5Mud%rLFz3ko1zAP0%b(;<jj#;7p6_X}t z$(|EV<5u5PbGnGTuyH~~hW71hpBeMJvgYUtJ?i$0oYkQ?)lDe#(BxnP^<#%m#0mVm z{KBzm!o{f5x3~JfT-SM4i@jpogDEasHDaRsofGfe-Xz`P9w?r7m-E~a2j7}|Q{J?{ zW_3DrA>?864Z*O`6<u3j?cBCXC&iq@@L|{%<J`$DcJ8u{Ii(II4Q^j;4{Tqi*V)~& zv|~o;*FekNt9sLtM5cf3i&K;-=~75qSrRN9nA_ida{cud;dkDr0#En-+U~7A*KC*c za^Ie0rB3OVnHej+9;>PC>~Y!o=h5WnU5i%aole%8_;{te-PYK-lX5OT`8@OL)WWue zbym0YJojz+swtCaJoloU@TK)rCcR4vHIbT>zh~y<pi9RRk6iRF?)$Li_Gc%FHwxae zlD#tFUJ{RWj{FdO;O!6}dgpcH!pVmw#mVV@Jn_J6>f+iNJO7k_dHeB#L)-tqx29aX z;QZQPWe4LlUiMcKDGGn)9TTd5USQX9?|E3Z<VJnpY1bHYw;1%O9X=p%+T<~lo71uE zZ5R5|>k_}@+I{D(y>-|-{y+1+&yC5AS;8|o8+VmojqfWsd%o4po<G^l)bu~|_2TxB z;PU}9yWO_!$TB;b=J0H#N?z+8&ubT7s~7#O^?CdE`NLV~IhoG={BM(2Y~5#Z-v4mY zGo9__kA9WTI#v^NKlAbq?$7$593U+ntv;EFf#D!KzFNwQ^c>)nnwDRbildr(8!VhI z@ISYxpu$r1lG9RGX<aUr7UeE|fu%_-*}V?ZOMAbi_C3zDn0?1^diR(3m-8>=mo1ac znHjdf^qBtNeyh9cmMSxXEw>o|o$$u?ciHa!cdN_h|NZ=XK10!uFxQ@fyI-~1b|yx> zKD1&ce`|71)w5%5A9z-0NwaPbGoHJ3(c6yH%d5Hbf6V%E?(31Jw^#oLC7VuI9l-Hf z&{jA1$F|pJa%aUI(EPbfpX2VoU9s}>RD8r1SDxAYHud(#zmZL*QdfUm{pGuKtJ&&5 zH@F-%cVs^j;NX;veQ{0Tc%rOU702G7%#DH3qW5Hrx1Or}DjIuw&AJ)$bk8#ePdq6k zrsO6a$Hu0R_;%Gkv4iK9aJ0v^zkcv@?JI$6?%n)n+Lr~I&g}~PWP5w%6Kl)lz{mMJ z^H*2L9j$Kp(|Bh~(*=VD$IgGhTejA^)^1o(9u#-cphZ}#Pgre{<)x2`K`nBZH@WP! zo1=WjHShYyFe~2s>!h}y?X6*2Zm{N&^126djeVCboIAo>{4Fl?oxb#nIq|1;->pb3 zH{~Z+oLrkGUYmIEM_y7o=krDCW&TH59=aX5b*FS+`5oWoi`PH7Zn<(X|ND#+kJh~D zFK$Us{44k|y7QLp+p}_4zop(R6klcGKjYr|{g&%5t>8R9|NlWD-*np@9u|^6{8_iV zZ#%V0bNk8(QkJpuGXo5LJ@(zul=`$|(W6gtf!uzwn}5vEPuaIDOKtnT5{^gT<L~x1 zPHWRVb8=U#@|X6Ci2^bGc6E8JOsf_)u>Ra6?Xq;cjdNV_!Z){fq<-E1?s`GNY-8iR zDp5-w>sKevK4NorFTdTO7*nYn!|A?O{9yN{7?uLzx?ktZyS8s;J#q3#a>IhDciPf7 z#^gyc%RUp{#KQB<_qbr(!mMkro5Cml3s{~b9~UgvqF<!7PbH@I^45lK@k5tybFX=% zedx@=*|VoFvt6F7zdL7QQpDpu*1<-a*KO;%l+~`=K9hfRS-oajk?q`{qES}H`&j>` z+Z``x`QPs$DfwcNr;0{<+%w({Jzpmt30&~=NOM<ZOZi3liodh}mK%!Rme5E)mdSlw z?DBqx>K^`M^@mSN%u7mflMMOiwC<?fZ^7EzC#D(Km|R*jzj{mW%A)(MPx)iCj&ELi ze8$Z~SJiK8Oj+t$7I5?8ocb>|cZBCiys&AnY7C##ZQ(k-Vw=CRf2Gdl9OFML!xGLP z7UwzW%NK1|d&=KXrGS5Xz>_Qeo8Op;EmS!;Z+g=cw}?dsp|dk|-!HnVd)FXpuW#R< z+G~IQ%cw3he$!O(c*o&6OqX~2JoX8nBC#(aD(s1w#S#7M1@m8C@0#<^`3KuA@Fd6^ zkze=kGBYqR@Zu{}LP#%EoD+*vJ@blF^NKS|GRsnfONuh{(w*~j3lfWvF475(_7`>( z`Iol6WNFUQZ4F&v0fAM`qVe9dW~iz8N~vhR{2kC{lwy7ROy2AKkM>`Viu=?U*!`Zr zXU@&L4htlb4;_B9^Ni(v%lnp_?dxj)vL*0a^XarTJMi1uJ!>|8*!v?y<Ne}ik2hYp zcVK#)wM6d@tCwD?d^`DC?`3@sC<)OB(NMM&-Rt3Px@3dlbsMfN%{<Nz=lJh@UTpfs zPG)|$;}eC`8-%LWCw|$xx}xpPz7V4<!y8kx<||(2^K;=dn-*iS<M@W0E_olf<4*D) zlj99Ub{}l`UAu1S#^7`{tI3Lo6Tj#EG?AR+m=oCh*6Vv$q|nVd-$Se|r)+qY@bs3- zq-gQ%iW31|hW$<F0$8JF=t+d-AN0Q<w>!eTvU_^vm2iW;lm*|;I6S+gxBmX648JEY zHJM-ENtp0vndN&!Q^~j;8V?wq*B4zkUi*#rpOEu9ai1>^d#0x~XBRu{US_Oxce3-* z#hsA{|K0z!|KqxE&)qLATDU`YX-nR38y?pe3(jr$U4KWy?B7AY{Epea>5cz3@#uW+ zIvJ&MTKS}h-j#g{-)D+&>(y>PcCt>%f7fi52X@mujW#!${G77LSf%^xG1jiNpVW1x zq`TFd>~_(b#qmu#lKaHP^KYZddZN6If<qSD9=yCGNHF!1&Z~n*jdKp@?O8l)cIH`G zrKHJcuD`k;m~~<2fzLO;9((yDng6Na_34?fv?qm~Dt%qH?%Azn4vY65E7Q?lG3m_F z=2Z(Dzk<i2A1wLpuEoT_upaMCJKm(HRqwp~@;pcr5Yi|NE=ep&g)|1=M!YN+2^IUl zcZsv<S>|Nriw)i@87G~)w4fu&z?tPp21mk-lqTbhrMrE&tV44{OPO}`-PX~{D^z@R z?!*g=<OH8~iNgDR^B%L?|Cax8bic#i?W=ZeE-Bt1Y+q$jviEEG`@eUe?w!8AzUn<Q zL-xm=O-7Atr`;95ekp2Ed_R9i^DasGO<PN%wm<HiB6vTCmnS1_(_MqFb7#GMQFgX> znQ6*9kKJvlzm(^$d-jUUiD|Z1i<X1nglXp2UfycEsb1jwdyc~2w`ZBSYb+W~_e-5{ zU$D1ZD7ZPD<--y6<p%{ES|8uuvN1$?nc*Qbo~)HU?>IuXy}!)1wc?m*(8sdFg_(JY z%P*#9<sDNqG~K++{MX+pY2S`AJ4H#Hduu*tmg&-fN3Rc8n@cTwxQ#<G+xxDKzWcOS zdxC0B<=L;6a<-U7d)6wloSSMPeUal-pzcPI)0%s|Qdb#hd$crtW}W8ubNkh8TbbWH z;Rt)+Bvv}np)xmCT)S}NktCgm*Om&2MjTi>Rfy}*ohaRn0)ly0J$0HyEV^6EQ`o9X ztG8d9y(pk-TQsNc)ulJtzBeBfVDCGyE$FRgoPowHrD->_zNfO4To%0%87;A{^z%pW z7M&RFLP<UGh)c_NU(Y@_#bi-df0H(g*`FxRoqMCdtCyUgX0T3VhfT!A8H-d`#phhP zsy+8=)}>3bSGUAmy_$DD^lZphh6SDn*-kyxOfLWO?y~Tc6KOGv6CU?y&S&ow`_nk- zz1eBG3m!jtU03YdS!<@SF?@qoQ)$N2jGbW#k%otMNA&6nMHk9)`n*h=fBXH$t76B# zhMbJ@4VCxU&Hm}y3g=mim*r+(@Ob4p)g(lCdE$~x!90iEP0}Z2jk%{DDJz*EqC0cW z(y()rY~P%jE3~^{vbfoiDyyZ%xw$LnhEL#V*;*6e!p*4|yOSex+wFz#%Qe}QUbyu) z*>jpq4V$d;tw6<1Qz$xEGh0Kxhwt;J^v?Yswj^0Z&x~i?&XJ>eal_lT#}h8?vuXP7 z)03cYQdYVCfbxDvT`kdtD}qEa5+`iFULH~0xr8Inc|X%Gd6V;xLnaz*=x0`#ctLh~ z+S&u_EI&=w4|f&PV+}rdVCJEv4?HGqn6T{g+@d7+f>z52l|*k_iSrY5ShHAeTe#$M zwy$<NVaOL0l4`IdAt2#y41?;o#|)9~CohP`<Sp7-x_Q0%oa}P1J4ZV=+RQh<ZLE2H z!JG5X{UyE!$e!SR{`h2Idr7qZLbJQ-AH(nXGbZlXwRYz;-2hYPmD>{Lyr>Z1*RJT; zqBm#i59xn?CNo0}SkA3ntGy=u%sqQmxrau3I&9_`w-(Azws;cSRCo5_tiviciXT<x zExP5Ql5_F$ws{vKzUG}Z+Z+AKNAc$c>0_I@=4mbr`st|qQD)x!Q{0zQqfc(%>mYx) z>%B_qoxItWxnTxQ;Y)7Likod1<aw9xF~`PjyW_)^EqcQKRYpD%-Q&eB*ln~j&P2>w zhRcVaVfNvQ*av&&%;$CYWxEq-Vp0|K?<mu~8Lic~a?U;cbgtfe&q+Pb-P7zP57vK( zXU%`UF4Nd|`VV>QXwT09r&5;pdC8==9DQ=-oKL>MEWtnW$z1nDcT~MQKFdX{kW2UU zn+<1Teon~L7caj1wRknlmsKC<DLa+?`^Pfl^~{P{(uVi<vD|xp_?OJCXC+aR)(hj) zKiu8DieX9T;+{RX(q`@2Yq`{tFZ|-FM;Ah@y5=2bN*Awg_E(>GDC3q&$fXT32bf%b zW;(9bu-Dn6Z>zmW-&SjnzO7Tn*T^e+&Z()Y`+N4xP0dj_tF!pt=9Xg`yc>TRh8#U& zajx4p$@RR2o<y@$w!~r8jmHeH99iX@93i@TorLq-LViQH{o<2yIs{#ge2!3yTk!pw zU=_#Sj;W7SuXIlj^v}LFabAo6e<{ZGp$}gk(7m?jPWHxkitCTw3ZGEs+wr_KWY1UG zjXKwQ5{{}|`|5JN-S6S5d7p0V-m&Df$~w8@%lKb!zA<O^y)X;k+^uy?xv4MI7RWBH z)ShO{{HRppkw)f!wy@6R48i<GzI&cV20ZmXAAO<G?~U4BmVX_ynPhi+Ry*FGs<KqA zd)?s`X_dk|A_KNY*>vu;^?A2;v9YUu;^(8*D})vY9Od+%^QDoguqi&Eal&bvg$@=1 zpUULY8T*C*UHMl~_kZ1-NlE&GCsqERaFILqDpTs$8r7~%+q!<MFZ>;ppZ|UPT}6kM z-EohvUAQ>sDSNTpj@>)h)e6=-95-~4R{zj&z&+z$-2Dkh?N|I>ap1&x`yU4V^^F!= zSNT19=_&KBbDu$<xTIG1^hN7a3r!+i*Xs18ew=o{{yDeH%24Sqn=A}Z-PPQ8Jvhi= zPyf<KBCIdmpFHb-x$Hk@+mUn&6}#h?zt}46TVniDyGDKAvG0$h|0wMHS`oT8;IH9> zt*q(>eUp#8QRKSL$<?vRD<EmF_>WbMHH$WwCw=AZy}IyKSD}yG{HN;$`M00>U4QEL zUk3S0=Fe0Q7)%UyTi>2o!}G8En1LPV$FtQE^PBotCf0~`{Q*yKxm9bL`LQ!F_=@0b za+1-H_023U&df`PROF6D=}5Ks+Nt1%=JEfRr%ls~*5g{C(ZzLxd($IRMpwau6510I zvL4;u;&W=&-J5A;jWd2MT3;tBuM+4{^MU!t!k+Z9=}`;xeLXMFwVYgB_xXNt=Knu` zze*pFkxQG9Q1Ni(uA|a>w(06jf6tZqR`Xs4N9t9X+fm<Qrk`HBN%tsQ_`ZP2`8oHu zr`GNjyfH5(=<C@AcBN*K!!_+oGB_VgUW(&Lsfc?gAgr0$s4MVxqwd>;iHWNBZPx`P zdgxY1A94_?RWmY>oL9zU*LS9A;e8kWOFYcCvU88G<@!1;T=vwlotwfR`?YFE-~O1W zbz@s?+f_gPyl6wOOAAihhp|R#Mf6X$pQK;^VE3J(9c!XzH!j-HdZ@a2OW&*=4T)`W zx9yAkibAgk`MVlCv8n9YeYfrQvt{qHQxjya8|^stVNLY3PR^Kg0~^nCyzVdbV$YPX z`M6`3O5QAq!>Mg{{_83~X*f#16}o>n^zn{VrTL|44AUN?a;)V5~+;uE4fU0ct* z7W-etwz$Y_ZRCm<eY@AHubqA+zsh=*T3D_v^Wx<ao2~MBo@r~gMwRV&&u!nln`iUf z%!SYLSGOg;xKbVTHA?4|@F~T=OD-9_Xj~R6#i={bZ&S)%6>Z1t_=&QIG^W)lZTC-O zV*c{x`oc4g{Kbg|osL%0y>|><99pb<Q@>dDKa{YNcfNe|5~JHb)5=@?K3fYe`T9wT z7ivBU;666zddYsV`P%;+-Z6xG8ME*B_?IP5|A?<kpKpxbv6r#@Z-utZKN7-IC&Yaw z%OG=(9p~RGiwtkKq|KJ%);PApeWC1PkH@?Jzcenf`0dmCN%hFgiODUCn|ZtpQ)byr z4m`wXxVhe`N7zmM(EFSpTMF8*8FnRpNw4IuTJ)^IerbCJAAis>aKo0Xan<`-ObiU3 zct>`~XxKutLV03QDn`S0YiKN}?`3=Wn%K0dVk}4W9o9H`r!M^A8R#r!pvF7tib>=4 zmt{KMx9+BuasLYb!}{w%v)Upzq51~<Qx(f~wtB8U`|IPSo7Ugw6~CXev;5cF-_zGK zO#7(NCUCe-c{N*pL9xF?X`MpO<A6_Y&K`%?M7rHud3|TBX2iA|&n}4?{oL{}vTtwQ ziKY94zwA&?>M&Ttw&c|TW1gAze|fxH?-y;IFqQ9^jPsS-o9{dciq8p`<=UNgbK3U_ z2j}o}@kM2<IwEk=TGWuEZL?L9#03GjnBxjUJpD~#7gj1w)V>xdn=X*rv3kYYw7oh{ zjN{f#T(g+<_>-+d)2D6vd$vQU?_uGEBYUkIulQW~d3uwAca^#!&*RUnwRH!2rM9nj zyEu7T?AfJdrdr<?^?A=ok=StO@&kkEp`qF0O}mb4+%EImq+qxB>-#U3Kh#=x)Zp|6 zi8oQDDur|3O%B(R*0(w7z2}<9ev2~C!!kjJXTG1l6uP?4M9f1gZ`PsQufMz=)cL+W zRxnvnK49tB=kq4ao>sCqY4e-irYQ+4_APa~_viaT&Z_l$wbVW)pD=pzujTcU*D<~G z&PBhvvRzWD*6(k>-^290y|PgkGrfXyqOwZVcQJIYx-aoXeVcB|zGJcB7jpW)3N<gX zy4kt*yUXP(r4bV)rEVGgZEP`EHcwJvVnDyd+ZI8^$SVQ-6GInph)(y=mvQvp>@VS$ z-e>S>U#qSA{==uvFI%uN^T<t}kBq`klzEQ5<X)>kRoP|EbHkU$>nE*UwCH7P|Besa z&WfwrM{H_VzxAy1k<q#d^Aou)+nu*7T)iUa7qeSOR$JI#<GekGju;wQp9+#&RrAsG z(ea3TAJ0xLe>-PZQi3MWh7$LCIoA2t&PyMfEA~w9-9;;JuhbW!JN&IrZ&|l3WVW;3 zB9qyNqg;N2kJsIO!M1G`69dC?yjjGR^u8Y?i=YooEDeE8&#jqKq^t0NRWvJFFSe`0 zYhrik<XLP!Ssq{B2Kb$tRJQ%*-ADI7w10TSE#b*?{-6BfeG<=e%oTLGj<#<$PA^M4 zccy%wWqf_@KPH9VbSb75(bIY_7io1_2d(e0&3yGfH*l?2wEi?++w4%SLwC+eFRRhb zala)j9eRC(&$jSZ-I&wY1eCtK>#|)qy*2WZ`L?pui&roBNqksidR^o7@_#MgR1cfn zNp&)qct@pW)3t3p${pu8Ok$M3thjtFd#Y~ce!=Ke&ll&TT?6llY+LwkpW%~t5`2le z|L60ja710aU3e+!(WRHKDvj3uQZG()F<g<@Fd<9VtVuCOy~X>|TIn5s_qiC=a}_)8 zlH^rx5f5it`ruYuq*be0PvV_`?M0J6y!v$Fk8!%%PRV1rcP<*ZN&K0t_|!<)y;SsI z?49JQ5T3~!W}TS-e9FoB?(2H^kIDO_*r=To*b+CP?RU#pG3!%Y?$3POXZ1als@^2I zPC|%loyx)sMls?l-wV1EwVL95gdcU*Bzw<H^ENvEWZRx<58<@vr>{Ie$n;C!cZ=#x zx~%xMzL?ka=L_@bD+~g1tL%PpoL}#;nk|82u8joi>NN4E-p;oC&b|{hL%9|%PMG5J z@Y<Cf`+r9t<lPgMCAu@!O?8s7Z)W=GLxoEgC+=*mTrch;vAaRU=Z%}ij?bUj`8W3j zO$e9X|4)(SO7iL5eD_}Jbf(^ZqIgI?l2hT({j$_0`7e@egUTJut0E@tva$cctRAc> zzKrieeapk`B^?+2ca-*g{Q6Sua?%S9!>#@2ds_GmcGed~nTxak>eKk^FsH6+`MK-H zzn5#}vT3fL#Hwd?bYfsnadKu~@8<I|*$eNPu6x%Qf0yU~!#yu%&9QPTnRT7<+4u9? zPOLt?xj<&|rU&iEBG_s|<mUEw{gOSB>|fpF>YujZNYRwHhBimJvX2x^eQI#pwC5jN zfHymbH`6rnvrG&Ojd=5q8@c%hqjp&u<ePulL7?{W4$s$ZCcX=6F3UN1RjoKWHB3~X z(V^>>m!oCD?H$VH+SR#h&0Fmc@W0~hWVUaR4>f%4rqvPW6?Uukv)ktU@_Em$Kkc8- z5SO&Owc*3A4Vi1arp_svv%1*qqxkAmlXB;zSjmZ1t~+zmL)tEEThi}8D@t}5gr~fj z@>tBt>ATO|)fT~Oz5UD5=U6xu$4kHE<LLdD;Kb2>l+!&UVQH_b31@cPmx5{jH|v5f z_i*kFoVX%q(VA~H^LXyAT*r~`-u<t*|AJ;u%Qe|g;opxfYia)XVB4;GZMRmzg^}mn ztnA+PugjINUhTVU`+_?4)bEa=*Yi7O&VQZ$G;QWa;r4r9#lNQ+bUH)`J}ord%(?&i zz29f+*S-72#`(7Y*aq3ORjYPnJv2=GUKX#g=DvZ)d5-n}?&}?TC9iX5TAS2|)tv|W z=X{v9Az}i{^s;@)Kb@Kw0%KJ?f3<~n-VSGNDu1wick@=M-4W|Iw)aZC|5bB6{NC*D z{Q^AC9?drIX8*b`em?()fDP(`4+ZD72+rfmYrU8cN(WpEre2!D$iT3a8DHa@jA>Lz zISLu(g^Zjn4Tc<07aX;1P4-Gwt{<ITm-M!1*FA8*%5JEp#$qx<s^-(Cy_(+JcAtIM z^l|wQ?wYACUW+>I5A08Id7c-}($(44es6Ad+PO0`cV5n~p1+@IhO@78SHS1yWvaIh z#Z3?M&hb27CEZaL%{J|=)wQPkS)A)PzNq-Llfy~UM{m3G@$X@a4t$&?b*be~+YO;< z+IyC5-Lp;cZN~-mCbfkdejLBtyI%0u-x=qRF&{5p<lgc!C_hF&Xr1sS?mFjJFPak- zx-Z8p4_U|GV348ET$ei6aKXa)f=eqhH*ZyIo}P4es#kA5_jWxd_q)OdvTLmsA3l7< z*>=G9=`5qFM=iPGd!mJO&soQ4ecv|WsD)za$`=;x+*0S{rDDyG9dM0NdXcEoq*3$K z<#o%%<Y&QkYabdVep&JO<FdzQDGQRB!mswI{dZcwx+9xyS>U7T0duQ(ou2NvAg8f1 zFj&)v*;qm~MS1`0GmD%Q@+KxoYG}njo`3JVi~fRNigyBJ`8P-;-ZtV<f6k%rrGC2l z4%=((e`yJ)m-j_2Nb+x6wMd{ReV%$VYk12-9@9I@A^-h3ms_l8Z9n__-2dAmTv<17 zYOM%;p5JDeH|MX_{ji_Q9<iN`onw?<Ho-W(e8PRd6<yPweP*9u?enWshS9mlrzBJ2 z7>7XgDxRa$w5BA@mN(J-+_rdX{=GjfF^OxMH@`Rh$11$Kbz^KyqQU)xA?#22HIBV_ zqxj|VzF9k%z8>Z3$vfu4xbmvks>nZ~`+9CVe$}w7ZkY3O(&q=AG5#km>x6DyGt+oY zQr>Tax$1@@$|uqbg|`b`x>DyVR=Y%L-~VMF-j~&!d(2n=bw-K!_l5t#JxsAB5|_)E z7#Px6@g;Jy+tZLlj^1Qg8{+FP>?l!}wktI?Qscf-RDffKW*KYGbRnTjD^_M6SkmT_ z?>uvh_qNSv--*?8&)HMCL0o@AMv~3%#XawHv^iQQsHGWOzrVicv(5Q4@7CYHU&jz} zco*ln5106}`ZBt*Vy$=!Ri=J66gaz#XU?kUR<8Rm3cOnIM(bx7%R(Np^4Sw#ul5qz zvC8e{G7gs~>}ou17o7`LWc*jm+Pu47WA(&N#<r6dx4L4^?_7~Caie6abgrR_(M?;n z#V_PNxH)-l8WgrGF8zKd)AU>F2fsJflM?s532)u5&J_Op?gGK9cPBh7{ki{whQW-h zHx^$zJ@XUmLci37=96?IXB?Q)dD5GyWRuyl!s$Dzg03#AI<qz9k=$Xw`32odQ&dI0 zM0ZWBR$2dOn?dvMs2%J)Q+@VF-MO@9!rjY%pXc->^{{+OetGRn<ChhM^WxO6CEn#% zT9qvS&+2t}$lI97PWdjYPZ=Eluq@xqe9juRg9b+uLjQyXOkE#STf40K+H+H}wu4>K z!Ae;#yEjw{KhmCFwK=?BH|U6-rfTyd^+N$|YBFs#^;0CC<y$H#71`$6C|}}~^8EFL zx8QH?M01aM&c=%^cNVd|)|Qkp+sj@uVYY<Y?RO#xESj@s+Wmj`NdHR6_B-e1Cf)hL za`^|3^2V*d)_*_bA%A$5k>7+TZ@+Y!^-napb>Hywyt#>H?pI%a`+2Omg||jP`Hs^p z{>0APCq4+S`6y!cBZB8eW%^}3h1|1`6M2fH`%gTYa`Qm4j=&xhv6qnzhP!+Z%r9xQ zU+UBJ?BUcq{yu9iSsV@eZB#9FxUl7#;EJd3g6=kzv!0I8pW&xIp?9t5lSh?Pzp)%U zIm>b0x#VfDK7aW=_b)T3a5rD_yt1B&fuR#`x_2PGlE<vxC!fu`>>$#5-uI4GY49XA zsZ;d}>phi|HmZs$xGE^R<}MA1+%Vf>@sX*veaG?y-ydn?_0XwckmD*m&Nsp2=9Gw8 zJB!~|+s`+i_4DWJ-~0^v1u4qz6_PI(Y9Bi!?P)F&9<)<_t3?0Ovb_h&ZwE?nnZEep zHIGx)d++qtJSCyb!%9>1k4!CKUfA|7^S@ISXX4iz;dP(7Cak|&&9YjScS*C&wXTSR zS_@MO)TbxLzbd}Sud?t`r;Xs3mT$gxm6@u__G&Wa|Kbz7zqURKjxSm^`EubS_pS}0 zum1?&_1&_*ZS601r-S{QCoyU1{Hxsj@B!Z{vu$ZPX|k?&Tssrml@b%z80&wzU@_BK z*i6Y(Ibn;#0VYp#(|Hfm->!Fmr4m$MSF`QJ&q=?fSFW72J<FjYHkvK+y`{n$!8v&) zD{X(CGjCsLb^VHoAIH(}UY>XPTx3q)IA-_o?55jkk0)H%BiXva^7zBM^G~gj-8Qwo z^2ei@vk&Y%JM;8Ji*+)!&JVfuiwY{t<~zEs`Mhg}Xu+L^oh91?yc|>eB$phGwQSA{ zG28#>+w<%hMO`b?R3+77b05ofG=Bf6z-oU)xz4}hPk5~I*~*ykZMI8$j8bl9)EH&n zc|K$Ija!j9+SB`%zx>0qTSj}&t;392(=1uoS45u!HyeJJ1UJ87WMJ5Uck0cT^pq8v zmyKug?Pw^p8x|fRrs1Leh*xxLEEo4Z{T2@nElx+FE&TgrZ%y8G>z&zc-4Cz+e{>Ym z>*W8(@Q*Q0uIA%W@xRQ+EN}CMaLwxK<i9(!_}m$D<NIeU@BaMwO}3%%PN%X<#giSb z!67bnk+V`aD&-bm6`k5ua<=BA(ek-PkvCc`Pfz1MwAQQq#mv8#%EI2Ni=Hof^XuLs zbsI_dmJbVDH>im?ZHbyLT&$a9y5Np_OKY^smvegsv=8ZU?#|GwT9+#RbA|H$_udk} zYh~oOUJ&SIOcI`!TRFKU{AJpou!nuYX=?5rKGv)&6?^hzWjzGaF0FMkD{XANeeli8 zj>W#2wN|$lYU(ak?vhv-{qy|wQ{I<qwWfIm6~2Aqs;M61y|(#m$M&31&dqC=m0X!( z_&e71rLEXi&FvA|-nSozEjK=2d3)o{Ltmdgon^kX%6{fKnfB=3ZrLk^jL$h{C2uZV zs&X~qQ<D9+LyE!$Yc{l)2`<Rq(NrQ6&bfH)*(rTIJWk6sMAEL=gg<dvd8|{N|MXP# zjOX_LAMI!N`uLgVy4+=+`+LiaD|hzPWURFGF+JF`Y8L0OHM!p9yHr+%M)p3wHUC83 zA;m8;pTzE|-1%MY^`rM!>ec#?lM+vw4*!|eesT3X)$F~m&i$H{uX9?$sf&Nx>nEWz z!v&{#HgCI_%9Fq7_=&tnEry#qpIFJa6inK$$;SLv$?N^Xi|i$r-M?H^|MGceAo~GL z9)^9(m?o`XIYBW#_+(4vcaw803peXb4b}KmrSIqS_CmUR`1<*5J6bkdh*!*NOuusD z&`)#rIr~j4+`rG!e-!kfW_s!ijpyt46|c(M5FRMvT<5wZub#p9q?wk>p&JR?%r0zh zJ*_k8cSqgRFTAo}_9`xQku_z^J5<mxHH~d%{1(Z>Hz($%T<1^O`ucbKpZhERn;IS~ zYLMjpEdTv$N1L=xzU!4e{g2JI%m<at{x+{J9%o`;xQe#}M#{DhNTCdEYe#X{gj}8Z zzy4&(yE9L?6la`J@L*9=e6mnug(rhEL+^x!UMJ?JJ1XWU52UD_^J(}|x8Raiz*^C! zYp1rEZH*G0+A@P{T9o#?vhBO?=EdH1U->TY>e|{J?`~af{X5^fJnp0d&yU|Ti{D$` z|6Ozcf6a5ge7WkM3Ju*U>)Ui3T@9`TW|l~=otu_Aed9@<kUT!gyLZ!8Y`Xe>^&0N7 zQpv_`srwps9^z;{JAtiTA-{R!$rWFu8~aW@)s;STts^04gY_eBwnN<2vy~L%7c?86 z)>$>L<9z(hgpzRXJHGq+8U$Z*&TLwC<?5_d&WVAHS5Nz0-SBkUvsXW_7@Yg?yLq;B zP4t;<FLELjR<k}{`|~xEH1ol;jX%8VX5W-Gist>gtI0|^%_Bfy>k;uaTb6~j*34P< z;^XCBgTLD&#D5-g>%N~Oydx{a=YaT3{S$gKcLqIJ!M$y!T#tKW+h)m@mgv$tt_y)3 z#VoD4X*a};Zf7Mc%~tC1z9F>f7H?ztafyl$ftLx%{^8w{yTVp3xp=y1ZCm|P!A~p8 zt1m?+OD=q9bMVNFzo)B}?%E2vT{hXZC~rq#%Sx$p{x=UUyH{}J%_T$L%PZ|~2uq4K zujt6)S9{DPJK3Q5+f*Iz<;$v`dGW8EzjB3_<YtzsvhLb06aD04_kNgSZZWI!%KTm~ zRZIWFt#@9#FvX^}SkH8k3t8mk`aVh4vLI`Qv+$)i%h;zUEL*hOr@e2pMDN5juA*I2 zgAP8LWV$%G_?h_ZyqmYDJz;oSDVCpUy0~+B<ISR<E3M+CU%hx~#5~brKYMt>!nRvC z)S?7e@V55ErG?9i8A|tjFp8h?J7P{!&6beNnkL_gp{r%17cJ%rFVgW$T9K|1Qa876 z|IW3Y;qRPgDR6}?51z)x`7AxJp)FOtd0U{5l$3jF=w!*0N4aV|51Te^4pQ)T7hadT z!Cdm6+~l7&3so#Pl{)U9e=+R96G!H-ReNHmu9!X1WL4g@MUjDm>&|)Z=hilj(T|uC zdDAC8V;j5kGog45nao$28?W0&G?#ka=51Q;WwiE)Pr0)5&zHW-{_ixnuxeqehJ}@# z)vW^|#VeW2lpLKZ6HoPBx&8U-&KC{-&gnj0r&jgx-tg$*nh_o0kbYc6MSMcrx)6q~ zo31n~<XyR-yW-S^+b<UQG5iu>3f5eI`bDmxrEr%EzqV76r<6q0Tc>`7)qCzR+nc?< z(G;h)q%yhxA%A~Dr|ADIUUTkM#wHuL&3(A>yf1&=p|x72dBP?0RZ7p<h)yy!W{%n6 zykP3|^^xZJZ*`u2jkS6grg(W*4@YX-)jPIU?~c~KF+XIho48wAQ)A_PORJ`=%lj5R zUcF=f)!ey<Iy=gjbygg6KbX<J=+WeFbKG){`|(<{gwI*2BO8CXxyf*wcCSy;Jo%=d zs*<;7<}Bonf6kfaUK{7SlIxRsiA_^1&mR2?X?c<N&do~fczrc!e%F7w(E6`0oA=4~ zzx<&lxBNnaLfHY%uqCHI8gKs1$<&l;wzlk{e#pBuk>UHcEebhY&T*5o^rK%+p@v9G z`-j{RjR)_qf3yt^u{p;icvzf6-?ZAaX1;*!rAw{0cJ;F*iylgDo1=4L`G+-Hlb-tp z?9vuJYA37zz_m2t@<N8&CqHcwv}jzFrJA(AkCErnX%B;I70a8#f-ba|>{+wxyrFDb zOMA4=hpd<FR&jwd5C2W@*!?%{NQiZ%$MMxa1bM&AZtU||es%t})#qLww<>xV`1G@| z?E2Mn&+xKIHJ9bD75K(0>psP3?PZSDHX&Q;S3X`p!M$elmLq0)8$bPdQ=%@{|M=ZR zj-pG)40FGyc5a;H6i}?Dc~n4R)uK<AO--t<+>&V0u_?|8D3Ht8oE7ovr_dJH?2GN) z7H^d=xZclT@7DWTcj30|Zsj>en^k8_+Tr%`&sOW51-S}^D`(9$ToAH)i{+}rYuB01 z%e&eeFK%Xa#>HP`t@CQ3M}N=lKWbI+dP1BKkKnVleE)3!v}>&|H{CP;(w10hBVV&z zDLWAzi?ttC|1n<^cc7X7gZ0v<%4M-L%{&(`i8TEm{l}5>&*K$-2Ax{s@{fgi|4+SC za7kVEfA9amgp1e0UVpE5)IVwb@4R8lrrUoGxNX+0^Zpf*c>DOO$-nmVy!#(mZzA)2 z>5DB+g1#G`e2^1PG*R&`_?p>PzQjlS#QbjO=`w{z=Q~P1&XJvdiO*=k)mb}&KAqm> zy3%^0yU8(+qNh5(B}{3rdOfEbzVbQo%xy<Y!L-n@Bi2)%yf%ufcwU~v@hf7Hsrrq1 z!Hz$&p1Lg5d#;fYUa<EB&-S*OY3h0(xjOb~KZ%J|JDDPNX|3Md=}R-e`&In*o)@%e z*^8hhOwaCQd^0kVo2hl#bf#GGU8Q^jrMbIAPP1OQk(%>RHgfL!uU&gxrqArUd-So& z5|#`jZPiU@cE9Yt8nteU*G@grRVT|XZk+n++Y_OC&t;E3eAEy%Z|&)$SvOCn%57g$ zI&<;qn4MFC--<D*G|iUTJo!%P;|GCnC!OAzdGytUX<2dygOq1XS!drPyG8F|Z!EXC zsHX5#+wd1btNAv~DXaJ25!{#;#iDGq{rHw+`&acCxRqurYI*)jGA(nN71Te!bN)qh z;oplT_^*k)4BS7Z@7;u(d5kqVYWYvf8Rw-RR8f0pHMx0Pgm_%vwYcTGMDM2=U0+sJ z_-c9HrSE$z)&G0V_{K2XET}wc)k|d?JI2UTD|Y{zQ&?XnE?8FbZ>3_RT6|hv@3}MI z%H*0ic^$kW<+L;A{93=|9VVOnR4wy=r7ph_r*-q%^KE~<4%c0aU1B^-rS+wu@7=9F z(oLRslROjZYn5)UD!TG^-eOhDt@Af^PglRg>0DkV|IAH&rrMs<{=Qi~E0!Go(Esbj zvq=iK7ik1v?0NK|M=5^Ci4W~Qi`Q;n(K}Vd;I`Hc!_&Q7D_{J2D#;ka^`%?>yjGy? zuAHFoX#a@2PY<oGndmoNpltQ49tQ;p%|{=DMSM1$Sm4@t!zX*nu_WDFKGzmWJ~hsG z_&lSpyno7Dqp0Y!1r=5wzd!1+XFeM<SKxr(7PDf;#dS&@owMgw?1?CN`qE48tLXj9 zB5ydi&hoBVrK~4&G<@NXj_R+C4U(JeZfIXQyHP7{@^6;X<)42h9ckAQ%legR`!kun z(e!P)fv=o^Nm_>6TtTzf%!$SV-&|{EpP3jd9Q<2FWY(KAd)`LR`mt5@rQhKl%i}LR zS$JlDp4pE>iBVFb$2M&Z6<IR(`V;BpxA$DjGjmE?C9WQILi*;<&0l>UznOQ&{;;mU zW#88;*AIP>HOzmenYI6D(W!vkQ}5F>*ZEmo6gu}jS6|wJZ_|~0!<x%Krm>3ue=d5V z>fF|2HD9H9CYU?^eRTA@;S$Mio1&gz`{1IG|GlTDGd_9W`0Pcy@^4q48|j@hPVD#V zUc&V}%J{+UB^JMJEY@7zxb*OhJAas#@hqwkP~}YPKf0+SN<5nBi-u*|!ZTu_S>3zl zEfRUV<dWl*;F#3U3VOHtZ`SNtQh80`*}LOQId}eC!nW?o{VcEDU7KF({%j4D+*b2- z$>XA|9h3P&ir2OkpL$Zc?PsLe-rXhF7<D8LJbLb4ad(Q!6<P6D%MMko>;4tketUv| z)ZLcVQ$M_!duK+>uOBxJrkU|CUbyp%>KVC1MF$nuNgtUm-L`Sr&MiJ{8@Cl+6Sx)} z<+p#&tFw|$yB~!9SS|B3?l=Qe?I+pw%JxPLzO^T$kNKYMdOqz~UFD3++xZ`yv-43| zdVQjC!&>GWVNbtUv43FTJfu=_LTSYZt545(A3WT7P9v33vrIec?CMHcR=?v_-QNR^ z&Z=MDuCwg3Zj@@-cHL{Q3y!_yia*d|tD*2UF6h1EJA-{^ZL2>CM0$ig4f$6!*ZD`D z*sp*UDVDjt(@K9V)!ps2S;KVpr)QrMZLZGw|3#s0fyR9K1KwG8!k&KG#a6I@|7TqI znebbUvRPF-4Aa-`o*yK``i^P)r?m2^>Yfvhi#_S&mdd;HY8un_t`7(I9>4$QxKLD~ z#m#`(YvfboGbS$j5Mli_YVoZ-^SQpstk#r2FgsOi?xwXRRvljdV%s@)Fkap;uQPc8 zpKgYOcGyDSzuTGwZf#_h-N8Qh2BW;(akf7U(jSffSTyPV5n|las_?HNuck}-Xy=3{ z8=Ey<T=u+}xblXMcvAP3&);fz^CoTG5qa&gSmyDyZz}U9$KBE6f5><9?05CT?KS+- zPc83gJMZx4OPnop?C&Q&xe1Fa0`47l{-jv^N!UiI{+Qlso_T?py7P~oYVZ$#nC|nN z^ON2`srti5=BwpDeEuW)SB&cK59L2tPb(b>`c-}F-4g%c5Bxtw=gXy^U1ix@_ww7T z<rVYq{blm|CGzybi;IeXl%JdYDrq)-zG!RS@0YT(&#Br?dvJTon`QmMANVY7ek_-` z6;bwYbLhJNP4!zZrtz%}WnDMb>f7V%OFMV<GhJJ}=I-%H4Bp+$VR==8?e3iwO-kRE ziGGycBP}}7wp&i4!@{WNyU4M>)`kzaAL-(s75VVLWygK<X~!LtIqFXxT9sIHO6qsK z(_sNS<s^&WAB97c#gb~`rV2l`cdKJH-oO2nb-Kf*+`Nmk*pHswb9K?1d37cgwuJ`@ ztM9I1J1#uqQK@RsJz3wMiXQ`9?A2~&PYlfVnsn;l<MWlVk5k_;O1gJmi3@o8u`14e z-Ku@f)d#k6&MB<8n=+yEW=hGep6O}l&wjLOdt&o+y-~pH<2z49+;&KJ>|1rVnf*zP zP?p*Cz@tXWolCWkDJ|vN+P36O()KO>TGIMc`@$#OowGLVMua)<3DqyJHyJ&>R<X0E zPV~a7RUeJ?U&Z=ym2N&fE#u?S-W!o-3f$k0a{tp4`S||(%r*XpPhXmOGH70JdS#wX z`_+nhsr&j<|1fQP=xWn3-DBr`5$1}y#rEkZ_xC2s9OeHg7<cfv$v=yEqUL<vee$P$ z_NWzG=pA@nWV`kE$F@7I%0>M5rcAf|E>)+Nzd<GR)7u9ZPqTE3zZDdF>9lNqblg;{ z$Ri<NPkr7LG3~;;o147XrFdCoJY~2yY4walt3NKeQXIN?tM${Y$Wz}ErJtQP5}P?Y zOw7CaYl+Bn&&66cQ9rswHcJ#|EYA~NJUMLL<fogOHibsUIqeKwd;Q3kQ`1+Nu3D|R z?se3(vMHafimI+Wx4)G=cjKl@s}9D0ynC|n<%Ts$Y3Ak@&(8nSG*P>;>5}4C{mB(e zl2+9&3A0pt-?%&HTEm*yP`5qPzP{3|uH)I#zyGYRiO`JLg;|p`o+(YL>@Z30F#UOC z1$T$%(vrVUR>7_*D<9qwI&^5~wrmsks3ljkM5lR4GatAu_Ued=P<mj{+7gGm|MVRe zmI&>Z;as!H!n|P5<cIeqTsbBEr%#<+yLr~?BEEB_Dd)pOXB$_RH8Zsv{l4*quY3MY z>5gS(k(U`?%1!yOaMPdV>?%U>_iU0Xv$y%^6}++i_sGC5Kg{>Sf-h!MA{!QFb>4m8 zA~rp?uGcHRa%SviKI^Rgn%)laCMoYPzu_;CvrDn4JHGCP^0&bEn-Bh0=HF(2aclp^ zxsUZ~CW&m?S5bad-8}N{RSTUhd@VXfu_>v^VwH;}r_OyoY4%ew>CSH5f^=h_-*tb_ zbcp4}ttnJJy|yS?YT8ZZ-TPjtOuKzK%hD$3bd$<7>nG<-bf))R36g%lVspgaSMTp` znwrd!yhKF%iQMw&cdaMPF5NluQ9I!ODwij#m+YDUxXfpYtjzvn-`Oqe=8AqYIPNYJ z*|YD2-m6>Z3b!9tu9cBj{cqUUUubvY<Lo*0a~=zS_+t+m6EVH<`_Co;28Jyf_~vN{ zj)?>$78R$aK(|->WadFv?yn8Y1f3OJw_fb@iROcSh8mo12?sV!(A_p6L3;bP=H`{+ zT`OaSFQrUitL#`KxOigo=B>WB-(*kMDT#l{Zl`iSc)^tAr?=br|CXPX|5Gyaes#Kv z_caSnlX#PI%kOu-?|E+P{yl#Fzo+>Od=L5sSy+DQT2H^$U6ppxL;vMzH^skG){1wx z%{}+z>=(_udXhH{vok&QQ`FBk2|9Wte0Hsk^nd=<Nzb<U-=c|yEM}3e$G31Ty*M@U zxEZ(QIv=goo06Yh4z4N;NI1M^ruGZJn`SIxugcri!ee*wv8li3xzV$$zF@}pEn>Ng zQ$%FSSBCJXUMtAB6ubXX@!q18n6jMOBj=`0f6>=xc3oz+iT9<;?@Ya4@5que;$CK~ zv%xel+2EnXwmQ3fo^R(keSW;<sWs!RWATVzXsDg-BIwfI=6gd)z~{?3*A1Zyaz)>9 z?%m??Zt)A3<7^yvRx9=_<9L_9LCH1n&hx0Z7d9|n<S661vpLH&>GrR#9{a8x+1sO= z+j@0xbi1(FGQI6RnEAprXu-`X#m6_XS_$tpt?{dP-K^`e(7tJ3*YsVnyqRy(Pq3zW zFIfC&q1(J>L76``OU(@=4<<GqE>}LJ!M9NP6jyb?VP^|1fwPjUlX6yb2cFu<+B|8o zVq<Tqv>?y*tFyG1i`9rO7VNnaY1}Jy?1O=Dtj^@tcA;JslXX7~RA<Uv|G?qL;Uf0+ z(iWwuuPn7scPRQzpMFmIo8(NpwB$>tUi`GYYxeX`G>>Js+T%O*?W|{%IwM3US^ep1 z34J0}v+nb;O#&CXULBJ>e|X~V*ZOag^w`v#kKGS=bEUvmQ~M~F;p2F}+RX_vt1qR# ze)=vkgKhKH!^*`9`<74Xx?XOyQ!KT2-sKMx`&{$%rs^J-&`j}<QrqeI<NJzZ-HR;R z)J<MkoWB#Z;f?CtB+FSDCN*C+9}Rz+cVa*LuPq_-9}7-+vNn6V`{^{h>>Gh87ccg> zHackOsf(N1blMbpZ{v<^w3ENnyS?Jw3Byhy+e^aU`EvJ{)Oep-q*5AH79xA>nyr8F zW|O>Is!^QFR<C@L`i0xrL35LXcGbN{?f=&Jd@`0w`>^JK=lvh=R&k{quZlao{iAG` zVX~;q-6^5Fk1l_G;@CpL=N^emnM|jx$ZSXsv-7up89M9R&-XvQALMY?DOf-6Uc<aU z*yz`E)0ksiv%~M5@|8O6w(nMl_1{1amHiehH+$}1_@H(#X>!VR@f)v?H{Q3uWX-fY z%jj%&QPsYng~#>Nd0ID1+6&!ymelwCjpo-Q+|z{5ONr0u$*C8~f2{sV{@>kFmIKQj zq!VT1Klf#>|IPm?&Zt^i?E1vJZnm>S<(_}3xtVeB{D*Fix^vHi{$}#-pYn_2Qk23T zom|bxb6Qpvx7a_<-FEoD^MU=BKUD8gkajS$o|LZMaV{#=I=N@|rpR(LnYYsuHcp<! z_35vS%kCQ8La~QB4Qc0o>a98#ZSnl~a;yBOdNWp@mD#PGyQN`5zlmkNU8V4MmHfrU z57oBCy7wIZZE>egHdf}C<(*R5yT9`i`wx5xo%3RMYc13N1C1+hTO7H|&gQgI+M<Y? znTgpqe538PCy_GJ9P0mDB6MW9?;Z+onlat_IP1DU-yhrM7yMNgy~tAkyHlpe=*ahb zYB|UE-e|PF(YpA>F^#gvGU?(!OLu&e5wh@neK@5_@Im$rmMa-tmwWzbM9w>?dCaP- z`G=L?<=2)*hTX<CA9<s)wiLa6ZXvX(|I_q67xE|H>fWy3^1thWlfg-aU3rf#-fW#9 zQ+E2{{Et7{ySzK^Z1vZ4S$BU&yN1I5r>pj)Px=49;L4HrFK?fiw2wP@VdC^VjUPf~ zI$BphY26m@XmWMlZpLD}tNWJNZ45Z8W1+KFwEuq^*Bbuo1v}^8vT#V1QCZ!7;$C8s z&7q6U5)VEXwCMB}=6A)cEvnpg>S<<7P>fmeb+^tHi!B_#o!*nIUcA9a&)GD_>-Tbz z={ePM(}itBA8gm%`JQdwyy-4R*Pb)&2vgNx#nU%=QX%W&%eDcn7mvBj3(VW!ExszG z&VOfp>tccZdrN-RE<Lwav{Y`k*Q80)?Hnvh)+%lDZ@RvUr*u_@|H9^1JLccmZSgMA zL_PFd%G|GEieEl03g=wVoS-_x!S_nhdsEi_`qH>7s!btsOMY>_{mQ^7JIm{@@a??E zZ(F~~w@q31%~SO5k^cYlqNWDz?s{J6^_8=))ale3=98&Qryf;$D*NDv?Vk9j&zV_O z);2J1;@N%um1+3m>jhUoitc&s`f<Z_YhSrE5qsW*e~V2st0!5VzP`kF^{<N`v{?my znASb6)Twy+YSrV^s8xsFD(rtMTD#gJI`o3re%EI&ChbyxS+(Nc<)v9sD<(#NT>GYE z(j<lO{|jnj|AOx=>QH>{@S2T*p-O;(A;6oNMTCKagM$J6I7=%+Q*;=WDP&;}WSU_v zXi1Nwi0$QRd79phOriUha5zP8(eC=XSk(1M%M!uKv!p7dZ+Y%c*`2#BTI7GH-RvKa zn$@%%g}&aDPu;V5_m<W+@%XZ`cXz7a?My%S=jZRo>J6f6v_BZGxwf|JnDm|y(WqW| zj^OItk8aKpQ712K6~B|!^~r5-*yA+I^|O<9C%!HXc5Fzjm@i*&_CcEK?R&SyRc9+! z9O#{vaVheiWzNlA3!i>nHaGQ!{tMB`w{^V@bmmx}dukoQdi&B@ud-gRCd<25-kP%2 zD@CSo<Xkze#ADAE`bBw7LPz2j<}G=Ve*zY@3LZ?bmsq6rOCUvJ(wX_&v#JkOo&3(D zByje+_Z^KQ@9EY05*t=q6`p_c-(6^t^eT?s8mtKt-YV7ZT*6B|MT2K7yQ7s~wbSIz zx+ZO&&2zhS4-1IqiR9#UpEXx~xP?Q}&ogg##ZJ9hg`4w^uRdaM?(E?mi)*cVBx9!j zI%C|UUgfFM&Ag*cm9?8^;^yrOlp0k|Ubu39*OqNg5ot3%SqVL^SetsI>aalG=Cr7! zy8#p1+*nQaz1exycGv5kHBD2E+X_!k(DYMPzPot&tkT_S8`Ucs!`Ui!?#T;{GEKZS z>1wR#_k#Hmfq&)*$t{1P7t8$JYl&)Af$BMD(d<u$ME35C%)Zgi^U!H;>eM5~u~lim zn5>tmN<4dU?C+Nw9I;jt!&=MiS~89=X@6<$Fj0NV(F|EXSx4Ww3Y&^s9_CB@e3a6E z$^6lS9_0p!whY<Db6;J%p!e*{j+WDMafc6|Ua~Lwku96Q!DivPwukPlzY^XfliZm1 zvsv74Zrk7EJKi16zw_tLJ1OOP2Us@FJHoN7u=UK;13BT}eRluWy|YZKd|zmLt6SjA z%jy@c7dWb0+-JRXoo%OcVR_5)DQ_oBZmRexAt@hM`h>$e-FwQ+f`X*Hb?wUk<rH3T zS$_SQ;xiko!0=|7!`2%A`Z-wa=04tb=cw+jXWveotoj2!#QA}(qVsho1_lRKeA&l? zVD>4>&q_@$A?7gHr4xPql^g}y?hCQF{BepceVn+bk<-WK_e6~}{}k1O75QyN8OFx8 znNmFez5LbW{xJU2NIu_iY{TT($#0$aUY+`VrC;gZeYX1<EDoJl65|l}`n|5B)jYvm zB)RWSweF)CTVJzYdYe&teBQ<+ZMRPwmL3Xl`Ma%W$I?X`bvNlL`DgsydcHD0W6O_l zWv7QOZkOf=7H$2-t9U=CR!_NoDVJEF!Wz|o_ZM$C{wwMWlUm5_Q^E&(FBt7S-&JWm z!TC_?@Bi)AYB@jtB+iOC>b9z5L+I-5?~g?6XzZxjp1q~#&toAojw_09{yN5N5;S^H z#~Iz?WmCH|(NyhC`i}N_(=wwD^~DBF?=@}~@?Cpw?WI1+4VR@)E_m<5x%`;#{+A|J z4^=+SZJWH2!!0IX=v1ufCAt5TPV&pvW&cPsQsPVa-uUE6Zrb;R<cs%WR;QGH{J^=? zeYVbmoMc7ayri4QPF+bTRXrNM(X3h8*djc(eui`Tz6Cq|x6EqHJGc1v%ok;Bzmnx_ zPdISyUYY$~7St1Y@08o#!N|a1ig#J0JE7PQM!WdU8RzNSODCT8lQtA-`|s=|#H2f+ zXnFTby_HjXb|oEjb?S^*P%tql^}Uau`nt4Z%KgGqwrgpth}k&Eb3K$~a(c<3c(uFu z^MBdjH|piz%QO5r5GTpq61=oF{Kz5cjnZA5?(cfV3eVj7%JlNvvd)JeqO_w8CQaVP z<t)g%+vw4@F12kZcJ(MN{F^uXvSZSg4`+V&ax7Jr^elGLxFTKav~g`)j4PwJc7#jk zhFP!lk3Veq>Tk#~BjB}`KTGYx)LG}dT#YAn?hXFFKiyho&F?=&?_Y-IEa~Eq{_6I9 z`wR|qfr;<G^-P;sA92vC(W>QceFvYkBC}lB2FD1$P0h16P8HbqtV7uUw9cA@*##Gp zAKMuF*#uj?nx5?TX3ls0Iab^MOuPK1<#Ne)i#dKS2G88O1KzIO`0a<!vi?gyPV8A* zqE*xUmvPDJ4CUXSu1(8*7Uh3Uziz+t(`#(pE25%Lb(!i!@BXB-;a_lD{jpa(dFLKD z<-R>Hv+FCH?w&<kWxkxY*)_jA@7aRr&nqT33zZpNeJ2cx{q<$9Hgz&GFsOlJpOHy~ z0aAG~Ku=v~Vt@cn2!%xYx`sIFdiuHP2Y91u^Imw@a0LScgEk`rg9wra1_nz81_rn` zUq?SrH`m}0JzqC;6F{0^7W0ERaIDOTWI}FYS!y1J^&qt{&5|Gv94BFF&dk><Nz6_x z%EW7oI@|~lV>Lcwz;4nlhuqwSVW`g*2Ubus1QZaaAhY0@i3!7F2)CgcT9BWsR{%e8 z8(-M5z>Nnn<})!c<QJu5I0_^O3ovdF2aYd8btD!fCTFDL)hGzp1Y+<pL#%>o1iK%v zcGQUS$EF=6Oqn666x77KbmPx4CsqcA1TF>!&~<7c`$2dMBj^?;kcUxR0W-!aKfk27 zq$sh#H!(9WxFivAI{-LDkt{HM<Ge_SiGkrMGXsMW$Ydzq!f4Hj#hWk-TvF2#OLIzG z^2_sb@)J{1i&P9DElG6qPfN#haWXM56tFNb1VhaRQCk?-a+72}dJ!B@lwVQlSXz>i znpcvUoCv>33f%$DrY-#iJPZtJ5)2FmAe*6h3!^z7DGtEyy_Ul7LK_$u7#=e)Fla(e z22ooWS*7ur4i7i%#y(vb&3=cUfuTd5fk6Xg9u#k3<dMN+tY=<vNn%b;Y7ur*Thz55 z|6ycc$ORp@1~m~xZDF+2!DFgVetJ4CBLxeJ_r`KFFr1cPV9*Dd1;tw!d-d=b=?f~{ zpcjFGk|=ifB44F$2Q?T(ZDDk`!ee+qVo8Q$adB#HQch}-icfxWVvb8@aY<2TQfWzM zejawakZ*?tUCs`&2ZXmU=GhQy7j~1YnG)c4C4z331(^!MTNv40@R*FA-cjzFMNj0r zkgjDzEhd6J@z?>40W5bgqFaD`6^b6n^-#Qp(Z`pt1*mtufz8HI6U#!)g;17Zc+3V} ztpYX*sWkh5boB%1>~e@P3=CTs+Xxv&_+k}w%aD&y2c02`u<S!50e8bJBc?lnZtd?} zGx)*ZsD2cUCtxj7vlYi_?dX;vA6gAMOB~_HYYD_#hIX<zx+TcRw1OrD5SFwj5pN0d zcm}!+$cId#M%Ax$;%xvAq2N3G72R6oll?$juMi%amrb6v5KltSvqZNJ`OGiSF$)Om z7UYp^9iGDj(d|V(zbOP}DFZ0ieJvu{UZTzyLw5o4(G#HUKQKpZVO&^BvJ3E?8ij5# z@(zF0AkU~J*<w8BT%g;FylooQ!&MC=+Y3olj#y6<K)2YN39;W4v@07C(sP^1v>1KE z6}pwkTN~YA_JNbv-8M3<gyiI4#A#sYb|UXIL(REQJIJ&%7<^J5_BI0Y1|QVuJk?E+ zb%eqedH)A!{}3#Aw=f>+BiUktp^Cg#9<-+fVVlfEl5NAk!vsA-O_8RgQR|qvDWqDB zJr0q_=}~PcT1vnMaKK?sbffzLc_<Uq(MAN{{xzi90Lk;%eS$n_1{xSe*z<2aA$y1) tCPnuh@?Z^UKn`K+pN#};MQ<x3wR13<9;|F2gY_Bo8Q6pv7=CRA@c{F|zbOC! diff --git a/fda-authentication-service/.mvn/wrapper/maven-wrapper.properties b/fda-authentication-service/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index 642d572ce9..0000000000 --- a/fda-authentication-service/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,2 +0,0 @@ -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar diff --git a/fda-authentication-service/Dockerfile b/fda-authentication-service/Dockerfile index 117770a3cd..f5cfe08e18 100644 --- a/fda-authentication-service/Dockerfile +++ b/fda-authentication-service/Dockerfile @@ -1,8 +1,4 @@ ###### FIRST STAGE ###### -FROM fda-metadata-db:latest as dependency -MAINTAINER Martin Weise <martin.weise@tuwien.ac.at> - -###### SECOND STAGE ###### FROM keycloak/keycloak:21.0 as config # Enable health and metrics support @@ -18,52 +14,16 @@ WORKDIR /opt/keycloak RUN keytool -genkeypair -storepass password -storetype PKCS12 -keyalg RSA -keysize 2048 -dname "CN=server" -alias server -ext "SAN:c=DNS:localhost,IP:127.0.0.1" -keystore conf/server.keystore RUN /opt/keycloak/bin/kc.sh build -###### THIRD STAGE ###### -FROM maven:slim as build - -COPY ./pom.xml ./ - -RUN mvn -fn -B dependency:go-offline > /dev/null - -COPY --from=dependency /root/.m2/repository/at/tuwien /root/.m2/repository/at/tuwien - -COPY ./rest-service ./rest-service -COPY ./services ./services -COPY ./report ./report - -# Make sure it compiles -RUN mvn -q clean package -DskipTests - -###### FOURTH STAGE ###### +###### SECOND STAGE ###### FROM keycloak/keycloak:21.0 as runtime COPY --from=config /opt/keycloak/ /opt/keycloak/ USER root -COPY ./service_ready /usr/bin -COPY ./docker-entrypoint.sh /usr/bin -COPY dbrepo-keycloak-realm.json /dbrepo-keycloak-realm.json -RUN chmod +x /usr/bin/service_ready /usr/bin/docker-entrypoint.sh +COPY dbrepo-realm.json /dbrepo-realm.json -ENV METADATA_DB=fda ENV METADATA_USERNAME=root ENV METADATA_PASSWORD=dbrepo -ENV BROKER_USERNAME=fda -ENV BROKER_PASSWORD=fda -ENV WEBSITE=http://example.com -ENV GATEWAY_ENDPOINT=http://gateway-service:9095/api/broker -ENV TOKEN_MAX=5 -ENV MAIL_FROM="Database Repository <noreply@example.com>" -ENV MAIL_REPLY_TO="Somebody <somebody@example.com>" -ENV MAIL_VERIFY=false -ENV JWT_ISSUER=dbrepo -ENV JWT_SECRET=secret -ENV JWT_EXPIRATION=86400000 -ENV SMTP_HOST="" -ENV SMTP_PORT="" -ENV SMTP_USERNAME="" -ENV SMTP_PASSWORD="" -ENV LOG_LEVEL=debug ENV KC_DB=mariadb ENV KC_DB_URL=jdbc:mariadb://fda-metadata-db/keycloak @@ -71,16 +31,12 @@ ENV KC_DB_USERNAME=${METADATA_USERNAME} ENV KC_DB_PASSWORD=${METADATA_PASSWORD} ENV KC_HOSTNAME=localhost -ENV KEYCLOAK_IMPORT=/dbrepo-keycloak-realm.json +ENV KEYCLOAK_IMPORT=/dbrepo-realm.json ENV KEYCLOAK_ADMIN=keycloak ENV KEYCLOAK_ADMIN_PASSWORD=keycloak VOLUME /tmp -HEALTHCHECK --interval=10s --timeout=5s --retries=12 CMD service_ready - -COPY --from=build ./rest-service/target/rest-service-*.jar ./authentication-service.jar - EXPOSE 9097 -ENTRYPOINT ["/usr/bin/docker-entrypoint.sh"] +ENTRYPOINT ["/opt/keycloak/bin/kc.sh", "start"] diff --git a/fda-authentication-service/README.md b/fda-authentication-service/README.md deleted file mode 100644 index 475bfdcfbf..0000000000 --- a/fda-authentication-service/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Authentication Service - -Uses SAML2.0 - -## Run - -The container needs the environment variable set with the Key Store password, put it in your `~/.bashrc`: - -```bash -export KEY_STORE_PASSWORD=... -``` - -or for fish in your `~/.config/fish/config.fish`: - -```fish -set KEY_STORE_PASSWORD "..." -``` - -## Key Store - -The key store is a secure container that contains the SSL/TLS certificate: - -1. Let's Encrypt private key for `dbrepo.ossdip.at` with alias `ssl` - -## Development - -Context metadata for IdP: `http://localhost:9097/saml/metadata` - -- The authentication is valid for 2 hours (default SAML2.0) -- Login: `https://dbrepo.ossdip.at:9097/saml/login?local=true` -> success -> `https://dbrepo.ossdip.at/dashboard` -- Logout: `https://dbrepo.ossdip.at:9097/saml/logout?local=true` -> success -> \ No newline at end of file diff --git a/fda-authentication-service/dbrepo-keycloak-realm.json b/fda-authentication-service/dbrepo-keycloak-realm.json deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/fda-authentication-service/dbrepo-realm.json b/fda-authentication-service/dbrepo-realm.json new file mode 100644 index 0000000000..b565bac813 --- /dev/null +++ b/fda-authentication-service/dbrepo-realm.json @@ -0,0 +1,1848 @@ +{ + "id" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "realm" : "dbrepo", + "notBefore" : 0, + "defaultSignatureAlgorithm" : "RS256", + "revokeRefreshToken" : false, + "refreshTokenMaxReuse" : 0, + "accessTokenLifespan" : 300, + "accessTokenLifespanForImplicitFlow" : 900, + "ssoSessionIdleTimeout" : 1800, + "ssoSessionMaxLifespan" : 36000, + "ssoSessionIdleTimeoutRememberMe" : 0, + "ssoSessionMaxLifespanRememberMe" : 0, + "offlineSessionIdleTimeout" : 2592000, + "offlineSessionMaxLifespanEnabled" : false, + "offlineSessionMaxLifespan" : 5184000, + "clientSessionIdleTimeout" : 0, + "clientSessionMaxLifespan" : 0, + "clientOfflineSessionIdleTimeout" : 0, + "clientOfflineSessionMaxLifespan" : 0, + "accessCodeLifespan" : 60, + "accessCodeLifespanUserAction" : 300, + "accessCodeLifespanLogin" : 1800, + "actionTokenGeneratedByAdminLifespan" : 43200, + "actionTokenGeneratedByUserLifespan" : 300, + "oauth2DeviceCodeLifespan" : 600, + "oauth2DevicePollingInterval" : 5, + "enabled" : true, + "sslRequired" : "external", + "registrationAllowed" : false, + "registrationEmailAsUsername" : false, + "rememberMe" : false, + "verifyEmail" : true, + "loginWithEmailAllowed" : false, + "duplicateEmailsAllowed" : false, + "resetPasswordAllowed" : false, + "editUsernameAllowed" : false, + "bruteForceProtected" : false, + "permanentLockout" : false, + "maxFailureWaitSeconds" : 900, + "minimumQuickLoginWaitSeconds" : 60, + "waitIncrementSeconds" : 60, + "quickLoginCheckMilliSeconds" : 1000, + "maxDeltaTimeSeconds" : 43200, + "failureFactor" : 30, + "roles" : { + "realm" : [ { + "id" : "3cf49968-553d-4d3c-a824-62decc4d3465", + "name" : "data-steward", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + }, { + "id" : "e3a498cf-1ced-4be8-a590-6d18b45289d5", + "name" : "developer", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + }, { + "id" : "d26d5c51-8941-4087-961e-c14863a004d3", + "name" : "researcher", + "description" : "", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + }, { + "id" : "2e7a1f5f-79d7-48b2-8d04-6fe77ac967aa", + "name" : "uma_authorization", + "description" : "${role_uma_authorization}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + }, { + "id" : "abd2d9ee-ebc4-4d0a-839e-6b588a6d442a", + "name" : "default-roles-dbrepo", + "description" : "${role_default-roles}", + "composite" : true, + "composites" : { + "realm" : [ "researcher", "offline_access", "uma_authorization" ], + "client" : { + "account" : [ "view-profile", "manage-account" ] + } + }, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + }, { + "id" : "848103a4-9956-422d-a587-9ab5e709f655", + "name" : "offline_access", + "description" : "${role_offline-access}", + "composite" : false, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0", + "attributes" : { } + } ], + "client" : { + "realm-management" : [ { + "id" : "4628f654-f8f3-483b-8f92-2a7fc5930b14", + "name" : "query-realms", + "description" : "${role_query-realms}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "95c2cc47-12f5-4d73-8b74-67e270c45ade", + "name" : "manage-authorization", + "description" : "${role_manage-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "824791f3-c345-42f8-b103-b7e6d7e40114", + "name" : "manage-identity-providers", + "description" : "${role_manage-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "1f840202-b7e2-4195-bac9-64e64dad2037", + "name" : "view-identity-providers", + "description" : "${role_view-identity-providers}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "3c32c096-bb13-44c9-a080-d756a48a9ea3", + "name" : "query-clients", + "description" : "${role_query-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "e4b85a68-7f31-4fcf-89a2-f10d7df358e9", + "name" : "view-authorization", + "description" : "${role_view-authorization}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "7d317752-ae56-46f2-a2ce-67c64d1b35f6", + "name" : "view-users", + "description" : "${role_view-users}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-users", "query-groups" ] + } + }, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "28824208-976e-4622-b4d7-3d18efbb46fa", + "name" : "realm-admin", + "description" : "${role_realm-admin}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-realms", "view-identity-providers", "manage-identity-providers", "manage-authorization", "query-clients", "view-authorization", "view-users", "manage-users", "view-realm", "query-users", "view-clients", "query-groups", "create-client", "manage-clients", "manage-events", "impersonation", "view-events", "manage-realm" ] + } + }, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "57e846a2-930d-4621-819d-c35086507146", + "name" : "manage-users", + "description" : "${role_manage-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "7fad9cde-bf96-475a-9174-14a87da51f95", + "name" : "view-realm", + "description" : "${role_view-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "bbcac294-d78a-4ea1-a4bf-0384266d2fe1", + "name" : "query-users", + "description" : "${role_query-users}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "480e1437-ab9e-47de-b47a-edc6b6e285de", + "name" : "view-clients", + "description" : "${role_view-clients}", + "composite" : true, + "composites" : { + "client" : { + "realm-management" : [ "query-clients" ] + } + }, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "b9a9a8f5-f91e-4e73-9e88-1cdf42bd49f9", + "name" : "create-client", + "description" : "${role_create-client}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "4d1397fb-247c-436f-b26f-124cd89afb08", + "name" : "query-groups", + "description" : "${role_query-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "e31f522b-b283-4ae1-b875-52afcd98b1d2", + "name" : "impersonation", + "description" : "${role_impersonation}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "51822d02-fa28-4a49-89da-bc534719d8a8", + "name" : "manage-clients", + "description" : "${role_manage-clients}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "b2743ce5-0ce8-4157-ae00-f693560f0b39", + "name" : "manage-events", + "description" : "${role_manage-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "7ea3d7e0-9bf4-438a-b773-243daf622aaa", + "name" : "view-events", + "description" : "${role_view-events}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + }, { + "id" : "fb73f6f5-0ed5-41d0-852c-0eb3b195b15a", + "name" : "manage-realm", + "description" : "${role_manage-realm}", + "composite" : false, + "clientRole" : true, + "containerId" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "attributes" : { } + } ], + "security-admin-console" : [ ], + "dbrepo-client" : [ ], + "admin-cli" : [ ], + "account-console" : [ ], + "broker" : [ { + "id" : "de0cfd5e-c2fe-4082-ac39-e3b092139a0f", + "name" : "read-token", + "description" : "${role_read-token}", + "composite" : false, + "clientRole" : true, + "containerId" : "88694c91-753d-4c44-9740-ec9ac06bba45", + "attributes" : { } + } ], + "account" : [ { + "id" : "acd78c04-eefc-4344-a5b4-3fc83d848936", + "name" : "delete-account", + "description" : "${role_delete-account}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "939be844-8c49-45b3-9ca1-4b10a454b346", + "name" : "view-profile", + "description" : "${role_view-profile}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "e52fdf00-3e73-4c17-bc1c-643493710a6b", + "name" : "view-applications", + "description" : "${role_view-applications}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "b02a822e-a708-420a-bddc-1a315033fd7c", + "name" : "view-consent", + "description" : "${role_view-consent}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "c590e5f5-2cbf-4151-b1dc-96c454f1f654", + "name" : "view-groups", + "description" : "${role_view-groups}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "15974151-6c13-426b-8cc3-7683dd1311e1", + "name" : "manage-account-links", + "description" : "${role_manage-account-links}", + "composite" : false, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "c12d8d94-c2df-498e-bbe4-2f934a83ae92", + "name" : "manage-consent", + "description" : "${role_manage-consent}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "view-consent" ] + } + }, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + }, { + "id" : "55f85811-bded-4d6b-8f7b-45844b963875", + "name" : "manage-account", + "description" : "${role_manage-account}", + "composite" : true, + "composites" : { + "client" : { + "account" : [ "manage-account-links" ] + } + }, + "clientRole" : true, + "containerId" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "attributes" : { } + } ] + } + }, + "groups" : [ ], + "defaultRole" : { + "id" : "abd2d9ee-ebc4-4d0a-839e-6b588a6d442a", + "name" : "default-roles-dbrepo", + "description" : "${role_default-roles}", + "composite" : true, + "clientRole" : false, + "containerId" : "82c39861-d877-4667-a0f3-4daa2ee230e0" + }, + "requiredCredentials" : [ "password" ], + "otpPolicyType" : "totp", + "otpPolicyAlgorithm" : "HmacSHA1", + "otpPolicyInitialCounter" : 0, + "otpPolicyDigits" : 6, + "otpPolicyLookAheadWindow" : 1, + "otpPolicyPeriod" : 30, + "otpPolicyCodeReusable" : false, + "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppMicrosoftAuthenticatorName", "totpAppFreeOTPName" ], + "webAuthnPolicyRpEntityName" : "keycloak", + "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyRpId" : "", + "webAuthnPolicyAttestationConveyancePreference" : "not specified", + "webAuthnPolicyAuthenticatorAttachment" : "not specified", + "webAuthnPolicyRequireResidentKey" : "not specified", + "webAuthnPolicyUserVerificationRequirement" : "not specified", + "webAuthnPolicyCreateTimeout" : 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyAcceptableAaguids" : [ ], + "webAuthnPolicyPasswordlessRpEntityName" : "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms" : [ "ES256" ], + "webAuthnPolicyPasswordlessRpId" : "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference" : "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment" : "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey" : "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement" : "not specified", + "webAuthnPolicyPasswordlessCreateTimeout" : 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister" : false, + "webAuthnPolicyPasswordlessAcceptableAaguids" : [ ], + "scopeMappings" : [ { + "clientScope" : "offline_access", + "roles" : [ "offline_access" ] + } ], + "clientScopeMappings" : { + "account" : [ { + "client" : "account-console", + "roles" : [ "manage-account", "view-groups" ] + } ] + }, + "clients" : [ { + "id" : "e767a4a6-79e9-4e08-82b7-1076e1a09142", + "clientId" : "account", + "name" : "${client_account}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/dbrepo/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/dbrepo/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "d3c4a04e-39ce-4549-a34a-11e25774cd96", + "clientId" : "account-console", + "name" : "${client_account-console}", + "rootUrl" : "${authBaseUrl}", + "baseUrl" : "/realms/dbrepo/account/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/realms/dbrepo/account/*" ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "22d90d9c-9881-474c-8dfd-a62c808a9f1c", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "81ef0f59-a5ca-4be4-a1d1-0c32edf1cfd6", + "clientId" : "admin-cli", + "name" : "${client_admin-cli}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : false, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "88694c91-753d-4c44-9740-ec9ac06bba45", + "clientId" : "broker", + "name" : "${client_broker}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "a0d03d5b-2390-43ba-a2af-a04bdc5273c3", + "clientId" : "dbrepo-client", + "name" : "", + "description" : "", + "rootUrl" : "", + "adminUrl" : "", + "baseUrl" : "", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "secret" : "Gp9IALXWsfftK8ek1J6jNT9hNfWV5U5c", + "redirectUris" : [ "*" ], + "webOrigins" : [ "*" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : true, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : true, + "protocol" : "openid-connect", + "attributes" : { + "oidc.ciba.grant.enabled" : "false", + "client.secret.creation.time" : "1678739113", + "backchannel.logout.session.required" : "true", + "post.logout.redirect.uris" : "*", + "display.on.consent.screen" : "false", + "oauth2.device.authorization.grant.enabled" : "false", + "backchannel.logout.revoke.offline.tokens" : "false" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : true, + "nodeReRegistrationTimeout" : -1, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "cfffd5d0-aa19-4057-8ca0-f2c51ca0e930", + "clientId" : "realm-management", + "name" : "${client_realm-management}", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ ], + "webOrigins" : [ ], + "notBefore" : 0, + "bearerOnly" : true, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : false, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + }, { + "id" : "f205c451-9524-4380-acc3-947f7ecb6b7c", + "clientId" : "security-admin-console", + "name" : "${client_security-admin-console}", + "rootUrl" : "${authAdminUrl}", + "baseUrl" : "/admin/dbrepo/console/", + "surrogateAuthRequired" : false, + "enabled" : true, + "alwaysDisplayInConsole" : false, + "clientAuthenticatorType" : "client-secret", + "redirectUris" : [ "/admin/dbrepo/console/*" ], + "webOrigins" : [ "+" ], + "notBefore" : 0, + "bearerOnly" : false, + "consentRequired" : false, + "standardFlowEnabled" : true, + "implicitFlowEnabled" : false, + "directAccessGrantsEnabled" : false, + "serviceAccountsEnabled" : false, + "publicClient" : true, + "frontchannelLogout" : false, + "protocol" : "openid-connect", + "attributes" : { + "post.logout.redirect.uris" : "+", + "pkce.code.challenge.method" : "S256" + }, + "authenticationFlowBindingOverrides" : { }, + "fullScopeAllowed" : false, + "nodeReRegistrationTimeout" : 0, + "protocolMappers" : [ { + "id" : "c4d54410-3f22-4259-9571-94da2c43b752", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + } ], + "defaultClientScopes" : [ "web-origins", "acr", "profile", "roles", "email" ], + "optionalClientScopes" : [ "address", "phone", "offline_access", "microprofile-jwt" ] + } ], + "clientScopes" : [ { + "id" : "55341d34-0086-4173-ae61-d9b175b179d8", + "name" : "acr", + "description" : "OpenID Connect scope for add acr (authentication context class reference) to the token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "58ea3217-0fff-4207-9d08-919f5493b629", + "name" : "acr loa level", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-acr-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true" + } + } ] + }, { + "id" : "7f6e9b44-e2eb-417d-b0fe-db820c9a6564", + "name" : "email", + "description" : "OpenID Connect built-in scope: email", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${emailScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "782819fe-ba5d-4ddb-9f95-cabb69d79c8d", + "name" : "email verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "emailVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email_verified", + "jsonType.label" : "boolean" + } + }, { + "id" : "ca613fc8-bbf2-4240-8b33-a1874f1559f3", + "name" : "email", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "email", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "email", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "210cc792-6c07-45a6-a77e-827cdf3b41ba", + "name" : "offline_access", + "description" : "OpenID Connect built-in scope: offline_access", + "protocol" : "openid-connect", + "attributes" : { + "consent.screen.text" : "${offlineAccessScopeConsentText}", + "display.on.consent.screen" : "true" + } + }, { + "id" : "b9da268f-6745-49dc-a764-3c54e385accc", + "name" : "profile", + "description" : "OpenID Connect built-in scope: profile", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${profileScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "84f0487a-1d7d-470c-9b8e-5835294ae235", + "name" : "username", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "preferred_username", + "jsonType.label" : "String" + } + }, { + "id" : "bbdcdb36-3ec0-443d-b1af-9993d40f0567", + "name" : "gender", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "gender", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "gender", + "jsonType.label" : "String" + } + }, { + "id" : "9faa870b-5491-4ce9-b27d-c9ce07d6a95e", + "name" : "birthdate", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "birthdate", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "birthdate", + "jsonType.label" : "String" + } + }, { + "id" : "f0e3c012-9523-4076-83ae-e466e2d08220", + "name" : "full name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-full-name-mapper", + "consentRequired" : false, + "config" : { + "id.token.claim" : "true", + "access.token.claim" : "true", + "userinfo.token.claim" : "true" + } + }, { + "id" : "f757d8ec-e181-429c-9287-9ad0600b061f", + "name" : "profile", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "profile", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "profile", + "jsonType.label" : "String" + } + }, { + "id" : "18cfbf4b-0a8e-45c7-a832-c0f72c92f3f3", + "name" : "updated at", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "updatedAt", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "updated_at", + "jsonType.label" : "long" + } + }, { + "id" : "841ea785-26ab-429a-a420-09ce3948924d", + "name" : "family name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "lastName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "family_name", + "jsonType.label" : "String" + } + }, { + "id" : "bfba13ff-f952-4e89-bbb1-a693fdebfae8", + "name" : "website", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "website", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "website", + "jsonType.label" : "String" + } + }, { + "id" : "475f071d-5149-4379-b928-76482f5f519c", + "name" : "zoneinfo", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "zoneinfo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "zoneinfo", + "jsonType.label" : "String" + } + }, { + "id" : "b8bebfed-b5e9-4604-a0ee-9817f7d439ac", + "name" : "middle name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "middleName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "middle_name", + "jsonType.label" : "String" + } + }, { + "id" : "445232c8-6830-476c-a6f1-8bbef167595a", + "name" : "picture", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "picture", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "picture", + "jsonType.label" : "String" + } + }, { + "id" : "65f2e474-6ede-4872-86e4-e49504dd0f2a", + "name" : "locale", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "locale", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "locale", + "jsonType.label" : "String" + } + }, { + "id" : "16cd5a27-ccf3-453c-ae1e-8621813ab73c", + "name" : "given name", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "firstName", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "given_name", + "jsonType.label" : "String" + } + }, { + "id" : "f9efedfc-3388-457c-b10a-1dff4525ff9b", + "name" : "nickname", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "nickname", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "nickname", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "627fa054-08eb-4206-af71-9e838e984b8b", + "name" : "microprofile-jwt", + "description" : "Microprofile - JWT built-in scope", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "false" + }, + "protocolMappers" : [ { + "id" : "e6cc53e5-5d7e-468e-88c8-0737dd3dc759", + "name" : "groups", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "multivalued" : "true", + "user.attribute" : "foo", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "groups", + "jsonType.label" : "String" + } + }, { + "id" : "83b4444c-10fc-44e8-a0c0-0c1da1f9bba3", + "name" : "upn", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-property-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "username", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "upn", + "jsonType.label" : "String" + } + } ] + }, { + "id" : "4122ff9e-ad3c-4142-afc6-9aefdecfc86d", + "name" : "role_list", + "description" : "SAML role list", + "protocol" : "saml", + "attributes" : { + "consent.screen.text" : "${samlRoleListScopeConsentText}", + "display.on.consent.screen" : "true" + }, + "protocolMappers" : [ { + "id" : "bb0747fa-c008-4af3-93be-e7739650ebd5", + "name" : "role list", + "protocol" : "saml", + "protocolMapper" : "saml-role-list-mapper", + "consentRequired" : false, + "config" : { + "single" : "false", + "attribute.nameformat" : "Basic", + "attribute.name" : "Role" + } + } ] + }, { + "id" : "425abf4a-2ee2-431d-aa92-e373a36fe556", + "name" : "address", + "description" : "OpenID Connect built-in scope: address", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${addressScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "8d4ffe4d-1d01-4ca1-8ff4-44eacca61b30", + "name" : "address", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-address-mapper", + "consentRequired" : false, + "config" : { + "user.attribute.formatted" : "formatted", + "user.attribute.country" : "country", + "user.attribute.postal_code" : "postal_code", + "userinfo.token.claim" : "true", + "user.attribute.street" : "street", + "id.token.claim" : "true", + "user.attribute.region" : "region", + "access.token.claim" : "true", + "user.attribute.locality" : "locality" + } + } ] + }, { + "id" : "37f61543-dad7-4a82-8e10-77acdd1eefdc", + "name" : "roles", + "description" : "OpenID Connect scope for add user roles to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${rolesScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "3b6b6914-8ad1-4a71-88ec-444f754aaacb", + "name" : "audience resolve", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-audience-resolve-mapper", + "consentRequired" : false, + "config" : { } + }, { + "id" : "2defedf5-9af3-4531-822c-a879dedcd29d", + "name" : "realm roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-realm-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "realm_access.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + }, { + "id" : "a7bd6723-e58e-47f7-95c0-2925ce99283d", + "name" : "client roles", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-client-role-mapper", + "consentRequired" : false, + "config" : { + "user.attribute" : "foo", + "access.token.claim" : "true", + "claim.name" : "resource_access.${client_id}.roles", + "jsonType.label" : "String", + "multivalued" : "true" + } + } ] + }, { + "id" : "52aad832-c6c4-49df-8a04-6ad4a406fdfa", + "name" : "phone", + "description" : "OpenID Connect built-in scope: phone", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "true", + "display.on.consent.screen" : "true", + "consent.screen.text" : "${phoneScopeConsentText}" + }, + "protocolMappers" : [ { + "id" : "dae802fb-9138-408a-b80e-a40eb0f56814", + "name" : "phone number", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumber", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number", + "jsonType.label" : "String" + } + }, { + "id" : "feb06a8d-b0eb-4911-8464-368d93f566fa", + "name" : "phone number verified", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-usermodel-attribute-mapper", + "consentRequired" : false, + "config" : { + "userinfo.token.claim" : "true", + "user.attribute" : "phoneNumberVerified", + "id.token.claim" : "true", + "access.token.claim" : "true", + "claim.name" : "phone_number_verified", + "jsonType.label" : "boolean" + } + } ] + }, { + "id" : "f64d64e8-57ce-4eb2-b99e-9f02fdbd99f9", + "name" : "web-origins", + "description" : "OpenID Connect scope for add allowed web origins to the access token", + "protocol" : "openid-connect", + "attributes" : { + "include.in.token.scope" : "false", + "display.on.consent.screen" : "false", + "consent.screen.text" : "" + }, + "protocolMappers" : [ { + "id" : "c6411e3b-6478-453d-b530-5fe175a4d786", + "name" : "allowed web origins", + "protocol" : "openid-connect", + "protocolMapper" : "oidc-allowed-origins-mapper", + "consentRequired" : false, + "config" : { } + } ] + } ], + "defaultDefaultClientScopes" : [ "roles", "role_list", "acr", "email", "profile", "web-origins" ], + "defaultOptionalClientScopes" : [ "offline_access", "address", "phone", "microprofile-jwt" ], + "browserSecurityHeaders" : { + "contentSecurityPolicyReportOnly" : "", + "xContentTypeOptions" : "nosniff", + "xRobotsTag" : "none", + "xFrameOptions" : "SAMEORIGIN", + "contentSecurityPolicy" : "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection" : "1; mode=block", + "strictTransportSecurity" : "max-age=31536000; includeSubDomains" + }, + "smtpServer" : { }, + "eventsEnabled" : false, + "eventsListeners" : [ "jboss-logging" ], + "enabledEventTypes" : [ ], + "adminEventsEnabled" : false, + "adminEventsDetailsEnabled" : false, + "identityProviders" : [ ], + "identityProviderMappers" : [ ], + "components" : { + "org.keycloak.services.clientregistration.policy.ClientRegistrationPolicy" : [ { + "id" : "4d3f9f14-f5d2-4b0c-8ea7-e6d078aa2191", + "name" : "Max Clients Limit", + "providerId" : "max-clients", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "max-clients" : [ "200" ] + } + }, { + "id" : "f35bce67-1e75-408b-b065-52183368d4fd", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1", + "name" : "Trusted Hosts", + "providerId" : "trusted-hosts", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "host-sending-registration-request-must-match" : [ "true" ], + "client-uris-must-match" : [ "true" ] + } + }, { + "id" : "f565cb47-3bcf-4078-8f94-eb4179c375b8", + "name" : "Full Scope Disabled", + "providerId" : "scope", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "0efa669d-1017-4b4a-82e1-c2eaf72de2c9", + "name" : "Allowed Client Scopes", + "providerId" : "allowed-client-templates", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allow-default-scopes" : [ "true" ] + } + }, { + "id" : "528fb423-d66e-472e-9120-1f03ba9e0f18", + "name" : "Consent Required", + "providerId" : "consent-required", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { } + }, { + "id" : "104ec5a9-025b-4c44-8ac0-82d22887ca3e", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "authenticated", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-attribute-mapper", "oidc-full-name-mapper", "saml-user-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper" ] + } + }, { + "id" : "3ab11d74-5e76-408a-b85a-26bf8950f979", + "name" : "Allowed Protocol Mapper Types", + "providerId" : "allowed-protocol-mappers", + "subType" : "anonymous", + "subComponents" : { }, + "config" : { + "allowed-protocol-mapper-types" : [ "saml-role-list-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "oidc-address-mapper", "saml-user-property-mapper", "oidc-usermodel-attribute-mapper", "oidc-sha256-pairwise-sub-mapper", "saml-user-attribute-mapper" ] + } + } ], + "org.keycloak.keys.KeyProvider" : [ { + "id" : "28ca0b6d-b2e2-4785-b04b-2391e6344e30", + "name" : "aes-generated", + "providerId" : "aes-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "6dc4834f-a1de-4cfe-a29d-e84ac8e9b1a8" ], + "secret" : [ "HpuzG_jWYKwypLeoPEMC4A" ], + "priority" : [ "100" ] + } + }, { + "id" : "bd7945cf-6d35-4e03-9c3a-197f2dc76973", + "name" : "hmac-generated", + "providerId" : "hmac-generated", + "subComponents" : { }, + "config" : { + "kid" : [ "c8500166-5cc4-4085-ad0f-853c3b0b0233" ], + "secret" : [ "TI3xg__G2Qy8C47DracpYir2X4ItQZSrhgr5KSlwRNISDbBqZ-ky3OcAyokSXMcpweSOaCPvbivpvzJNklUBvw" ], + "priority" : [ "100" ], + "algorithm" : [ "HS256" ] + } + }, { + "id" : "2f53ccf3-37b0-4d34-83e7-ed497499ee51", + "name" : "rsa-enc-generated", + "providerId" : "rsa-enc-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEowIBAAKCAQEA3b1tNLfcjFLUw9UShVDNf+ZD8sQqb4YBaIXcSJTX/zDQUPiCp176BBGI3s4VplDArnOW+LumozmKogeoHEnGEIDW8ovgK5uMU9tSA2p0qqGBUMOdR8YATTIfCJe7qGiiuGa3WZy3sQLM70SuRzx02YU8gvUcvl2Js4KyqAziOUX/w3Wa59H9jjGNUXYyqaPWJp73eHzbVYWySzyLG22mVlcUtBx5siL5T2/Xu0p9z4l7/bapwwmOVi1ZrcHjbEAwdGEiSMGI/uWqAF+r1BRpmJLR7HNXcL3eK4/56VYLaiwSejfyYeRFMITEn/UxGYhcXZ5xMUUCG0TxjBhLYpTBuwIDAQABAoIBAA4dwebcxkrH99Poa8+WkiE7JgaS9sahx9OBI2xwJANoIU2TpzGuNLQZ76uLgB+rPWZTD9Xm5a1iJjwOyQ9/937TzPCk91D0tpgcusRikb8jx/6TGB9acL4kBjYUVCCHr3BA2G75MKKGtJ2OMvAbCQSosZj+r2VDwYFEPUkV2jheE5JHSBkwyIRrus3JCwu8gu5fyCg9z8ljcxJxI5HIsi4v8Z21aCw/cLj7h5cMt44wCjQz4rOfYNBEFeHDtlfR1QtWKgjm4ZHHJbKrzf9b2kQXclziceEbSM0tYbROEXKi+s0Zc+z3HEG89vv0vfN400clmzzIAijKY6gg3pPRWdECgYEA+lnWYbSlXDMNYx6RBXm1RnlMUYIm4oy4/9ljgnoGJ6WCn3SjFkgaDtiKfGIG1BSB85r04pAPANgcWHf5tWDnq0ARvBVG0BX2bKd++7B3D4d3CRYKCwm88SslJXv9dfHVhq4+zViFPiUWwT20A72jCuUCvL88y5fh/YBecfdh+jECgYEA4r5RD0NB9dMaeg5/jk/GEHIo4Z9KLc6FrSoOFo2xFkPOy1sgDpDOiNtypuWvniO7k7Ose3DS3hlfTMsKzIW/CgQJ20+Y4cvBWDaOsRxfjj7w3d+jH5OSJdKKSzTrgLKc9ZhlRzVXy0J0hipIA6HG5kdVdLXmh85ITmf1CbJhE6sCgYBjPVeBNbXTHZ2x6/z62aslO5IoQVqetb/kE82hfDOSZcao5Ph9Lam+ttH2ynkAevykj4mBgi+gWwqpey2uW7KaLPSaxShj9kDQA3mP1fzsV/u0y1rB02Nlin/YIxVvOqU1FT9p8SwoXVVu1sHUNck62VtDbN9xqUx5S/ikXrclEQKBgQCoTssOwEcK+Vty9KYcdfy4onTUHZBLdjxl8Iyqkxy7QTQUYRznkvesQPDXEDGO+kk3dyx2KKZt9Hl4IFNww2quPZcvcuMx4DQxjbXXpA8OIIxcta95NepLJwA+mRai3nKCH1A2TlNP7pFeMa5o+8IPly3Ix2lKr4Wepa4PN5i1pwKBgCZ1QP6XAOERl9NznNmU0rXVcvYNP4PIIfQWfvGsldZ4QKkmjjAGiS0/oYqdWs+UDRZyCRChaVjDXO9fk0PEG5OGKAj9nyiYCT/M8xtJ3UeP5ffZZvJ/vnye3QdDIo1e38ZzsWwJHmLYw7fRqY9W5Vxo0Vsy22U3CJY70KTxVdTy" ], + "keyUse" : [ "ENC" ], + "certificate" : [ "MIICmzCCAYMCBgGG3GWycDANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDdvW00t9yMUtTD1RKFUM1/5kPyxCpvhgFohdxIlNf/MNBQ+IKnXvoEEYjezhWmUMCuc5b4u6ajOYqiB6gcScYQgNbyi+Arm4xT21IDanSqoYFQw51HxgBNMh8Il7uoaKK4ZrdZnLexAszvRK5HPHTZhTyC9Ry+XYmzgrKoDOI5Rf/DdZrn0f2OMY1RdjKpo9Ymnvd4fNtVhbJLPIsbbaZWVxS0HHmyIvlPb9e7Sn3PiXv9tqnDCY5WLVmtweNsQDB0YSJIwYj+5aoAX6vUFGmYktHsc1dwvd4rj/npVgtqLBJ6N/Jh5EUwhMSf9TEZiFxdnnExRQIbRPGMGEtilMG7AgMBAAEwDQYJKoZIhvcNAQELBQADggEBAK3kQ1VkQrzvSWvmXmazmNoA1ZiPzRDs1XhGUWxgsxzgPylr3dGBuqQbKvgnLUBQLSqlJHpI4fZflHswu1qrvVZYtekPcGef4WhcKAu2i1RwxrKa6RJQ1tRbrLuVYCzPv5p/DWgltWVn88aoLnqQn0SK/0PB/o4a4Cm7Kq2ZzCr1dACBr06LvOHsc7249OySmbG4HH+pLK6jVURhZ9VaObqAHe2FJBVVoIzURbdiRRURqumrIvbnpeaU1aFyg6ED5wTnXvmMPmVPt9F79mcB33bASO5wyu00X8t1hyN2Show2l2vxLACGUzVkTQt15s7uDLKE7qLmKSR3EuSGXWv3wA=" ], + "priority" : [ "100" ], + "algorithm" : [ "RSA-OAEP" ] + } + }, { + "id" : "2293ff99-3c6d-46d1-8635-5e679d5b134a", + "name" : "rsa-generated", + "providerId" : "rsa-generated", + "subComponents" : { }, + "config" : { + "privateKey" : [ "MIIEpAIBAAKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQABAoIBADNcMt6hAHub4JTAYS6Mra0EPRBO2XhWmACBrv3+8ETClXd5475KPLDewgRVtlmtbwU8G8awUXESQgPS9lfiqvQhPreA3cHlm6oP2WMKOEtakr2s8I+frsTBLCo0Ini9RaSzjoVVgS0zofyhASKi+T970MafSj5P3XNb8YBFdXgoYDiA7FXLH6a/+m7LScL+wGcFMAAeYESxZbMQLfH3v8L+4EcTraiwjLG17ZdlF3dpybMyUSse6ZQ/PdlyvBuzzLXhN6Ce2gd9ATfS+YWTzo7Yf+GU+ex5bIpVOfHqtuM/hyq7YGKENClsXwNZIAoFnvGCbvECAfgyapVrD30IfykCgYEA0rgsSZ82pxT40NxwgBD1g9lbNVBKXphRB/3S078qusUzJjT7AldEj4imGPhAbI7bI8gAeWJsp1XJWkjM8ktaVrh+NQl7p8e9OPh0pQF/5Bdg8ajbjXESpjnaU66pVYRQy/d+jNli/YRAHX5RUfsBl+6W4+WSVMGmKBiqJsur+ecCgYEAz1YVXClcmUnyZem5B+2E9noIzjF6ROE+jIb6rawM85P3Xd0lXtECQavtxw+Qk7I32qOwrxl1UpK2foVel3pazi+4OpMfmqtYGenRP1Zk1cZwrDo0cIemTDGjj3kJ8tYn12CGolFQpJZgK6OHzvG0tOxI5VZgjIViWNPe1PGWXtUCgYEAxXGNDe8BZs1f11S2lUlOw5yGug3hoYFXbAWJ5p7Ziuf8ZXB/QlJDC7se54a11wKEk6Jzz0lKRgE8CjzszJuOqnN0zn10QGIIC7nCklo1W6QMUmPGVWH994N976tZP6gbjQL6sT+AYcvpx7j0ubxYYeRNvnz+ACzzY964kGGHY0ECgYEAumlwPPNnMN7+VEjGNm2D7UMdJZ3wi3tkjF5ThdA5uMohTsAk+FG80KSu3RmOaGyEsUwY7+VYyYvlDm4E9PZqLBVVczyR3rMNPAcwPd0EPfvzk7WlLkOX7ct3fehaXH3VRlyfz9KCSeh1wOZ/lT1VtpD2nVOC7PSDzs92+kfXZZ0CgYAnrD1y4skgXkdwolZ3unn3EFyGm2d+X5aMTHwQPdWxqoNIAl/9wdghlzihwnPhhsxq1WzlxuC3V2IMrNPtRx70Mi+FbSmR5m4Xx5RptgMtMlwno+L40PzNJgMjHGjt0wcx3Vel8wuohDtnqMyS7P5nG1/TQx0Cyzwn7QOXlNpgbQ==" ], + "keyUse" : [ "SIG" ], + "certificate" : [ "MIICmzCCAYMCBgGG3GWyBTANBgkqhkiG9w0BAQsFADARMQ8wDQYDVQQDDAZkYnJlcG8wHhcNMjMwMzEzMTkxMzE3WhcNMzMwMzEzMTkxNDU3WjARMQ8wDQYDVQQDDAZkYnJlcG8wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCqqcdDYFZZb28M0tEJzEP77FmD/Xqioyj9zWX6VwUSOMAgmMmn8eqs9hT9T0a+q4YTo9tUW1PNbUpwprA5b4Uk04DcIajxDVMUR/PjcHytmkqwVskq9AZW/Vngdoo+8tSbuIybwe/3Vwt266hbHpDcM97a+DXcYooRl7tQWCEX7RP27wQrMD9epDQ6IgKayZg9vC9/03dsIqwH9jXQRiZlFvwiEKhX2aY7lPGBaCK414JO00K/Z49iov9TRa/IYVbSt5qwgrx6DcqsBSPwOnI6A85UGfeUEZ/7coVJiL7RvBlsllapsL9eWTbQajVh94k9Ei3sibEPbtH+U2OAM78zAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAASnN1Cuif1sdfEK2kWAURSXGJCohCROLWdKFjaeHPRaEfpbFJsgxW0Yj3nwX5O3bUlOWoTyENwnXSsXMQsqnNi+At32CKaKO8+AkhAbgQL9F0B+KeJwmYv3cUj5N/LYkJjBvZBzUZ4Ugu5dcxH0k7AktLAIwimkyEnxTNolOA3UyrGGpREr8MCKWVr10RFuOpF/0CsJNNwbHXzalO9D756EUcRWZ9VSg6QVNso0YYRKTnILWDn9hcTRnqGy3SHo3anFTqQZ+BB57YbgFWy6udC0LYRB3zdp6zNti87eu/VEymiDY/mmo1AB8Tm0b6vxFz4AKcL3ax5qS6YnZ9efSzk=" ], + "priority" : [ "100" ] + } + } ] + }, + "internationalizationEnabled" : false, + "supportedLocales" : [ ], + "authenticationFlows" : [ { + "id" : "9c0078f1-0f2e-47c0-8cd6-78851acb1504", + "alias" : "Account verification options", + "description" : "Method with which to verity the existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-email-verification", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Verify Existing Account by Re-authentication", + "userSetupAllowed" : false + } ] + }, { + "id" : "ba90b374-8502-4a56-af42-f3a2aa931d32", + "alias" : "Authentication Options", + "description" : "Authentication options.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "basic-auth", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "basic-auth-otp", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "3bd266c5-b1fd-4f50-95f3-70e9e4e47fda", + "alias" : "Browser - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "11e6957d-ea18-4657-8639-fcf935eac694", + "alias" : "Direct Grant - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "c5791aee-2c8c-4077-bc02-b57d6bdfa2c8", + "alias" : "First broker login - Conditional OTP", + "description" : "Flow to determine if the OTP is required for the authentication", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-otp-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "ffd20971-0d90-44d9-943c-a1aaef135be7", + "alias" : "Handle Existing Account", + "description" : "Handle what to do if there is existing account with same email/username like authenticated identity provider", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-confirm-link", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Account verification options", + "userSetupAllowed" : false + } ] + }, { + "id" : "9593a3cc-c7fd-40a4-95fb-05a7b06ad04c", + "alias" : "Reset - Conditional OTP", + "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "conditional-user-configured", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-otp", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "7a81f226-53f3-407d-99cf-483b60eba0a9", + "alias" : "User creation or linking", + "description" : "Flow for the existing/non-existing user alternatives", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "create unique user config", + "authenticator" : "idp-create-user-if-unique", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Handle Existing Account", + "userSetupAllowed" : false + } ] + }, { + "id" : "90ab1710-d1e5-4f37-8c0e-5dc4acabf82b", + "alias" : "Verify Existing Account by Re-authentication", + "description" : "Reauthentication of existing account", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "idp-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "First broker login - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "ede4d86a-92d4-4803-bbb4-753f0fa7d33e", + "alias" : "browser", + "description" : "browser based authentication", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-cookie", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "auth-spnego", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "identity-provider-redirector", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 25, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "forms", + "userSetupAllowed" : false + } ] + }, { + "id" : "aac32d41-adc7-4a06-948e-660dcb305218", + "alias" : "clients", + "description" : "Base authentication for clients", + "providerId" : "client-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "client-secret", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-secret-jwt", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "client-x509", + "authenticatorFlow" : false, + "requirement" : "ALTERNATIVE", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "ade1ad88-2db7-4986-9cfa-b948e0808655", + "alias" : "direct grant", + "description" : "OpenID Connect Resource Owner Grant", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "direct-grant-validate-username", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "direct-grant-validate-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 30, + "autheticatorFlow" : true, + "flowAlias" : "Direct Grant - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "570ea615-bd6b-49f2-abb3-e6bd27f44e0d", + "alias" : "docker auth", + "description" : "Used by Docker clients to authenticate against the IDP", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "docker-http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "e878d8d8-0da7-4682-9124-483e8b8c6b59", + "alias" : "first broker login", + "description" : "Actions taken after first broker login with identity provider account, which is not yet linked to any Keycloak account", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticatorConfig" : "review profile config", + "authenticator" : "idp-review-profile", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "User creation or linking", + "userSetupAllowed" : false + } ] + }, { + "id" : "83aec897-602b-40c0-8df8-14abdba141bf", + "alias" : "forms", + "description" : "Username, password, otp and other auth forms.", + "providerId" : "basic-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "auth-username-password-form", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Browser - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "ddb3f5d4-6395-4c09-ba1a-593dd3c85c16", + "alias" : "http challenge", + "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "no-cookie-redirect", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : true, + "flowAlias" : "Authentication Options", + "userSetupAllowed" : false + } ] + }, { + "id" : "3e707d1d-bfa5-4e54-a2e5-cccadad9e75b", + "alias" : "registration", + "description" : "registration flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-page-form", + "authenticatorFlow" : true, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : true, + "flowAlias" : "registration form", + "userSetupAllowed" : false + } ] + }, { + "id" : "ed0157b5-6d81-47c9-8555-7082e92d5a62", + "alias" : "registration form", + "description" : "registration form", + "providerId" : "form-flow", + "topLevel" : false, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "registration-user-creation", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-profile-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 40, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-password-action", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 50, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "registration-recaptcha-action", + "authenticatorFlow" : false, + "requirement" : "DISABLED", + "priority" : 60, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + }, { + "id" : "c61c0070-feba-4770-b17f-7dfa3ba774ef", + "alias" : "reset credentials", + "description" : "Reset credentials for a user if they forgot their password or something", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "reset-credentials-choose-user", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-credential-email", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 20, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticator" : "reset-password", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 30, + "autheticatorFlow" : false, + "userSetupAllowed" : false + }, { + "authenticatorFlow" : true, + "requirement" : "CONDITIONAL", + "priority" : 40, + "autheticatorFlow" : true, + "flowAlias" : "Reset - Conditional OTP", + "userSetupAllowed" : false + } ] + }, { + "id" : "4a928f9c-461e-437a-8eb8-3f120bddbb98", + "alias" : "saml ecp", + "description" : "SAML ECP Profile Authentication Flow", + "providerId" : "basic-flow", + "topLevel" : true, + "builtIn" : true, + "authenticationExecutions" : [ { + "authenticator" : "http-basic-authenticator", + "authenticatorFlow" : false, + "requirement" : "REQUIRED", + "priority" : 10, + "autheticatorFlow" : false, + "userSetupAllowed" : false + } ] + } ], + "authenticatorConfig" : [ { + "id" : "03d31f7c-8f00-4d23-b0fc-4413c20d64c9", + "alias" : "create unique user config", + "config" : { + "require.password.update.after.registration" : "false" + } + }, { + "id" : "96db8eb0-e810-428e-a814-d753c8b086c6", + "alias" : "review profile config", + "config" : { + "update.profile.on.first.login" : "missing" + } + } ], + "requiredActions" : [ { + "alias" : "CONFIGURE_TOTP", + "name" : "Configure OTP", + "providerId" : "CONFIGURE_TOTP", + "enabled" : true, + "defaultAction" : false, + "priority" : 10, + "config" : { } + }, { + "alias" : "TERMS_AND_CONDITIONS", + "name" : "Terms and Conditions", + "providerId" : "TERMS_AND_CONDITIONS", + "enabled" : false, + "defaultAction" : false, + "priority" : 20, + "config" : { } + }, { + "alias" : "UPDATE_PASSWORD", + "name" : "Update Password", + "providerId" : "UPDATE_PASSWORD", + "enabled" : true, + "defaultAction" : false, + "priority" : 30, + "config" : { } + }, { + "alias" : "UPDATE_PROFILE", + "name" : "Update Profile", + "providerId" : "UPDATE_PROFILE", + "enabled" : true, + "defaultAction" : false, + "priority" : 40, + "config" : { } + }, { + "alias" : "VERIFY_EMAIL", + "name" : "Verify Email", + "providerId" : "VERIFY_EMAIL", + "enabled" : true, + "defaultAction" : false, + "priority" : 50, + "config" : { } + }, { + "alias" : "delete_account", + "name" : "Delete Account", + "providerId" : "delete_account", + "enabled" : false, + "defaultAction" : false, + "priority" : 60, + "config" : { } + }, { + "alias" : "webauthn-register", + "name" : "Webauthn Register", + "providerId" : "webauthn-register", + "enabled" : true, + "defaultAction" : false, + "priority" : 70, + "config" : { } + }, { + "alias" : "webauthn-register-passwordless", + "name" : "Webauthn Register Passwordless", + "providerId" : "webauthn-register-passwordless", + "enabled" : true, + "defaultAction" : false, + "priority" : 80, + "config" : { } + }, { + "alias" : "update_user_locale", + "name" : "Update User Locale", + "providerId" : "update_user_locale", + "enabled" : true, + "defaultAction" : false, + "priority" : 1000, + "config" : { } + } ], + "browserFlow" : "browser", + "registrationFlow" : "registration", + "directGrantFlow" : "direct grant", + "resetCredentialsFlow" : "reset credentials", + "clientAuthenticationFlow" : "clients", + "dockerAuthenticationFlow" : "docker auth", + "attributes" : { + "cibaBackchannelTokenDeliveryMode" : "poll", + "cibaExpiresIn" : "120", + "cibaAuthRequestedUserHint" : "login_hint", + "oauth2DeviceCodeLifespan" : "600", + "clientOfflineSessionMaxLifespan" : "0", + "oauth2DevicePollingInterval" : "5", + "clientSessionIdleTimeout" : "0", + "parRequestUriLifespan" : "60", + "clientSessionMaxLifespan" : "0", + "clientOfflineSessionIdleTimeout" : "0", + "cibaInterval" : "5", + "realmReusableOtpCode" : "false" + }, + "keycloakVersion" : "21.0.1", + "userManagedAccessAllowed" : false, + "clientProfiles" : { + "profiles" : [ ] + }, + "clientPolicies" : { + "policies" : [ ] + } +} \ No newline at end of file diff --git a/fda-authentication-service/docker-entrypoint.sh b/fda-authentication-service/docker-entrypoint.sh deleted file mode 100644 index cfc3f7e184..0000000000 --- a/fda-authentication-service/docker-entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/bash - -(java -Dlog4j2.formatMsgNoLookups=true -jar ./authentication-service.jar) & - -/opt/keycloak/bin/kc.sh start \ No newline at end of file diff --git a/fda-authentication-service/mvnw b/fda-authentication-service/mvnw deleted file mode 100755 index a16b5431b4..0000000000 --- a/fda-authentication-service/mvnw +++ /dev/null @@ -1,310 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Maven Start Up Batch script -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# M2_HOME - location of maven2's installed home dir -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - export JAVA_HOME="`/usr/libexec/java_home`" - else - export JAVA_HOME="/Library/Java/Home" - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -if [ -z "$M2_HOME" ] ; then - ## resolve links - $0 may be a link to maven's home - PRG="$0" - - # need this for relative symlinks - while [ -h "$PRG" ] ; do - ls=`ls -ld "$PRG"` - link=`expr "$ls" : '.*-> \(.*\)$'` - if expr "$link" : '/.*' > /dev/null; then - PRG="$link" - else - PRG="`dirname "$PRG"`/$link" - fi - done - - saveddir=`pwd` - - M2_HOME=`dirname "$PRG"`/.. - - # make it fully qualified - M2_HOME=`cd "$M2_HOME" && pwd` - - cd "$saveddir" - # echo Using m2 at $M2_HOME -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --unix "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$M2_HOME" ] && - M2_HOME="`(cd "$M2_HOME"; pwd)`" - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`which java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - echo "${basedir}" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=`find_maven_basedir "$(pwd)"` -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - else - jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) jarUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $jarUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget "$jarUrl" -O "$wrapperJarPath" - else - wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" - fi - elif command -v curl > /dev/null; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl -o "$wrapperJarPath" "$jarUrl" -f - else - curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f - fi - - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaClass" ]; then - if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaClass") - fi - if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$M2_HOME" ] && - M2_HOME=`cygpath --path --windows "$M2_HOME"` - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/fda-authentication-service/mvnw.cmd b/fda-authentication-service/mvnw.cmd deleted file mode 100644 index c8d43372c9..0000000000 --- a/fda-authentication-service/mvnw.cmd +++ /dev/null @@ -1,182 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM https://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Maven Start Up Batch script -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM M2_HOME - location of maven2's installed home dir -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" -if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - -FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %DOWNLOAD_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" -if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%" == "on" pause - -if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% - -exit /B %ERROR_CODE% diff --git a/fda-authentication-service/pom.xml b/fda-authentication-service/pom.xml deleted file mode 100644 index 3132c84732..0000000000 --- a/fda-authentication-service/pom.xml +++ /dev/null @@ -1,223 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-parent</artifactId> - <version>2.3.10.RELEASE</version> - </parent> - - <groupId>at.tuwien</groupId> - <artifactId>fda-authentication-service</artifactId> - <version>1.1.0-alpha</version> - <name>fda-authentication-service</name> - <description>Demo project for Spring Boot</description> - - <packaging>pom</packaging> - <modules> - <module>rest-service</module> - <module>services</module> - <module>report</module> - </modules> - - <properties> - <java.version>11</java.version> - <spring-cloud.version>3.0.1</spring-cloud.version> - <mapstruct.version>1.4.2.Final</mapstruct.version> - <swagger.version>2.1.7</swagger.version> - <springfox.version>3.0.0</springfox.version> - <jacoco.version>0.8.7</jacoco.version> - <spring-saml.version>1.0.10.RELEASE</spring-saml.version> - <javax-rs.version>2.1.1</javax-rs.version> - <javax-mail.version>1.4.7</javax-mail.version> - <jwt.version>3.18.3</jwt.version> - <docker.version>3.2.7</docker.version> - </properties> - - <dependencies> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-validation</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-web</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-security</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.cloud</groupId> - <artifactId>spring-cloud-starter-bootstrap</artifactId> - <version>${spring-cloud.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-mail</artifactId> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-actuator</artifactId> - </dependency> - <!-- Monitoring --> - <dependency> - <groupId>io.micrometer</groupId> - <artifactId>micrometer-registry-prometheus</artifactId> - <scope>runtime</scope> - </dependency> - <!-- Email Template --> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-thymeleaf</artifactId> - </dependency> - <!-- Entity and API --> - <dependency> - <groupId>at.tuwien</groupId> - <artifactId>fda-metadata-db-api</artifactId> - <version>${project.version}</version> - <scope>compile</scope> - </dependency> - <dependency> - <groupId>at.tuwien</groupId> - <artifactId>fda-metadata-db-entites</artifactId> - <version>${project.version}</version> - <scope>compile</scope> - </dependency> - <!-- Testing --> - <dependency> - <groupId>javax.ws.rs</groupId> - <artifactId>javax.ws.rs-api</artifactId> - <version>${javax-rs.version}</version> - </dependency> - <dependency> - <groupId>org.springframework.security</groupId> - <artifactId>spring-security-test</artifactId> - <scope>test</scope> - </dependency> - <dependency> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-starter-test</artifactId> - <scope>test</scope> - <exclusions> - <exclusion> - <groupId>org.junit.jupiter</groupId> - <artifactId>junit-vintage-engine</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <version>${jacoco.version}</version> - </dependency> - <!-- DataSource --> - <dependency> - <groupId>com.h2database</groupId> - <artifactId>h2</artifactId> - <scope>runtime</scope> - </dependency> - <dependency> - <groupId>org.mariadb.jdbc</groupId> - <artifactId>mariadb-java-client</artifactId> - <version>${mariadb.version}</version> - </dependency> - <!-- IDE --> - <dependency> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - </dependency> - <!-- Mapping --> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>${mapstruct.version}</version> - <optional>true</optional><!-- IntelliJ --> - </dependency> - <dependency> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct</artifactId> - <version>${mapstruct.version}</version> - </dependency> - <!-- AMPQ --> - <dependency> - <groupId>com.rabbitmq</groupId> - <artifactId>amqp-client</artifactId> - <version>${rabbit-amqp-client.version}</version> - </dependency> - <!-- JWT --> - <dependency> - <groupId>com.auth0</groupId> - <artifactId>java-jwt</artifactId> - <version>${jwt.version}</version> - </dependency> - <!-- Docker --> - <dependency> - <groupId>com.github.docker-java</groupId> - <artifactId>docker-java</artifactId> - <version>${docker.version}</version> - <scope>test</scope> - <exclusions> - <exclusion> - <groupId>javax.ws.rs</groupId> - <artifactId>jsr311-api</artifactId> - </exclusion> - </exclusions> - </dependency> - <dependency> - <groupId>com.github.docker-java</groupId> - <artifactId>docker-java-transport-httpclient5</artifactId> - <version>${docker.version}</version> - <scope>test</scope> - </dependency> - </dependencies> - - <build> - <resources> - <resource> - <directory>${basedir}/src/main/resources</directory> - <filtering>true</filtering> - <includes> - <include>**/application*.yml</include> - <include>**/*-mail.txt</include> - </includes> - </resource> - </resources> - <plugins> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <version>${jacoco.version}</version> - <configuration> - <excludes> - <exclude>at/tuwien/utils/**/*</exclude> - <exclude>at/tuwien/seeder/**/*</exclude> - <exclude>at/tuwien/mapper/**/*</exclude> - <exclude>at/tuwien/exception/**/*</exclude> - <exclude>at/tuwien/config/**/*</exclude> - <exclude>at/tuwien/auth/MariaDbPassword.class</exclude> - <exclude>**/FdaAuthenticationServiceApplication.class</exclude> - <exclude>**/ApiExceptionHandler.class</exclude> - </excludes> - </configuration> - <executions> - <execution> - <id>default-prepare-agent</id> - <goals> - <goal>prepare-agent</goal> - </goals> - </execution> - <execution> - <id>report</id> - <phase>verify</phase> - <goals> - <goal>report</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - -</project> diff --git a/fda-authentication-service/report/pom.xml b/fda-authentication-service/report/pom.xml deleted file mode 100644 index 9abba734e7..0000000000 --- a/fda-authentication-service/report/pom.xml +++ /dev/null @@ -1,56 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>at.tuwien</groupId> - <artifactId>fda-authentication-service</artifactId> - <version>1.1.0-alpha</version> - </parent> - - <artifactId>api</artifactId> - <version>1.1.0-alpha</version> - <name>fda-authentication-service-api</name> - <description> - This module is only intended for the pipeline coverage report. See the detailed report in the - respective modules - </description> - - <properties> - <jacoco.version>0.8.7</jacoco.version> - </properties> - - <dependencies> - <dependency> - <groupId>at.tuwien</groupId> - <artifactId>rest-service</artifactId> - <version>${project.version}</version> - </dependency> - <dependency> - <groupId>at.tuwien</groupId> - <artifactId>services</artifactId> - <version>${project.version}</version> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.jacoco</groupId> - <artifactId>jacoco-maven-plugin</artifactId> - <version>${jacoco.version}</version> - <executions> - <execution> - <id>report-aggregate</id> - <phase>verify</phase> - <goals> - <goal>report-aggregate</goal> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file diff --git a/fda-authentication-service/rest-service/pom.xml b/fda-authentication-service/rest-service/pom.xml deleted file mode 100644 index 12ecfdaaad..0000000000 --- a/fda-authentication-service/rest-service/pom.xml +++ /dev/null @@ -1,44 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <groupId>at.tuwien</groupId> - <artifactId>fda-authentication-service</artifactId> - <version>1.1.0-alpha</version> - </parent> - - <artifactId>rest-service</artifactId> - <version>1.1.0-alpha</version> - <name>fda-authentication-service-rest-service</name> - - <properties> - <jacoco.version>0.8.7</jacoco.version> - </properties> - - <dependencies> - <dependency> - <groupId>at.tuwien</groupId> - <artifactId>services</artifactId> - <version>${project.version}</version> - </dependency> - </dependencies> - - <build> - <plugins> - <plugin> - <groupId>org.springframework.boot</groupId> - <artifactId>spring-boot-maven-plugin</artifactId> - <executions> - <execution> - <goals> - <goal>repackage</goal><!-- to make it exuteable with $ java -jar ./app.jar --> - </goals> - </execution> - </executions> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/FdaAuthenticationServiceApplication.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/FdaAuthenticationServiceApplication.java deleted file mode 100644 index c116eb2738..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/FdaAuthenticationServiceApplication.java +++ /dev/null @@ -1,17 +0,0 @@ -package at.tuwien; - -import org.springframework.boot.SpringApplication; -import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; - -@EnableWebSecurity -@SpringBootApplication -@EnableJpaAuditing -public class FdaAuthenticationServiceApplication { - - public static void main(String[] args) { - SpringApplication.run(FdaAuthenticationServiceApplication.class, args); - } - -} diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java deleted file mode 100644 index 2250fa5088..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package at.tuwien.config; - -import com.google.common.io.Files; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.boot.context.event.ApplicationReadyEvent; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.event.EventListener; - -import java.io.File; -import java.io.IOException; - -@Log4j2 -@Configuration -public class ReadyConfig { - - @Value("${fda.ready.path}") - private String readyPath; - - @EventListener(ApplicationReadyEvent.class) - public void init() throws IOException { - Files.touch(new File(readyPath)); - } - -} diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java deleted file mode 100644 index f89da111a7..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/config/SwaggerConfig.java +++ /dev/null @@ -1,45 +0,0 @@ -package at.tuwien.config; - -import io.swagger.v3.oas.models.ExternalDocumentation; -import io.swagger.v3.oas.models.OpenAPI; -import io.swagger.v3.oas.models.info.Contact; -import io.swagger.v3.oas.models.info.Info; -import io.swagger.v3.oas.models.info.License; -import org.springdoc.core.GroupedOpenApi; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -@Configuration -public class SwaggerConfig { - - @Value("${app.version:unknown}") - private String version; - - @Bean - public OpenAPI springShopOpenAPI() { - return new OpenAPI() - .info(new Info() - .title("Database Repository Authentication Service API") - .contact(new Contact() - .name("Prof. Andreas Rauber") - .email("andreas.rauber@tuwien.ac.at")) - .description("Service that manages the authentication") - .version(version) - .license(new License() - .name("Apache 2.0") - .url("https://www.apache.org/licenses/LICENSE-2.0"))) - .externalDocs(new ExternalDocumentation() - .description("Wiki Documentation") - .url("https://gitlab.phaidra.org/fair-data-austria-db-repository/fda-services/-/wikis")); - } - - @Bean - public GroupedOpenApi publicApi() { - return GroupedOpenApi.builder() - .group("container-service") - .pathsToMatch("/api/**") - .build(); - } - -} diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/AuthenticationEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/AuthenticationEndpoint.java deleted file mode 100644 index 436ef9740a..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/AuthenticationEndpoint.java +++ /dev/null @@ -1,87 +0,0 @@ -package at.tuwien.endpoints; - -import at.tuwien.api.auth.JwtResponseDto; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.user.UserDto; -import at.tuwien.entities.user.User; -import at.tuwien.exception.OrcidMalformedException; -import at.tuwien.exception.TokenRevokedException; -import at.tuwien.exception.UserEmailNotVerifiedException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.mapper.UserMapper; -import at.tuwien.service.AuthenticationService; -import at.tuwien.service.UserService; -import io.micrometer.core.annotation.Timed; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpHeaders; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; - -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import java.security.Principal; - -@Log4j2 -@RestController -@CrossOrigin(origins = "*") -@ControllerAdvice -@RequestMapping("/api/auth") -public class AuthenticationEndpoint { - - private final UserMapper userMapper; - private final UserService userService; - private final AuthenticationService authenticationService; - - @Autowired - public AuthenticationEndpoint(UserMapper userMapper, UserService userService, - AuthenticationService authenticationService) { - this.userMapper = userMapper; - this.userService = userService; - this.authenticationService = authenticationService; - } - - @PostMapping - @Timed(value = "auth.create", description = "Time needed to create an authentication token") - @Operation(summary = "Create authentication token") - public ResponseEntity<JwtResponseDto> authenticateUser(@Valid @RequestBody LoginRequestDto data) - throws UserNotFoundException, UserEmailNotVerifiedException { - log.debug("endpoint create authentication token, data={}", data); - final JwtResponseDto response = authenticationService.authenticate(data); - return ResponseEntity.accepted() - .body(response); - } - - @PutMapping - @Transactional - @Timed(value = "auth.validate", description = "Time needed to validate an authentication token") - @Operation(summary = "Validate authentication token", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> authenticateUser(@NotNull Principal principal, - @RequestHeader(HttpHeaders.AUTHORIZATION) String authorization) - throws UserNotFoundException, OrcidMalformedException, TokenRevokedException { - log.debug("endpoint validate authentication token, principal={}, authorization={}", principal, authorization); - final User user = userService.findByUsername(principal.getName()); - log.trace("authentication for principal name {} retrieved user {}", principal.getName(), user); - final UserDto dto = userMapper.userToUserDto(user); - log.trace("mapped user to dto {}", dto); - authenticationService.verifyToken(authorization); - return ResponseEntity.accepted() - .body(dto); - } - - @PostMapping("/renew") - @Timed(value = "auth.renew", description = "Time needed to renew an authentication token") - @PreAuthorize("isAuthenticated()") - @Operation(summary = "Renew authentication token", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<JwtResponseDto> reAuthenticateUser(Principal principal) { - log.debug("endpoint renew authentication token, principal={}", principal); - final JwtResponseDto response = authenticationService.renew(principal); - return ResponseEntity.ok() - .body(response); - } - -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TimeSecretEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TimeSecretEndpoint.java deleted file mode 100644 index 47b7dce59c..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TimeSecretEndpoint.java +++ /dev/null @@ -1,93 +0,0 @@ -package at.tuwien.endpoints; - -import at.tuwien.api.user.UserForgotDto; -import at.tuwien.config.SecurityConfig; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.service.MailService; -import at.tuwien.service.TimeSecretService; -import at.tuwien.service.UserService; -import io.micrometer.core.annotation.Timed; -import io.swagger.v3.oas.annotations.Operation; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; -import org.thymeleaf.context.Context; - -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Null; -import java.security.Principal; - -@Log4j2 -@RestController -@CrossOrigin(origins = "*") -@ControllerAdvice -@RequestMapping("/api/user/secret") -public class TimeSecretEndpoint { - - private final UserService userService; - private final MailService mailService; - private final TimeSecretService tokenService; - private final SecurityConfig securityConfig; - - @Autowired - public TimeSecretEndpoint(UserService userService, MailService mailService, TimeSecretService tokenService, - SecurityConfig securityConfig) { - this.userService = userService; - this.mailService = mailService; - this.tokenService = tokenService; - this.securityConfig = securityConfig; - } - - @GetMapping - @Transactional - @Timed(value = "email.verify", description = "Time needed to verify the user email") - @Operation(summary = "verify user email") - public void verifyEmail(@NotNull @RequestParam String token, - @NotNull HttpServletResponse httpServletResponse, - @Null Principal principal) throws SecretInvalidException, NotAllowedException { - log.debug("endpoint verify user email, token={}, principal={}", token, principal); - if (principal != null) { - log.error("Failed to verify e-mail while being logged-in"); - throw new NotAllowedException("Failed to verify e-mail while being logged-in"); - } - tokenService.invalidate(token); - httpServletResponse.setHeader("Location", securityConfig.getWebsite() + "/login?email_verified"); - log.debug("redirect user to website {}", securityConfig.getWebsite() + "/login?email_verified"); - httpServletResponse.setStatus(302); - } - - @PostMapping("/resend") - @Transactional - @Timed(value = "email.resend", description = "Time needed to re-send the user email verification") - @Operation(summary = "resend user token") - public ResponseEntity<?> resend(@NotNull @Valid @RequestBody UserForgotDto data, - @Null Principal principal) throws UserNotFoundException, UserEmailFailedException, - UserEmailAlreadyVerifiedException, NotAllowedException { - log.debug("endpoint resend user token, data={}, principal={}", data, principal); - if (principal != null) { - log.error("Failed to verify e-mail while being logged-in"); - throw new NotAllowedException("Failed to verify e-mail while being logged-in"); - } - final User user = userService.findByUsernameOrEmail(data.getUsername(), data.getEmail()); - if (user.getEmailVerified()) { - log.error("Failed to resend user token for email {}, already verified", user.getEmail()); - log.trace("failed to resend user token for user {}", user); - throw new UserEmailAlreadyVerifiedException("Failed to resend user token, email already verified"); - } - final TimeSecret token = tokenService.create(user); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - context.setVariable("token", token.getToken()); - mailService.send(user, "E-Mail Verification", "mail-verify-email.txt", context); - return ResponseEntity.status(HttpStatus.OK) - .build(); - } - -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TokenEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TokenEndpoint.java deleted file mode 100644 index 3ab0910220..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/TokenEndpoint.java +++ /dev/null @@ -1,111 +0,0 @@ -package at.tuwien.endpoints; - -import at.tuwien.api.auth.TokenBriefDto; -import at.tuwien.api.auth.TokenDto; -import at.tuwien.config.AuthenticationConfig; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.NotAllowedException; -import at.tuwien.exception.TokenNotEligableException; -import at.tuwien.exception.TokenNotFoundException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.mapper.UserMapper; -import at.tuwien.service.TokenService; -import at.tuwien.service.UserService; -import io.micrometer.core.annotation.Timed; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; - -import javax.validation.constraints.NotNull; -import java.security.Principal; -import java.util.List; -import java.util.stream.Collectors; - -@Log4j2 -@RestController -@CrossOrigin(origins = "*") -@ControllerAdvice -@RequestMapping("/api/user/token") -public class TokenEndpoint { - - private final UserMapper userMapper; - private final UserService userService; - private final TokenService tokenService; - private final AuthenticationConfig authenticationConfig; - - @Autowired - public TokenEndpoint(UserMapper userMapper, UserService userService, TokenService tokenService, - AuthenticationConfig authenticationConfig) { - this.userMapper = userMapper; - this.userService = userService; - this.tokenService = tokenService; - this.authenticationConfig = authenticationConfig; - } - - @GetMapping - @Transactional(readOnly = true) - @PreAuthorize("isAuthenticated()") - @Timed(value = "token.list", description = "Time needed to list the developer tokens") - @Operation(summary = "Lists developer tokens for user", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<List<TokenBriefDto>> listAll(@NotNull Principal principal) throws UserNotFoundException { - log.debug("endpoint list developer tokens, principal={}", principal); - final List<Token> tokens = tokenService.findAll(principal); - log.trace("found all tokens {}", tokens); - final List<TokenBriefDto> dtos = tokens.stream() - .map(userMapper::tokenToTokenBriefDto) - .collect(Collectors.toList()); - log.info("Found {} tokens", dtos.size()); - return ResponseEntity.ok(dtos); - } - - @PostMapping - @Transactional - @PreAuthorize("hasRole('ROLE_RESEARCHER') or hasRole('ROLE_DEVELOPER')") - @Timed(value = "token.create", description = "Time needed to create a developer token") - @Operation(summary = "Create developer token", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<TokenDto> create(@NotNull Principal principal) throws UserNotFoundException, - TokenNotEligableException { - log.debug("endpoint create developer token, principal={}", principal); - /* check */ - final List<Token> tokens = tokenService.findAll(principal); - log.trace("found all tokens {}", tokens); - if (tokens.size() >= authenticationConfig.getTokenCount()) { - log.error("Failed to create token, already exceeded maximum quota of {}", authenticationConfig.getTokenCount()); - throw new TokenNotEligableException("Failed to create token"); - } - /* create */ - final Token token = tokenService.create(principal); - final TokenDto dto = userMapper.tokenToTokenDto(token); - log.trace("created developer token and resulting in {}", dto); - return ResponseEntity.status(HttpStatus.CREATED) - .body(dto); - } - - @DeleteMapping("/{id}") - @Transactional - @PreAuthorize("hasRole('ROLE_RESEARCHER') or hasRole('ROLE_DEVELOPER')") - @Timed(value = "token.delete", description = "Time needed to delete the developer tokens") - @Operation(summary = "Delete developer token", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<?> delete(@NotNull @PathVariable("id") Long id, - @NotNull Principal principal) throws TokenNotFoundException, UserNotFoundException, - NotAllowedException { - log.debug("endpoint delete developer token, id={}, principal={}", id, principal); - final Token token = tokenService.findOne(id); - final User user = userService.findByUsername(principal.getName()); - if (!token.getCreator().equals(user.getId())) { - log.error("Failed to delete token because it is not owned by the current user"); - throw new NotAllowedException("Failed to delete token because it is not owned by the current user"); - } - tokenService.delete(token.getTokenHash(), principal); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .build(); - } - -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java deleted file mode 100644 index 65cdb66e8c..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/endpoints/UserEndpoint.java +++ /dev/null @@ -1,267 +0,0 @@ -package at.tuwien.endpoints; - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.*; -import at.tuwien.config.SecurityConfig; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.User; -import at.tuwien.exception.RoleNotFoundException; -import at.tuwien.exception.UserEmailExistsException; -import at.tuwien.exception.UserEmailFailedException; -import at.tuwien.exception.UserNameExistsException; -import at.tuwien.exception.*; -import at.tuwien.mapper.UserMapper; -import at.tuwien.service.MailService; -import at.tuwien.service.QueueService; -import at.tuwien.service.TimeSecretService; -import at.tuwien.service.UserService; -import io.micrometer.core.annotation.Timed; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; -import joptsimple.internal.Strings; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.access.prepost.PreAuthorize; -import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.bind.annotation.*; -import org.thymeleaf.context.Context; - -import javax.servlet.http.HttpServletResponse; -import javax.validation.Valid; -import javax.validation.constraints.NotNull; -import javax.validation.constraints.Null; -import java.security.Principal; -import java.util.List; -import java.util.stream.Collectors; - -@Log4j2 -@RestController -@CrossOrigin(origins = "*") -@ControllerAdvice -@RequestMapping("/api/user") -public class UserEndpoint { - - private final UserMapper userMapper; - private final UserService userService; - private final MailService mailService; - private final TimeSecretService timeSecretService; - private final QueueService queueService; - private final SecurityConfig securityConfig; - - @Autowired - public UserEndpoint(UserMapper userMapper, UserService userService, MailService mailService, - TimeSecretService timeSecretService, QueueService queueService, SecurityConfig securityConfig) { - this.userMapper = userMapper; - this.userService = userService; - this.mailService = mailService; - this.timeSecretService = timeSecretService; - this.queueService = queueService; - this.securityConfig = securityConfig; - } - - @GetMapping - @Transactional(readOnly = true) - @Timed(value = "user.list", description = "Time needed to list the users") - @Operation(summary = "List users") - public ResponseEntity<List<UserBriefDto>> list() { - log.debug("endpoint list users"); - final List<UserBriefDto> users = userService.findAll() - .stream() - .map(userMapper::userToUserBriefDto) - .collect(Collectors.toList()); - log.info("Found {} users", users.size()); - log.trace("found users {}", users); - return ResponseEntity.ok(users); - } - - @PostMapping - @Transactional - @Timed(value = "user.create", description = "Time needed to create a user") - @Operation(summary = "Create user") - public ResponseEntity<UserDto> register(@NotNull @Valid @RequestBody SignupRequestDto data, - @Null Principal principal) - throws UserEmailExistsException, UserNameExistsException, RoleNotFoundException, UserEmailFailedException, - BrokerUserCreationException, OrcidMalformedException, NotAllowedException { - log.debug("endpoint create user, data={}, principal={}", data, principal); - if (principal != null) { - log.error("Failed to create user while being logged-in"); - throw new NotAllowedException("Failed to create user while being logged-in"); - } - final User user = userService.create(data); - queueService.createUser(user.getUsername(), data); - final TimeSecret token = timeSecretService.create(user); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - context.setVariable("token", token.getToken()); - mailService.send(user, "Account Creation", "mail-welcome.txt", context); - final UserDto dto = userMapper.userToUserDto(user); - log.trace("create user resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.CREATED) - .body(dto); - } - - @PutMapping - @Transactional - @Timed(value = "user.forgot", description = "Time needed to request a new user password") - @Operation(summary = "Request a new user password") - public ResponseEntity<UserDto> forgot(@NotNull @Valid @RequestBody UserForgotDto data, - @Null Principal principal) - throws UserNotFoundException, UserEmailFailedException, OrcidMalformedException, NotAllowedException { - log.debug("endpoint request a new user password, data={}, principal={}", data, principal); - if (principal != null) { - log.error("Failed to request a new user password while being logged-in"); - throw new NotAllowedException("Failed to request a new user password while being logged-in"); - } - final User user = userService.forgot(data); - final TimeSecret token = timeSecretService.create(user); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - context.setVariable("token", token.getToken()); - mailService.send(user, "Account Information", "mail-request-password-reset.txt", context); - final UserDto dto = userMapper.userToUserDto(user); - log.trace("forgot user information resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(dto); - } - - @PutMapping("/reset") - @Transactional - @Timed(value = "user.reset", description = "Time needed to reset a user password") - @Operation(summary = "Reset user password") - public void reset(@NotNull @Valid @RequestBody UserResetDto data, - @NotNull HttpServletResponse httpServletResponse, - @Null Principal principal) throws UserEmailFailedException, - SecretInvalidException, UserNotFoundException, BrokerUserCreationException, NotAllowedException { - log.debug("endpoint reset user information, data={}, principal={}", data, principal); - if (principal != null) { - log.error("Failed to reset user password while being logged-in"); - throw new NotAllowedException("Failed to reset user password while being logged-in"); - } - final User user = timeSecretService.invalidate(data.getToken()); - final UserPasswordDto userPasswordDto = userMapper.userResetDtoToUserPasswordDto(data); - userService.updatePassword(user.getId(), userPasswordDto); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - mailService.send(user, "Password Reset Successful!", "mail-password-changed.txt", context); - httpServletResponse.setHeader("Location", securityConfig.getWebsite() + "/login?password_reset"); - log.debug("redirect user to website {}", securityConfig.getWebsite() + "/login?password_reset"); - httpServletResponse.setStatus(302); - } - - @GetMapping("/{id}") - @Transactional(readOnly = true) - @Timed(value = "user.find", description = "Time needed to find a user") - @PreAuthorize("hasRole('ROLE_DEVELOPER')") - @Operation(summary = "Find some user", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> find(@NotNull @PathVariable("id") Long id) throws UserNotFoundException, - OrcidMalformedException { - log.debug("endpoint find user, id={}", id); - final User entity = userService.find(id); - final UserDto dto = userMapper.userToUserDto(entity); - log.trace("find user resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.OK) - .body(dto); - } - - @PutMapping("/{id}") - @Transactional - @Timed(value = "user.update", description = "Time needed to update a user") - @PreAuthorize("hasRole('ROLE_DEVELOPER') or hasPermission(#id, 'UPDATE_USER')") - @Operation(summary = "Update user", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> update(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody UserUpdateDto data) - throws UserNotFoundException, OrcidMalformedException { - log.debug("endpoint update user, id={}, data={}", id, data); - final User entity = userService.update(id, data); - final UserDto dto = userMapper.userToUserDto(entity); - log.trace("update user resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(dto); - } - - @PutMapping("/{id}/roles") - @Transactional - @Timed(value = "user.roles", description = "Time needed to update a user role") - @PreAuthorize("hasRole('ROLE_DEVELOPER')") - @Operation(summary = "Update user roles", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> updateRoles(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody UserRolesDto data) - throws UserNotFoundException, RoleNotFoundException, RoleUniqueException, OrcidMalformedException { - log.debug("endpoint update user roles, id={}, data={}", id, data); - final User entity = userService.updateRoles(id, data); - final UserDto dto = userMapper.userToUserDto(entity); - log.trace("update user roles resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(dto); - } - - @PutMapping("/{id}/theme") - @Transactional - @Timed(value = "user.theme", description = "Time needed to update a user theme") - @PreAuthorize("hasPermission(#id, 'UPDATE_THEME')") - @Operation(summary = "Update user theme", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<Void> updateTheme(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody UserThemeSetDto data) - throws UserNotFoundException { - log.debug("endpoint update user theme, id={}, data={}", id, data); - userService.updateTheme(id, data); - return ResponseEntity.accepted() - .build(); - } - - @PutMapping("/{id}/password") - @Transactional - @Timed(value = "user.password", description = "Time needed to update a user password") - @PreAuthorize("hasRole('ROLE_DEVELOPER') or hasPermission(#id, 'UPDATE_PASSWORD')") - @Operation(summary = "Update user password", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> updatePassword(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody UserPasswordDto data) - throws UserNotFoundException, BrokerUserCreationException, OrcidMalformedException, - UserEmailFailedException { - log.debug("endpoint update user password, id={}, data={}", id, data); - final User user = userService.updatePassword(id, data); - /* modify broker service */ - final UserDetailsDto details = queueService.findUser(user.getUsername()); - final CreateUserDto modifyDto = userMapper.userPasswordDtoToCreateUserDto(data); - if (details.getTags().length > 0) { - final String tags = Strings.join(details.getTags(), ","); - log.debug("found tags, setting the tags={}", tags); - modifyDto.setTags(tags); - } - queueService.modifyUserPassword(user, modifyDto); - log.info("Updated broker service password for user with id {}", user.getId()); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - mailService.send(user, "Password Reset Successful!", "mail-password-changed.txt", context); - final UserDto dto = userMapper.userToUserDto(user); - log.trace("update user password resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(dto); - } - - @PutMapping("/{id}/email") - @Transactional - @Timed(value = "user.email", description = "Time needed to update a user email") - @PreAuthorize("hasRole('ROLE_DEVELOPER') or hasPermission(#id, 'UPDATE_EMAIL')") - @Operation(summary = "Update user email", security = @SecurityRequirement(name = "bearerAuth")) - public ResponseEntity<UserDto> updateEmail(@NotNull @PathVariable("id") Long id, - @NotNull @Valid @RequestBody UserEmailDto data) - throws UserNotFoundException, OrcidMalformedException, UserEmailFailedException { - log.debug("endpoint update user email, id={}, data={}", id, data); - final User user = userService.updateEmail(id, data); - final TimeSecret token = timeSecretService.create(user); - final Context context = new Context(); - context.setVariable("username", user.getUsername()); - context.setVariable("token", token.getToken()); - mailService.send(user, "E-Mail Verification", "mail-verify-email.txt", context); - final UserDto dto = userMapper.userToUserDto(user); - log.trace("update user email resulted in user {}", dto); - return ResponseEntity.status(HttpStatus.ACCEPTED) - .body(dto); - } - -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java b/fda-authentication-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java deleted file mode 100644 index 1e88a3fc1f..0000000000 --- a/fda-authentication-service/rest-service/src/main/java/at/tuwien/handlers/ApiExceptionHandler.java +++ /dev/null @@ -1,204 +0,0 @@ -package at.tuwien.handlers; - -import at.tuwien.api.error.ApiErrorDto; -import at.tuwien.exception.*; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.ControllerAdvice; -import org.springframework.web.bind.annotation.ExceptionHandler; -import org.springframework.web.bind.annotation.ResponseStatus; -import org.springframework.web.context.request.WebRequest; -import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; - -@ControllerAdvice -public class ApiExceptionHandler extends ResponseEntityExceptionHandler { - - @ResponseStatus(HttpStatus.FORBIDDEN) - @ExceptionHandler(AuthenticationInvalidException.class) - public ResponseEntity<ApiErrorDto> handle(AuthenticationInvalidException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.FORBIDDEN) - .message(e.getLocalizedMessage()) - .code("error.auth.invalid") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(AuthenticationMalformedException.class) - public ResponseEntity<ApiErrorDto> handle(AuthenticationMalformedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.BAD_REQUEST) - .message(e.getLocalizedMessage()) - .code("error.auth.malformed") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED) - @ExceptionHandler(NotAllowedException.class) - public ResponseEntity<ApiErrorDto> handle(NotAllowedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.METHOD_NOT_ALLOWED) - .message(e.getLocalizedMessage()) - .code("error.auth.notallowed") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.NOT_ACCEPTABLE) - @ExceptionHandler(BrokerUserCreationException.class) - public ResponseEntity<ApiErrorDto> handle(BrokerUserCreationException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.NOT_ACCEPTABLE) - .message(e.getLocalizedMessage()) - .code("error.auth.createbrokeruser") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(OrcidMalformedException.class) - public ResponseEntity<ApiErrorDto> handle(OrcidMalformedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.BAD_REQUEST) - .message(e.getLocalizedMessage()) - .code("error.auth.orcid") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(RoleNotFoundException.class) - public ResponseEntity<ApiErrorDto> handle(RoleNotFoundException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.NOT_FOUND) - .message(e.getLocalizedMessage()) - .code("error.auth.role") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.BAD_REQUEST) - @ExceptionHandler(RoleUniqueException.class) - public ResponseEntity<ApiErrorDto> handle(RoleUniqueException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.BAD_REQUEST) - .message(e.getLocalizedMessage()) - .code("error.auth.unique-role") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.EXPECTATION_FAILED) - @ExceptionHandler(SecretInvalidException.class) - public ResponseEntity<ApiErrorDto> handle(SecretInvalidException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.EXPECTATION_FAILED) - .message(e.getLocalizedMessage()) - .code("error.auth.secretinvalid") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.EXPECTATION_FAILED) - @ExceptionHandler(TokenNotEligableException.class) - public ResponseEntity<ApiErrorDto> handle(TokenNotEligableException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.EXPECTATION_FAILED) - .message(e.getLocalizedMessage()) - .code("error.auth.tokennoteligable") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(TokenNotFoundException.class) - public ResponseEntity<ApiErrorDto> handle(TokenNotFoundException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.NOT_FOUND) - .message(e.getLocalizedMessage()) - .code("error.auth.tokennotfound") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.UNAUTHORIZED) - @ExceptionHandler(TokenRevokedException.class) - public ResponseEntity<ApiErrorDto> handle(TokenRevokedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.UNAUTHORIZED) - .message(e.getLocalizedMessage()) - .code("error.auth.tokenrevoked") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.FAILED_DEPENDENCY) - @ExceptionHandler(UserEmailAlreadyVerifiedException.class) - public ResponseEntity<ApiErrorDto> handle(UserEmailAlreadyVerifiedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.FAILED_DEPENDENCY) - .message(e.getLocalizedMessage()) - .code("error.auth.emailisverified") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.EXPECTATION_FAILED) - @ExceptionHandler(UserEmailExistsException.class) - public ResponseEntity<ApiErrorDto> handle(UserEmailExistsException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.EXPECTATION_FAILED) - .message(e.getLocalizedMessage()) - .code("error.auth.email") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.PRECONDITION_REQUIRED) - @ExceptionHandler(UserEmailFailedException.class) - public ResponseEntity<ApiErrorDto> handle(UserEmailFailedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.PRECONDITION_REQUIRED) - .message(e.getLocalizedMessage()) - .code("error.auth.emaildelivery") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.I_AM_A_TEAPOT) - @ExceptionHandler(UserEmailNotVerifiedException.class) - public ResponseEntity<ApiErrorDto> handle(UserEmailNotVerifiedException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.I_AM_A_TEAPOT) - .message(e.getLocalizedMessage()) - .code("error.auth.emailnotverified") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.CONFLICT) - @ExceptionHandler(UserNameExistsException.class) - public ResponseEntity<ApiErrorDto> handle(UserNameExistsException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.CONFLICT) - .message(e.getLocalizedMessage()) - .code("error.auth.username") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - - @ResponseStatus(HttpStatus.NOT_FOUND) - @ExceptionHandler(UserNotFoundException.class) - public ResponseEntity<ApiErrorDto> handle(UserNotFoundException e, WebRequest request) { - final ApiErrorDto response = ApiErrorDto.builder() - .status(HttpStatus.NOT_FOUND) - .message(e.getLocalizedMessage()) - .code("error.auth.user") - .build(); - return new ResponseEntity<>(response, new HttpHeaders(), response.getStatus()); - } - -} \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/resources/application-docker.yml b/fda-authentication-service/rest-service/src/main/resources/application-docker.yml deleted file mode 100644 index e0301e77ee..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/application-docker.yml +++ /dev/null @@ -1,62 +0,0 @@ -app.version: '@project.version@' -spring: - main.banner-mode: off - datasource: - url: jdbc:mariadb://metadata-db:3306/fda - driver-class-name: org.mariadb.jdbc.Driver - username: "${METADATA_USERNAME}" - password: "${METADATA_PASSWORD}" - jpa: - show-sql: false - database-platform: org.hibernate.dialect.MariaDBDialect - hibernate: - ddl-auto: validate - use-new-id-generator-mappings: false - open-in-view: false - properties: - hibernate: - default_schema: fda - jdbc: - time_zone: UTC - application: - name: authentication-service - cloud: - loadbalancer.ribbon.enabled: false - mail: - default-encoding: UTF-8 - host: "${SMTP_HOST}" - port: "${SMTP_PORT}" - username: "${SMTP_USERNAME}" - password: "${SMTP_PASSWORD}" - properties.mail.smtp: - timeout: 5000 - auth: true - starttls.enable: true -management.endpoints.web.exposure.include: health,info,prometheus -server: - port: 9097 -logging: - pattern.console: "%d %highlight(%-5level) %msg%n" - level: - root: warn - at.tuwien.: "${LOG_LEVEL}" - org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug -eureka: - instance.hostname: authentication-service - client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ -fda: - ready.path: /ready - website: "${WEBSITE}" - gateway.endpoint: "${GATEWAY_ENDPOINT}" - token.max: "${TOKEN_MAX}" - default_roles: "${DEFAULT_ROLES}" - superusers: "${SUPERUSERS}" - mail: - prefix: / - from: "${MAIL_FROM}" - replyto: "${MAIL_REPLY_TO}" - verify: false -jwt: - issuer: "${JWT_ISSUER}" - secret: "${JWT_SECRET}" - expiration.ms: "${JWT_EXPIRATION}" # 24 hrs \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/resources/application-local.yml b/fda-authentication-service/rest-service/src/main/resources/application-local.yml deleted file mode 100644 index ecc251a17c..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/application-local.yml +++ /dev/null @@ -1,66 +0,0 @@ -app.version: '@project.version@' -spring: - main.banner-mode: off - datasource: - url: jdbc:mariadb://localhost:3306/fda - driver-class-name: org.mariadb.jdbc.Driver - username: root - password: dbrepo - jpa: - show-sql: false - database-platform: org.hibernate.dialect.MariaDBDialect - hibernate: - ddl-auto: validate - use-new-id-generator-mappings: false - open-in-view: false - properties: - hibernate: - default_schema: fda - jdbc: - time_zone: UTC - application: - name: authentication-service - cloud: - loadbalancer.ribbon.enabled: false - rabbitmq: - host: localhost - username: fda - password: fda - mail: - default-encoding: UTF-8 - host: "" - port: 0 - username: "" - password: "" - properties.mail.smtp: - timeout: 5000 - auth: true - starttls.enable: true -management.endpoints.web.exposure.include: health,info,prometheus -server: - port: 9097 -logging: - pattern.console: "%d %highlight(%-5level) %msg%n" - level: - root: warn - at.tuwien.: trace - org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug -eureka: - instance.hostname: authentication-service - client.serviceUrl.defaultZone: http://localhost:9090/eureka/ -fda: - ready.path: ./ready - website: localhost:3000 - gateway.endpoint: http://localhost:9095/api/broker - token.max: 100 - default_roles: ROLE_RESEARCHER - superusers: dev1 - mail: - prefix: / - from: Nobody - replyto: Nobody - verify: false -jwt: - issuer: dbrepo - secret: dbrepo - expiration.ms: 100000000 \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/resources/application.yml b/fda-authentication-service/rest-service/src/main/resources/application.yml deleted file mode 100644 index 892984eeb2..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/application.yml +++ /dev/null @@ -1,65 +0,0 @@ -app.version: '@project.version@' -spring: - main.banner-mode: off - datasource: - url: "jdbc:mariadb://metadata-db:3306/${METADATA_DB}" - driver-class-name: org.mariadb.jdbc.Driver - username: "${METADATA_USERNAME}" - password: "${METADATA_PASSWORD}" - jpa: - show-sql: false - database-platform: org.hibernate.dialect.MariaDBDialect - hibernate: - ddl-auto: validate - use-new-id-generator-mappings: false - open-in-view: false - properties: - hibernate: - default_schema: "${METADATA_DB}" - jdbc: - time_zone: UTC - application: - name: authentication-service - cloud: - loadbalancer.ribbon.enabled: false - rabbitmq: - username: "${BROKER_USERNAME}" - password: "${BROKER_PASSWORD}" - mail: - default-encoding: UTF-8 - host: "${SMTP_HOST}" - port: "${SMTP_PORT}" - username: "${SMTP_USERNAME}" - password: "${SMTP_PASSWORD}" - properties.mail.smtp: - timeout: 5000 - auth: true - starttls.enable: true -management.endpoints.web.exposure.include: health,info,prometheus -server: - port: 9097 -logging: - pattern.console: "%d %highlight(%-5level) %msg%n" - level: - root: warn - at.tuwien.: "${LOG_LEVEL}" - org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug -eureka: - instance.hostname: authentication-service - client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ -fda: - ready.path: /ready - website: "${WEBSITE}" - gateway.endpoint: "${GATEWAY_ENDPOINT}" - token.max: "${TOKEN_MAX}" - default_roles: "${DEFAULT_ROLES}" - superusers: "${SUPERUSERS}" - mail: - prefix: / - from: "${MAIL_FROM}" - replyto: "${MAIL_REPLY_TO}" - verify: "${MAIL_VERIFY}" -jwt: - issuer: "${JWT_ISSUER}" - secret: "${JWT_SECRET}" - expiration.ms: "${JWT_EXPIRATION}" # 24 hrs \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/main/resources/templates/mail-password-changed.txt b/fda-authentication-service/rest-service/src/main/resources/templates/mail-password-changed.txt deleted file mode 100644 index 8115274478..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/templates/mail-password-changed.txt +++ /dev/null @@ -1,13 +0,0 @@ -Dear [[${username}]], - -Your password was successfully changed! - -In case this wasn't you, please contact us as soon as possible by replying to this mail! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/main/resources/templates/mail-request-password-reset.txt b/fda-authentication-service/rest-service/src/main/resources/templates/mail-request-password-reset.txt deleted file mode 100644 index 32670c5549..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/templates/mail-request-password-reset.txt +++ /dev/null @@ -1,13 +0,0 @@ -Dear [[${username}]], - -You can reset your password using the link below: - -[[${website}]]/reset?token=[[${token}]] - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/main/resources/templates/mail-verify-email.txt b/fda-authentication-service/rest-service/src/main/resources/templates/mail-verify-email.txt deleted file mode 100644 index d028698e04..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/templates/mail-verify-email.txt +++ /dev/null @@ -1,15 +0,0 @@ -Dear [[${username}]], - -Now please verify your e-mail address by clicking this link: - -[[${website}]]/api/user/token?token=[[${token}]] - -We are looking forward to your data in the repository! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/main/resources/templates/mail-welcome.txt b/fda-authentication-service/rest-service/src/main/resources/templates/mail-welcome.txt deleted file mode 100644 index 56c8369d22..0000000000 --- a/fda-authentication-service/rest-service/src/main/resources/templates/mail-welcome.txt +++ /dev/null @@ -1,17 +0,0 @@ -Dear [[${username}]], - -You have successfully created an account on the demo instance of DBRepo, a joint deployment of the TU Vienna and the University of Vienna as part of the FAIR Data Austria project. Now please verify your e-mail address by clicking this link: - -[[${website}]]/api/user/secret?token=[[${token}]] - -Please note that we reserve the right to make changes and new deployments to this demo instance. Your data will not be saved in such a case, and you may need to create a new account. If you want to receive updates on DBRepo, please subscribe to our mailing list: https://lists.univie.ac.at/mailman/listinfo/fairdata_dbrepo - -We are looking forward to your test data in the repository! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java deleted file mode 100644 index c6c927f9b9..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/BaseUnitTest.java +++ /dev/null @@ -1,358 +0,0 @@ -package at.tuwien; - -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.UserDetailsDto; -import at.tuwien.api.user.UserThemeSetDto; -import at.tuwien.entities.container.Container; -import at.tuwien.entities.container.image.ContainerImage; -import at.tuwien.entities.container.image.ContainerImageEnvironmentItem; -import at.tuwien.entities.container.image.ContainerImageEnvironmentItemType; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.test.context.TestPropertySource; - -import java.security.Principal; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.List; - -import static java.time.temporal.ChronoUnit.HOURS; -import static java.time.temporal.ChronoUnit.MILLIS; - -@TestPropertySource(locations = "classpath:application.properties") -public abstract class BaseUnitTest { - - public final static String JWT_1 = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJzdWIiOiJtd2Vpc2UiLCJybmQiOjk2NjIyNzAwMCwiZXhwIjoxNjczODg2MDk5LCJpYXQiOjE2NzM3OTk2OTl9.y1jqokCfZE7c_Ztt_nLQlf73jCYXPH5TZpCvo3RwS0C5azyrqLh03bphl6R8A24g6Kv_3qjzvnubNIwmO7y7pA"; - - public final static Long USER_1_ID = 1L; - public final static String USER_1_EMAIL = "john.doe@example.com"; - public final static String USER_1_USERNAME = "jdoe"; - public final static String USER_1_PASSWORD = "s3cr3t1nf0rm4t10n"; - public final static String USER_1_PASSWORD_ENCODED = "$2a$10$0dtdedA/RLTrFbUsvpbUw.I73AXOKeQP3t5UXj96OvnDEaDb3d3M6"; - public final static String USER_1_DATABASE_PASSWORD = "*A8C67ABBEAE837AABCF49680A157D85D44A117E9"; - public final static String USER_1_FIRSTNAME = "John"; - public final static String USER_1_LASTNAME = "Doe"; - public final static String USER_1_AFFILIATION = "TU Graz"; - public final static String USER_1_ORCID = "000000034216302X"; - public final static String USER_1_ORCID_UNCOMPRESSED = "0000-0003-4216-302X"; - public final static String USER_1_TITLES_BEFORE = "Dr."; - public final static String USER_1_TITLES_AFTER = "MSc BSc"; - public final static Boolean USER_1_VERIFIED = false; - public final static Boolean USER_1_THEME_DARK = false; - public final static Instant USER_1_CREATED = Instant.now() - .minus(1, ChronoUnit.DAYS); - public final static Instant USER_1_LAST_MODIFIED = USER_1_CREATED; - - public final static User USER_1 = User.builder() - .id(USER_1_ID) - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .password(USER_1_PASSWORD_ENCODED) - .databasePassword(USER_1_DATABASE_PASSWORD) - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID) - .titlesBefore(USER_1_TITLES_BEFORE) - .titlesAfter(USER_1_TITLES_AFTER) - .emailVerified(USER_1_VERIFIED) - .themeDark(USER_1_THEME_DARK) - .created(USER_1_CREATED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .lastModified(USER_1_LAST_MODIFIED) - .build(); - - public final static SignupRequestDto USER_1_SIGNUP_REQUEST_DTO = SignupRequestDto.builder() - .username(USER_1_USERNAME) - .password(USER_1_PASSWORD) - .email(USER_1_EMAIL) - .build(); - - public final static UserDetails USER_1_DETAILS = UserDetailsDto.builder() - .id(USER_1_ID) - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .password(USER_1_PASSWORD) - .authorities(List.of(new SimpleGrantedAuthority("ROLE_RESEARCHER"))) - .build(); - - public final static at.tuwien.api.amqp.UserDetailsDto USER_1_DETAILS_DTO = at.tuwien.api.amqp.UserDetailsDto.builder() - .name(USER_1_USERNAME) - .tags(new String[]{}) - .build(); - - public final static at.tuwien.api.amqp.UserDetailsDto USER_1_DETAILS_WITH_TAGS_DTO = at.tuwien.api.amqp.UserDetailsDto.builder() - .name(USER_1_USERNAME) - .tags(new String[]{"administrator"}) - .build(); - - public final static Principal USER_1_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_1_DETAILS, - USER_1_PASSWORD, USER_1_DETAILS.getAuthorities()); - - public final static Long USER_2_ID = 2L; - public final static String USER_2_EMAIL = "jane.doe@example.com"; - public final static String USER_2_USERNAME = "jdoe2"; - public final static String USER_2_FIRSTNAME = "Jane"; - public final static String USER_2_LASTNAME = "Doe"; - public final static String USER_2_AFFILIATION = "TU Wien"; - public final static String USER_2_ORCID = "0000000292726225"; - public final static String USER_2_ORCID_UNCOMPRESSED = "0000-0002-9272-6225"; - public final static String USER_2_PASSWORD = "s3cr3t1nf0rm4t10n"; - public final static String USER_2_DATABASE_PASSWORD = "*A8C67ABBEAE837AABCF49680A157D85D44A117E9"; - public final static Boolean USER_2_VERIFIED = true; - public final static Boolean USER_2_THEME_DARK = false; - public final static Instant USER_2_CREATED = Instant.now() - .minus(1, ChronoUnit.DAYS); - public final static Instant USER_2_LAST_MODIFIED = USER_1_CREATED; - - public final static User USER_2 = User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .password(USER_2_PASSWORD) - .databasePassword(USER_2_DATABASE_PASSWORD) - .firstname(USER_2_FIRSTNAME) - .lastname(USER_2_LASTNAME) - .affiliation(USER_2_AFFILIATION) - .orcid(USER_2_ORCID) - .emailVerified(USER_2_VERIFIED) - .themeDark(USER_2_THEME_DARK) - .created(USER_2_CREATED) - .roles(List.of(RoleType.ROLE_DEVELOPER)) - .lastModified(USER_2_LAST_MODIFIED) - .build(); - - public final static SignupRequestDto USER_2_SIGNUP_REQUEST_DTO = SignupRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_2_EMAIL) - .build(); - - public final static UserDetails USER_2_DETAILS = UserDetailsDto.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .password(USER_2_PASSWORD) - .authorities(List.of(new SimpleGrantedAuthority("ROLE_DEVELOPER"))) - .build(); - - public final static at.tuwien.api.amqp.UserDetailsDto USER_2_DETAILS_DTO = at.tuwien.api.amqp.UserDetailsDto.builder() - .name(USER_2_USERNAME) - .tags(new String[]{}) - .build(); - - public final static Principal USER_2_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_2_DETAILS, - USER_2_PASSWORD, USER_2_DETAILS.getAuthorities()); - - public final static Long USER_3_ID = 3L; - public final static String USER_3_EMAIL = "jonas.doe@example.com"; - public final static String USER_3_USERNAME = "jdoe3"; - public final static String USER_3_PASSWORD = "s3cr3t1nf0rm4t10n"; - public final static String USER_3_DATABASE_PASSWORD = "*A8C67ABBEAE837AABCF49680A157D85D44A117E9"; - public final static Boolean USER_3_VERIFIED = true; - public final static Boolean USER_3_THEME_DARK = false; - public final static Instant USER_3_CREATED = Instant.now() - .minus(1, ChronoUnit.DAYS); - public final static Instant USER_3_LAST_MODIFIED = USER_1_CREATED; - - public final static User USER_3 = User.builder() - .id(USER_3_ID) - .username(USER_3_USERNAME) - .email(USER_3_EMAIL) - .password(USER_3_PASSWORD) - .databasePassword(USER_3_DATABASE_PASSWORD) - .emailVerified(USER_3_VERIFIED) - .themeDark(USER_3_THEME_DARK) - .created(USER_3_CREATED) - .roles(List.of()) - .lastModified(USER_3_LAST_MODIFIED) - .build(); - - public final static SignupRequestDto USER_3_SIGNUP_REQUEST_DTO = SignupRequestDto.builder() - .username(USER_3_USERNAME) - .password(USER_3_PASSWORD) - .email(USER_3_EMAIL) - .build(); - - public final static UserDetails USER_3_DETAILS = UserDetailsDto.builder() - .id(USER_3_ID) - .username(USER_3_USERNAME) - .email(USER_3_EMAIL) - .password(USER_3_PASSWORD) - .authorities(List.of()) - .build(); - - public final static at.tuwien.api.amqp.UserDetailsDto USER_3_DETAILS_DTO = at.tuwien.api.amqp.UserDetailsDto.builder() - .name(USER_3_USERNAME) - .tags(new String[]{}) - .build(); - - public final static Principal USER_3_PRINCIPAL = new UsernamePasswordAuthenticationToken(USER_3_DETAILS, - USER_3_PASSWORD, USER_3_DETAILS.getAuthorities()); - - public final static UserThemeSetDto USER_THEME_DARK_DTO = UserThemeSetDto.builder() - .themeDark(true) - .build(); - - public final static UserThemeSetDto USER_THEME_LIGHT_DTO = UserThemeSetDto.builder() - .themeDark(false) - .build(); - - public final static Long TIME_SECRET_1_ID = 1L; - public final static Boolean TIME_SECRET_1_PROCESSED = false; - public final static String TIME_SECRET_1_TOKEN = "mysecrettokenrandomlygenerated"; - public final static Instant TIME_SECRET_1_VALID_TO = Instant.now() - .plus(1, ChronoUnit.DAYS); - - public final static TimeSecret TIME_SECRET_1 = TimeSecret.builder() - .id(TIME_SECRET_1_ID) - .uid(USER_1_ID) - .user(USER_1) - .token(TIME_SECRET_1_TOKEN) - .processed(TIME_SECRET_1_PROCESSED) - .validTo(TIME_SECRET_1_VALID_TO) - .build(); - - public final static Long TIME_SECRET_2_ID = 2L; - public final static Boolean TIME_SECRET_2_PROCESSED = true; - public final static String TIME_SECRET_2_TOKEN = "blahblahblah"; - public final static Instant TIME_SECRET_2_VALID_TO = Instant.now() - .plus(1, ChronoUnit.DAYS); - - public final static TimeSecret TIME_SECRET_2 = TimeSecret.builder() - .id(TIME_SECRET_2_ID) - .uid(USER_2_ID) - .user(USER_2) - .token(TIME_SECRET_2_TOKEN) - .processed(TIME_SECRET_2_PROCESSED) - .validTo(TIME_SECRET_2_VALID_TO) - .build(); - - public final static Long TIME_SECRET_3_ID = 3L; - public final static Boolean TIME_SECRET_3_PROCESSED = false; - public final static String TIME_SECRET_3_TOKEN = "blahblahblah"; - public final static Instant TIME_SECRET_3_VALID_TO = Instant.now() - .plus(1, ChronoUnit.DAYS); - - public final static Long TOKEN_1_ID = 1L; - public final static String TOKEN_1_TOKEN = "Ul0ioy8oUl0ioy8o"; - public final static String TOKEN_1_TOKEN_HASH = "131290c0f8bbb4ab9348c3d95ae3b595b625bd130f2ee6a48803a4120ce9c147"; - public final static String TOKEN_1_AUTHORIZATION = "Bearer " + TOKEN_1_TOKEN; - public final static Instant TOKEN_1_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS); - - public final static Token TOKEN_1 = Token.builder() - .id(TOKEN_1_ID) - .token(TOKEN_1_TOKEN) - .tokenHash(TOKEN_1_TOKEN_HASH) - .creator(USER_1_ID) - .expires(TOKEN_1_EXPIRES) - .build(); - - public final static Long TOKEN_2_ID = 2L; - public final static String TOKEN_2_TOKEN = "Ul0ioy8oUl0ioy8o"; - public final static String TOKEN_2_TOKEN_HASH = "131290c0f8bbb4ab9348c3d95ae3b595b625bd130f2ee6a48803a4120ce9c147"; - public final static String TOKEN_2_AUTHORIZATION = "Bearer " + TOKEN_2_TOKEN; - public final static Instant TOKEN_2_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS); - - public final static Token TOKEN_2 = Token.builder() - .id(TOKEN_2_ID) - .token(TOKEN_2_TOKEN) - .tokenHash(TOKEN_2_TOKEN_HASH) - .creator(USER_2_ID) - .expires(TOKEN_2_EXPIRES) - .build(); - - public final static Token TOKEN_2_EXPIRED = Token.builder() - .id(TOKEN_2_ID) - .token(TOKEN_2_TOKEN) - .expires(Instant.now().minus(100000000, MILLIS)) - .build(); - - public final static Long TOKEN_3_ID = 3L; - public final static String TOKEN_3_TOKEN = "Ul0ioy8oUl0ioy8o"; - public final static String TOKEN_3_TOKEN_HASH = "131290c0f8bbb4ab9348c3d95ae3b595b625bd130f2ee6a48803a4120ce9c147"; - public final static String TOKEN_3_AUTHORIZATION = "Bearer " + TOKEN_3_TOKEN; - public final static Instant TOKEN_3_EXPIRES = Instant.now().plus(100000000, ChronoUnit.MILLIS); - - public final static Token TOKEN_3 = Token.builder() - .id(TOKEN_3_ID) - .token(TOKEN_3_TOKEN) - .tokenHash(TOKEN_3_TOKEN_HASH) - .creator(USER_3_ID) - .expires(TOKEN_3_EXPIRES) - .build(); - - public final static String IMAGE_BROKER_IMAGE = "rabbitmq"; - public final static String IMAGE_BROKER_TAG = "3-management-alpine"; - - public final static Long CONTAINER_BROKER_ID = 1L; - public final static String CONTAINER_BROKER_NAME = "broker-service"; - public final static String CONTAINER_BROKER_INTERNAL_NAME = "broker-service"; - public final static String CONTAINER_BROKER_IP = "172.31.0.2"; - - public final static Container CONTAINER_BROKER = Container.builder() - .id(CONTAINER_BROKER_ID) - .name(CONTAINER_BROKER_NAME) - .internalName(CONTAINER_BROKER_INTERNAL_NAME) - .ipAddress(CONTAINER_BROKER_IP) - .build(); - - public final static Long IMAGE_1_ID = 1L; - public final static String IMAGE_1_REPOSITORY = "mariadb"; - public final static String IMAGE_1_TAG = "10.5"; - public final static String IMAGE_1_HASH = "d6a5e003eae42397f7ee4589e9f21e231d3721ac131970d2286bd616e7f55bb4\n"; - public final static String IMAGE_1_DIALECT = "org.hibernate.dialect.MariaDBDialect"; - public final static String IMAGE_1_DRIVER = "org.mariadb.jdbc.Driver"; - public final static String IMAGE_1_JDBC = "mariadb"; - public final static String IMAGE_1_LOGO = "AAAA"; - public final static Integer IMAGE_1_PORT = 3306; - public final static Long IMAGE_1_SIZE = 12000L; - public final static Instant IMAGE_1_CREATED = Instant.now().minus(40, HOURS); - public final static Instant IMAGE_1_UPDATED = Instant.now().minus(39, HOURS); - public final static List<ContainerImageEnvironmentItem> IMAGE_1_ENVIRONMENT = List.of(ContainerImageEnvironmentItem.builder() - .iid(IMAGE_1_ID) - .type(ContainerImageEnvironmentItemType.PRIVILEGED_PASSWORD) - .key("MARIADB_ROOT_PASSWORD") - .value("mariadb") - .build(), - ContainerImageEnvironmentItem.builder() - .iid(IMAGE_1_ID) - .type(ContainerImageEnvironmentItemType.PRIVILEGED_USERNAME) - .key("UZERNAME") - .value("root") - .build(), - ContainerImageEnvironmentItem.builder() - .iid(IMAGE_1_ID) - .type(ContainerImageEnvironmentItemType.USERNAME) - .key("MARIADB_USER") - .value("mariadb") - .build(), - ContainerImageEnvironmentItem.builder() - .iid(IMAGE_1_ID) - .type(ContainerImageEnvironmentItemType.PASSWORD) - .key("MARIADB_PASSWORD") - .value("mariadb") - .build()); - - public final static ContainerImage IMAGE_1 = ContainerImage.builder() - .id(IMAGE_1_ID) - .repository(IMAGE_1_REPOSITORY) - .tag(IMAGE_1_TAG) - .hash(IMAGE_1_HASH) - .size(IMAGE_1_SIZE) - .environment(IMAGE_1_ENVIRONMENT) - .dialect(IMAGE_1_DIALECT) - .driverClass(IMAGE_1_DRIVER) - .jdbcMethod(IMAGE_1_JDBC) - .created(IMAGE_1_CREATED) - .defaultPort(IMAGE_1_PORT) - .compiled(IMAGE_1_UPDATED) - .build(); - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java deleted file mode 100644 index 92adfa7fa2..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/AuthTokenFilterTest.java +++ /dev/null @@ -1,112 +0,0 @@ -package at.tuwien.auth; - -import at.tuwien.BaseUnitTest; -import at.tuwien.config.H2Utils; -import at.tuwien.config.ReadyConfig; -import at.tuwien.repositories.UserRepository; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.mock.web.MockFilterChain; -import org.springframework.mock.web.MockHttpServletRequest; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.test.context.junit.jupiter.SpringExtension; - - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; - -import java.io.IOException; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class AuthTokenFilterTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private UserRepository userRepository; - - @Autowired - private AuthTokenFilter authTokenFilter; - - @Autowired - private H2Utils h2Utils; - - @BeforeEach - public void beforeEach() { - h2Utils.runScript("view.sql"); - } - - @Test - public void doFilterInternal_notFound_fails() throws ServletException, IOException { - final MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Bearer " + JWT_1); - final MockHttpServletResponse response = new MockHttpServletResponse(); - final FilterChain chain = new MockFilterChain(); - - /* mock */ - when(userRepository.findByUsername("mweise")) - .thenReturn(Optional.empty()); - - /* test */ - authTokenFilter.doFilterInternal(request, response, chain); - assertEquals(401, response.getStatus()); - } - - @Test - public void doFilterInternal_succeeds() throws ServletException, IOException { - final MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Bearer " + JWT_1); - final MockHttpServletResponse response = new MockHttpServletResponse(); - final FilterChain chain = new MockFilterChain(); - - /* mock */ - when(userRepository.findByUsername("mweise")) - .thenReturn(Optional.of(USER_1)); - - /* test */ - authTokenFilter.doFilterInternal(request, response, chain); - assertEquals(200, response.getStatus()); - } - - @Test - public void parseJwt_succeeds() { - final MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Bearer " + JWT_1); - - /* test */ - final String response = authTokenFilter.parseJwt(request); - assertEquals(JWT_1, response); - } - - @Test - public void parseJwt_fails() { - final MockHttpServletRequest request = new MockHttpServletRequest(); - request.addHeader("Authorization", "Basic dXNlcjpwYXNz"); - - /* test */ - final String response = authTokenFilter.parseJwt(request); - assertNull(response); - } - - @Test - public void parseJwt_noAuthenticationHeader_fails() { - final MockHttpServletRequest request = new MockHttpServletRequest(); - - /* test */ - final String response = authTokenFilter.parseJwt(request); - assertNull(response); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/JwtUtilsTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/JwtUtilsTest.java deleted file mode 100644 index eb7c4bdbfe..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/auth/JwtUtilsTest.java +++ /dev/null @@ -1,34 +0,0 @@ -package at.tuwien.auth; - -import at.tuwien.BaseUnitTest; -import at.tuwien.config.ReadyConfig; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.junit.jupiter.api.Assertions.*; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class JwtUtilsTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @Autowired - private JwtUtils jwtUtils; - - @Test - public void getUserNameFromJwtToken_succeeds() { - - /* test */ - final String response = jwtUtils.getUserNameFromJwtToken(JWT_1); - assertEquals("mweise", response); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java deleted file mode 100644 index 37b2ca7d1b..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/DockerConfig.java +++ /dev/null @@ -1,147 +0,0 @@ -package at.tuwien.config; - -import at.tuwien.entities.container.Container; -import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.command.InspectContainerResponse; -import com.github.dockerjava.api.exception.NotModifiedException; -import com.github.dockerjava.api.model.HostConfig; -import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.RestartPolicy; -import com.github.dockerjava.core.DefaultDockerClientConfig; -import com.github.dockerjava.core.DockerClientBuilder; -import com.github.dockerjava.core.DockerClientConfig; -import com.github.dockerjava.httpclient5.ApacheDockerHttpClient; -import com.github.dockerjava.transport.DockerHttpClient; -import lombok.extern.log4j.Log4j2; - -import java.util.Arrays; -import java.util.Objects; - -@Log4j2 -public class DockerConfig { - - private final static DockerClientConfig dockerClientConfig = DefaultDockerClientConfig.createDefaultConfigBuilder() - .withDockerHost("unix:///var/run/docker.sock") - .build(); - - private final static DockerHttpClient dockerHttpClient = new ApacheDockerHttpClient.Builder() - .dockerHost(dockerClientConfig.getDockerHost()) - .sslConfig(dockerClientConfig.getSSLConfig()) - .build(); - - public final static HostConfig hostConfig = HostConfig.newHostConfig() - .withRestartPolicy(RestartPolicy.alwaysRestart()); - - public final static DockerClient dockerClient = DockerClientBuilder.getInstance() - .withDockerHttpClient(dockerHttpClient) - .build(); - - public static void startContainer(Container container, Integer seconds) throws InterruptedException { - final InspectContainerResponse inspect = dockerClient.inspectContainerCmd(container.getHash()) - .exec(); - log.trace("container {} state {}", container.getHash(), inspect.getState().getStatus()); - if (Objects.equals(inspect.getState().getStatus(), "running")) { - return; - } - log.trace("container {} needs to be started", container.getHash()); - dockerClient.startContainerCmd(container.getHash()) - .exec(); - Thread.sleep(seconds * 1000L); - log.debug("container {} was started", container.getHash()); - } - - public static void startContainer(Container container) throws InterruptedException { - startContainer(container, 15); - } - - public static void createContainer(Container container) { - final CreateContainerResponse response = dockerClient.createContainerCmd(container.getImage().getRepository() + ":" + container.getImage().getTag()) - .withHostConfig(hostConfig.withNetworkMode("fda-userdb")) - .withName(container.getInternalName()) - .withIpv4Address("172.30.0.5") - .withHostName(container.getInternalName()) - .withEnv("MARIADB_USER=mariadb", "MARIADB_PASSWORD=mariadb", "MARIADB_ROOT_PASSWORD=mariadb", "MARIADB_DATABASE=weather") - .exec(); - container.setHash(response.getId()); - } - - public static void stopContainer(Container container) { - final InspectContainerResponse inspect = dockerClient.inspectContainerCmd(container.getHash()) - .exec(); - log.trace("container {} state {}", container.getHash(), inspect.getState().getStatus()); - if (!Objects.equals(inspect.getState().getStatus(), "running")) { - return; - } - log.trace("container {} needs to be stopped", container.getHash()); - dockerClient.stopContainerCmd(container.getHash()) - .exec(); - log.debug("container {} was stopped", container.getHash()); - } - - public static void removeContainer(Container container) { - final InspectContainerResponse inspect = dockerClient.inspectContainerCmd(container.getHash()) - .exec(); - log.trace("container {} state {}", container.getHash(), inspect.getState().getStatus()); - log.trace("container {} needs to be removed", container.getHash()); - dockerClient.removeContainerCmd(container.getHash()) - .exec(); - log.debug("container {} was removed", container.getHash()); - } - - public static void removeAllContainers() { - dockerClient.listContainersCmd() - .withShowAll(true) - .exec() - .forEach(container -> { - log.info("Delete container {}", Arrays.asList(container.getNames())); - try { - dockerClient.stopContainerCmd(container.getId()).exec(); - } catch (NotModifiedException e) { - // ignore - } - dockerClient.removeContainerCmd(container.getId()).exec(); - }); - dockerClient.listVolumesCmd() - .withDanglingFilter(true) - .exec() - .getVolumes() - .forEach(volume -> { - log.info("Delete volume {}", volume.getName()); - try { - dockerClient.removeVolumeCmd(volume.getName()).exec(); - } catch (NotModifiedException e) { - // ignore - } - }); - } - - public static void removeAllNetworks() { - dockerClient.listNetworksCmd() - .exec() - .stream() - .filter(n -> n.getName().startsWith("fda")) - .forEach(network -> { - log.info("Delete network {}", network.getName()); - dockerClient.removeNetworkCmd(network.getId()).exec(); - }); - } - - public static void createAllNetworks() { - dockerClient.createNetworkCmd() - .withName("fda-userdb") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.30.0.0/16"))) - .withEnableIpv6(false) - .exec(); - dockerClient.createNetworkCmd() - .withName("fda-public") - .withIpam(new Network.Ipam() - .withConfig(new Network.Ipam.Config() - .withSubnet("172.31.0.0/16"))) - .withEnableIpv6(false) - .exec(); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/H2Utils.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/H2Utils.java deleted file mode 100644 index f7799c00a0..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/H2Utils.java +++ /dev/null @@ -1,37 +0,0 @@ -package at.tuwien.config; - -import lombok.extern.log4j.Log4j2; -import org.codehaus.plexus.util.FileUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Component; -import org.springframework.transaction.annotation.Transactional; - -import javax.persistence.EntityManager; -import java.io.File; -import java.io.IOException; - -@Log4j2 -@Component -public class H2Utils { - - @Autowired - private EntityManager entityManager; - - @Transactional - public void runQuery(String query) { - log.debug("query={}", query); - entityManager.createNativeQuery(query) - .executeUpdate(); - } - - @Transactional - public void runScript(String scriptName) { - try { - runQuery(FileUtils.fileRead(new File("./src/test/resources/" + scriptName))); - } catch (IOException e) { - log.error("Failed to load script {}", scriptName); - throw new RuntimeException("Failed to load script", e); - } - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java deleted file mode 100644 index af291b3a88..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/config/RabbitMqConfig.java +++ /dev/null @@ -1,85 +0,0 @@ -package at.tuwien.config; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; -import at.tuwien.dto.AmqpUserBriefDto; -import lombok.extern.log4j.Log4j2; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.*; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; - -import java.net.URI; -import java.util.Base64; - -@Log4j2 -@Configuration -public class RabbitMqConfig extends BaseUnitTest { - - @Bean("junitRestTemplate") - public RestTemplate restTemplate() { - final RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory("http://" + CONTAINER_BROKER_IP + ":15672")); - return restTemplate; - } - - @Bean - public HttpHeaders httpHeaders() { - final String username = "guest"; - final String password = "guest"; - log.debug("add basic authorization header with username={}, password={}", username, password); - return httpHeaders(username, password); - } - - public HttpHeaders httpHeaders(String username, String password) { - final HttpHeaders headers = new HttpHeaders(); - final String basic = "Basic " + Base64.getEncoder().encodeToString((username + ":" + password).getBytes()); - headers.add("Authorization", basic); - log.debug("add basic authorization header with username={}, password={}", username, password); - return headers; - } - - public void addUser(String username, String password, String tags) { - final RestTemplate restTemplate = restTemplate(); - final CreateUserDto request = CreateUserDto.builder() - .password(password) - .tags(tags) - .build(); - final ResponseEntity<AmqpUserBriefDto> response = restTemplate.exchange("/api/users/" + username, HttpMethod.PUT, - new HttpEntity<>(request, httpHeaders()), AmqpUserBriefDto.class); - if (!response.getStatusCode().equals(HttpStatus.CREATED)) { - log.error("Failed to add user, status is: {}", response.getStatusCode()); - throw new RuntimeException("Failed to add user"); - } - } - - public void grantAccess(String username) { - final RestTemplate restTemplate = restTemplate(); - final GrantVirtualHostPermissionsDto request = GrantVirtualHostPermissionsDto.builder() - .configure(".*") - .write(".*") - .read(".*") - .build(); - final URI uri = URI.create("http://" + CONTAINER_BROKER_IP + ":15672/api/permissions/%2F/" + username); - final ResponseEntity<AmqpUserBriefDto> response = restTemplate.exchange(uri, HttpMethod.PUT, - new HttpEntity<>(request, httpHeaders()), AmqpUserBriefDto.class); - if (!response.getStatusCode().equals(HttpStatus.CREATED)) { - log.error("Failed to grant access, status is: {}", response.getStatusCode()); - throw new RuntimeException("Failed to grant access"); - } - } - - public AmqpUserBriefDto whoami(String username, String password) { - final RestTemplate restTemplate = restTemplate(); - final ResponseEntity<AmqpUserBriefDto> response = restTemplate.exchange("/api/whoami", HttpMethod.GET, - new HttpEntity<>(null, httpHeaders(username, password)), AmqpUserBriefDto.class); - if (!response.getStatusCode().equals(HttpStatus.OK)) { - log.error("Failed to determine whoami, status is: {}", response.getStatusCode()); - throw new RuntimeException("Failed to determine whoami"); - } - return response.getBody(); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/dto/AmqpUserBriefDto.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/dto/AmqpUserBriefDto.java deleted file mode 100644 index 3ef043c40f..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/dto/AmqpUserBriefDto.java +++ /dev/null @@ -1,17 +0,0 @@ -package at.tuwien.dto; - -import lombok.*; - -@Getter -@Setter -@Builder -@AllArgsConstructor -@NoArgsConstructor -@ToString -public class AmqpUserBriefDto { - - private String name; - - private String[] tags; - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/AuthenticationEndpointUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/AuthenticationEndpointUnitTest.java deleted file mode 100644 index 6563934a84..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/AuthenticationEndpointUnitTest.java +++ /dev/null @@ -1,417 +0,0 @@ -package at.tuwien.endpoint; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.JwtResponseDto; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.user.UserDto; -import at.tuwien.endpoints.AuthenticationEndpoint; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.AuthenticationService; -import com.auth0.jwt.exceptions.TokenExpiredException; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.core.Authentication; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.security.crypto.password.StandardPasswordEncoder; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.security.Principal; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.stream.Collectors; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class AuthenticationEndpointUnitTest extends BaseUnitTest { - - @MockBean - private UserRepository userRepository; - - @MockBean - private AuthenticationManager authenticationManager; - - @MockBean - private TokenRepository tokenRepository; - - @Autowired - private AuthenticationEndpoint authenticationEndpoint; - - @Test - public void authenticateUser_anonymous_succeeds() throws UserNotFoundException, UserEmailNotVerifiedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - - /* test */ - authenticateUser_generic(USER_2, USER_2_PRINCIPAL, request); - } - - @Test - public void authenticateUser_anonymousNotVerified_fails() { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(false) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - - /* test */ - assertThrows(UserEmailNotVerifiedException.class, () -> { - authenticateUser_generic(USER_2, USER_2_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"RESEARCHER"}) - public void authenticateUser_researcher_succeeds() throws UserNotFoundException, UserEmailNotVerifiedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - - /* test */ - authenticateUser_generic(USER_2, USER_2_PRINCIPAL, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void authenticateUser_developer_succeeds() throws UserNotFoundException, UserEmailNotVerifiedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - - /* test */ - authenticateUser_generic(USER_2, USER_2_PRINCIPAL, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DATA_STEWARD"}) - public void authenticateUser_dataSteward_succeeds() throws UserNotFoundException, UserEmailNotVerifiedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_DATA_STEWARD)) - .build())); - - /* test */ - authenticateUser_generic(USER_2, USER_2_PRINCIPAL, request); - } - - @Test - public void authenticateUser2_anonymous_succeeds() throws UserNotFoundException, OrcidMalformedException, - TokenRevokedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - final Token token = Token.builder() - .token(TOKEN_2_TOKEN) - .tokenHash(TOKEN_2_TOKEN_HASH) - .creator(USER_2_ID) - .expires(TOKEN_2_EXPIRES) - .lastUsed(null) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_DEVELOPER)) - .build())); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(token)); - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - authenticateUser2_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2_AUTHORIZATION, request); - } - - @Test - public void authenticateUser2_anonymousRevoked_succeeds() { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(TOKEN_2_EXPIRED)); - - /* test */ - assertThrows(TokenExpiredException.class, () -> { - authenticateUser2_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2_AUTHORIZATION, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"RESEARCHER"}) - public void authenticateUser2_researcher_succeeds() throws UserNotFoundException, OrcidMalformedException, - TokenRevokedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - final Token token = Token.builder() - .token(TOKEN_2_TOKEN) - .tokenHash(TOKEN_2_TOKEN_HASH) - .creator(USER_2_ID) - .expires(TOKEN_2_EXPIRES) - .lastUsed(null) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_RESEARCHER)) - .build())); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(token)); - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - authenticateUser2_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2_AUTHORIZATION, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void authenticateUser2_developer_succeeds() throws UserNotFoundException, OrcidMalformedException, - TokenRevokedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - final Token token = Token.builder() - .token(TOKEN_2_TOKEN) - .tokenHash(TOKEN_2_TOKEN_HASH) - .creator(USER_2_ID) - .expires(TOKEN_2_EXPIRES) - .lastUsed(null) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_DEVELOPER)) - .build())); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(token)); - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - authenticateUser2_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2_AUTHORIZATION, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DATA_STEWARD"}) - public void authenticateUser2_dataSteward_succeeds() throws UserNotFoundException, OrcidMalformedException, - TokenRevokedException { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - final Token token = Token.builder() - .token(TOKEN_2_TOKEN) - .tokenHash(TOKEN_2_TOKEN_HASH) - .creator(USER_2_ID) - .expires(TOKEN_2_EXPIRES) - .lastUsed(null) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(User.builder() - .id(USER_2_ID) - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .emailVerified(USER_2_VERIFIED) - .roles(List.of(RoleType.ROLE_DATA_STEWARD)) - .build())); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(token)); - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - authenticateUser2_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2_AUTHORIZATION, request); - } - - @Test - public void reAuthenticateUser_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - reAuthenticateUser_generic(USER_2_PRINCIPAL); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void reAuthenticateUser_researcher_succeeds() { - - /* test */ - reAuthenticateUser_generic(USER_1_PRINCIPAL); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void reAuthenticateUser_developer_succeeds() { - - /* test */ - reAuthenticateUser_generic(USER_2_PRINCIPAL); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void reAuthenticateUser_dataSteward_succeeds() { - - /* test */ - reAuthenticateUser_generic(USER_3_PRINCIPAL); - } - - /* ################################################################################################### */ - /* ## GENERIC TEST CASES ## */ - /* ################################################################################################### */ - - protected void authenticateUser_generic(User user, Principal principal, LoginRequestDto data) throws UserNotFoundException, - UserEmailNotVerifiedException { - - /* mock */ - when(authenticationManager.authenticate(any(Authentication.class))) - .thenReturn((Authentication) principal); - - /* test */ - final ResponseEntity<JwtResponseDto> response = authenticationEndpoint.authenticateUser(data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - final JwtResponseDto body = response.getBody(); - assertNotNull(body); - assertEquals(user.getId(), body.getId()); - assertEquals(user.getUsername(), body.getUsername()); - assertEquals(user.getEmail(), body.getEmail()); - assertEquals(user.getRoles().stream().map(Enum::name).collect(Collectors.toList()), body.getRoles()); - assertNotNull(body.getToken()); - } - - protected void authenticateUser2_generic(User user, Principal principal, String authorization, LoginRequestDto data) throws UserNotFoundException, - OrcidMalformedException, TokenRevokedException { - - /* mock */ - when(authenticationManager.authenticate(any(Authentication.class))) - .thenReturn((Authentication) principal); - - /* test */ - final ResponseEntity<UserDto> response = authenticationEndpoint.authenticateUser(principal, authorization); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - final UserDto body = response.getBody(); - assertNotNull(body); - assertEquals(user.getId(), body.getId()); - assertEquals(user.getUsername(), body.getUsername()); - assertEquals(user.getEmail(), body.getEmail()); - } - - protected void reAuthenticateUser_generic(Principal principal) { - - /* test */ - final ResponseEntity<JwtResponseDto> response = authenticationEndpoint.reAuthenticateUser(principal); - assertEquals(HttpStatus.OK, response.getStatusCode()); - final JwtResponseDto body = response.getBody(); - assertNotNull(body); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TimeSecretEndpointUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TimeSecretEndpointUnitTest.java deleted file mode 100644 index 0c42db091f..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TimeSecretEndpointUnitTest.java +++ /dev/null @@ -1,240 +0,0 @@ -package at.tuwien.endpoint; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.user.UserForgotDto; -import at.tuwien.endpoints.TimeSecretEndpoint; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.MailService; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.thymeleaf.context.Context; - -import javax.servlet.http.HttpServletResponse; -import java.nio.file.AccessDeniedException; -import java.security.Principal; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class TimeSecretEndpointUnitTest extends BaseUnitTest { - - @MockBean - private UserRepository userRepository; - - @MockBean - private AuthenticationManager authenticationManager; - - @MockBean - private TimeSecretRepository timeSecretRepository; - - @MockBean - private TokenRepository tokenRepository; - - @MockBean - private MailService mailService; - - @Autowired - private TimeSecretEndpoint timeSecretEndpoint; - - @Test - public void verifyEmail_anonymous_succeeds() throws SecretInvalidException, NotAllowedException { - - /* test */ - verifyEmail_generic(TIME_SECRET_1, USER_1, null, TOKEN_1_TOKEN); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void verifyEmail_researcher_succeeds() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - verifyEmail_generic(TIME_SECRET_1, USER_1, USER_1_PRINCIPAL, TOKEN_1_TOKEN); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"DEVELOPER"}) - public void verifyEmail_developer_succeeds() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - verifyEmail_generic(TIME_SECRET_1, USER_1, USER_1_PRINCIPAL, TOKEN_1_TOKEN); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"DATA_STEWARD"}) - public void verifyEmail_dataSteward_succeeds() { - - /* test */ - assertThrows(NotAllowedException.class, () -> { - verifyEmail_generic(TIME_SECRET_1, USER_1, USER_1_PRINCIPAL, TOKEN_1_TOKEN); - }); - } - - @Test - public void resend_anonymous_succeeds() throws UserNotFoundException, UserEmailAlreadyVerifiedException, - UserEmailFailedException, NotAllowedException { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .build(); - final User user = User.builder() - .id(USER_1_ID) - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .emailVerified(false) - .build(); - - /* test */ - resend_generic(user, null, request); - } - - @Test - public void resend_anonymousAlreadyVerified_succeeds() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .build(); - - /* test */ - assertThrows(UserEmailAlreadyVerifiedException.class, () -> { - resend_generic(USER_1, null, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void resend_researcher_succeeds() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .build(); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - resend_generic(USER_1, USER_1_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void resend_researcherDifferent_succeeds() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .build(); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - resend_generic(USER_2, USER_2_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void resend_developer_succeeds() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .build(); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - resend_generic(USER_2, USER_2_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void resend_dataSteward_succeeds() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_3_USERNAME) - .email(USER_3_EMAIL) - .build(); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - resend_generic(USER_3, USER_3_PRINCIPAL, request); - }); - } - - /* ################################################################################################### */ - /* ## GENERIC TEST CASES ## */ - /* ################################################################################################### */ - - protected void verifyEmail_generic(TimeSecret timeSecret, User user, Principal principal, String token) - throws SecretInvalidException, NotAllowedException { - final HttpServletResponse mock = new MockHttpServletResponse(); - - /* mock */ - when(timeSecretRepository.findByToken(anyString())) - .thenReturn(Optional.of(timeSecret)); - when(timeSecretRepository.save(any(TimeSecret.class))) - .thenReturn(TimeSecret.builder() - .id(timeSecret.getId()) - .uid(timeSecret.getUid()) - .token(timeSecret.getToken()) - .processed(true) - .validTo(timeSecret.getValidTo()) - .build()); - when(userRepository.save(any(User.class))) - .thenReturn(user); - - /* test */ - timeSecretEndpoint.verifyEmail(token, mock, principal); - final String authentication = mock.getHeader("Location"); - assertNotNull(authentication); - assertTrue(authentication.contains("/login")); - assertEquals(302, mock.getStatus()); - } - - protected void resend_generic(User user, Principal principal, UserForgotDto data) throws UserEmailFailedException, - UserNotFoundException, UserEmailAlreadyVerifiedException, NotAllowedException { - - /* mock */ - if (user != null) { - when(userRepository.findByUsernameOrEmail(data.getUsername(), data.getEmail())) - .thenReturn(Optional.of(user)); - } else { - when(userRepository.findByUsernameOrEmail(data.getUsername(), data.getEmail())) - .thenReturn(Optional.empty()); - } - when(timeSecretRepository.save(any(TimeSecret.class))) - .thenReturn(TIME_SECRET_1); - when(userRepository.save(any(User.class))) - .thenReturn(user); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - final ResponseEntity<?> response = timeSecretEndpoint.resend(data, principal); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNull(response.getBody()); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TokenEndpointUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TokenEndpointUnitTest.java deleted file mode 100644 index 57e08e2497..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/TokenEndpointUnitTest.java +++ /dev/null @@ -1,310 +0,0 @@ -package at.tuwien.endpoint; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.TokenBriefDto; -import at.tuwien.api.auth.TokenDto; -import at.tuwien.api.user.UserForgotDto; -import at.tuwien.endpoints.TimeSecretEndpoint; -import at.tuwien.endpoints.TokenEndpoint; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.MailService; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.thymeleaf.context.Context; - -import javax.servlet.http.HttpServletResponse; -import java.security.Principal; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class TokenEndpointUnitTest extends BaseUnitTest { - - @MockBean - private UserRepository userRepository; - - @MockBean - private TokenRepository tokenRepository; - - @MockBean - private MailService mailService; - - @Autowired - private TokenEndpoint tokenEndpoint; - - @Test - public void listAll_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - listAll_generic(USER_1, null, List.of()); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void listAll_notFound_fails() { - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - listAll_generic(null, USER_1_PRINCIPAL, null); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void listAll_researcher_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_1, USER_1_PRINCIPAL, List.of(TOKEN_1)); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void listAll_developer_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_2, USER_2_PRINCIPAL, List.of(TOKEN_2)); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void listAll_dataSteward_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_3, USER_3_PRINCIPAL, List.of(TOKEN_3)); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void listAll_researcherEmpty_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_1, USER_1_PRINCIPAL, List.of()); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void listAll_developerEmpty_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_2, USER_2_PRINCIPAL, List.of()); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void listAll_dataStewardEmpty_succeeds() throws UserNotFoundException { - - /* test */ - listAll_generic(USER_3, USER_3_PRINCIPAL, List.of()); - } - - @Test - public void create_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - create_generic(USER_1, null, TOKEN_1, List.of()); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void create_researcher_succeeds() throws UserNotFoundException, TokenNotEligableException { - - /* test */ - create_generic(USER_1, USER_1_PRINCIPAL, TOKEN_1, List.of()); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void create_researcherLimitExceeded_fails() { - - /* test */ - assertThrows(TokenNotEligableException.class, () -> { - create_generic(USER_1, USER_1_PRINCIPAL, TOKEN_1, List.of(TOKEN_1)); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void create_developer_succeeds() throws UserNotFoundException, TokenNotEligableException { - - /* test */ - create_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2, List.of()); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void create_developerLimitExceeded_fails() { - - /* test */ - assertThrows(TokenNotEligableException.class, () -> { - create_generic(USER_2, USER_2_PRINCIPAL, TOKEN_2, List.of(TOKEN_2)); - }); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void create_dataSteward_fails() { - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - create_generic(USER_3, USER_3_PRINCIPAL, TOKEN_3, List.of()); - }); - } - - @Test - public void delete_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - delete_generic(TOKEN_1_ID, TOKEN_1, null, null, null); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void delete_researcher_succeeds() throws UserNotFoundException, TokenNotFoundException, NotAllowedException { - - /* test */ - delete_generic(TOKEN_1_ID, TOKEN_1, USER_1_USERNAME, USER_1, USER_1_PRINCIPAL); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void delete_developer_succeeds() throws UserNotFoundException, TokenNotFoundException, NotAllowedException { - - /* test */ - delete_generic(TOKEN_2_ID, TOKEN_2, USER_2_USERNAME, USER_2, USER_2_PRINCIPAL); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void delete_dataSteward_fails() { - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - delete_generic(TOKEN_3_ID, TOKEN_3, USER_3_USERNAME, USER_3, USER_3_PRINCIPAL); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void delete_researcherForeign_fails() { - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - delete_generic(TOKEN_2_ID, TOKEN_2, USER_2_USERNAME, USER_2, USER_1_PRINCIPAL); - }); - } - - /* ################################################################################################### */ - /* ## GENERIC TEST CASES ## */ - /* ################################################################################################### */ - - protected void listAll_generic(User user, Principal principal, List<Token> tokens) throws UserNotFoundException { - - /* mock */ - if (user != null) { - when(userRepository.findByUsername(user.getUsername())) - .thenReturn(Optional.of(user)); - when(tokenRepository.findMine(user.getId())) - .thenReturn(tokens); - } else { - when(userRepository.findByUsername(anyString())) - .thenReturn(Optional.empty()); - when(tokenRepository.findMine(anyLong())) - .thenReturn(List.of()); - } - - /* test */ - final ResponseEntity<List<TokenBriefDto>> response = tokenEndpoint.listAll(principal); - assertEquals(HttpStatus.OK, response.getStatusCode()); - final List<TokenBriefDto> body = response.getBody(); - assertNotNull(body); - assertEquals(tokens.size(), body.size()); - } - - protected void create_generic(User user, Principal principal, Token token, List<Token> existingTokens) - throws UserNotFoundException, TokenNotEligableException { - - /* mock */ - if (user != null) { - when(userRepository.findByUsername(user.getUsername())) - .thenReturn(Optional.of(user)); - when(tokenRepository.findMine(user.getId())) - .thenReturn(existingTokens); - } else { - when(userRepository.findByUsername(anyString())) - .thenReturn(Optional.empty()); - when(tokenRepository.findMine(anyLong())) - .thenReturn(List.of()); - } - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - final ResponseEntity<TokenDto> response = tokenEndpoint.create(principal); - assertEquals(HttpStatus.CREATED, response.getStatusCode()); - final TokenDto body = response.getBody(); - assertNotNull(body); - } - - protected void delete_generic(Long tokenId, Token token, String username, User user, Principal principal) - throws UserNotFoundException, TokenNotFoundException, NotAllowedException { - - /* mock */ - if (user != null) { - when(tokenRepository.findById(tokenId)) - .thenReturn(Optional.of(token)); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.of(token)); - when(userRepository.findByUsername(username)) - .thenReturn(Optional.of(user)); - } else { - when(tokenRepository.findById(anyLong())) - .thenReturn(Optional.empty()); - when(tokenRepository.findByTokenHash(anyString())) - .thenReturn(Optional.empty()); - when(userRepository.findByUsername(username)) - .thenReturn(Optional.empty()); - } - when(tokenRepository.save(any(Token.class))) - .thenReturn(token); - - /* test */ - final ResponseEntity<?> response = tokenEndpoint.delete(tokenId, principal); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNull(response.getBody()); - } -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/UserEndpointUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/UserEndpointUnitTest.java deleted file mode 100644 index d58cfed178..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/endpoint/UserEndpointUnitTest.java +++ /dev/null @@ -1,992 +0,0 @@ -package at.tuwien.endpoint; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.*; -import at.tuwien.endpoints.UserEndpoint; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.MailService; -import at.tuwien.service.QueueService; -import at.tuwien.service.TimeSecretService; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Primary; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.mock.web.MockHttpServletResponse; -import org.springframework.security.access.AccessDeniedException; -import org.springframework.security.authentication.AuthenticationCredentialsNotFoundException; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.provisioning.InMemoryUserDetailsManager; -import org.springframework.security.test.context.support.WithMockUser; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.thymeleaf.context.Context; - -import javax.servlet.http.HttpServletResponse; -import java.security.Principal; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.*; -import static org.mockito.Mockito.doNothing; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class UserEndpointUnitTest extends BaseUnitTest { - - @MockBean - private UserRepository userRepository; - - @MockBean - private TimeSecretService timeSecretService; - - @MockBean - private QueueService queueService; - - @MockBean - private MailService mailService; - - @Bean - @Primary - public UserDetailsService userDetailsService() { - return new InMemoryUserDetailsManager(List.of(USER_1_DETAILS, USER_2_DETAILS, USER_3_DETAILS)); - } - - @Autowired - private UserEndpoint userEndpoint; - - @Test - public void updateRoles_anonymous_fails() { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - updateRoles_generic(USER_3_ID, USER_3, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateRoles_researcherResearcher_fails() { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - updateRoles_generic(USER_3_ID, USER_3, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void updateRoles_researcherDeveloper_succeeds() throws UserNotFoundException, RoleUniqueException, - OrcidMalformedException, RoleNotFoundException { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - updateRoles_generic(USER_3_ID, USER_3, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"RESEARCHER", "DEVELOPER"}) - public void updateRoles_researcherDeveloperAndRearcher_succeeds() throws UserNotFoundException, RoleUniqueException, - OrcidMalformedException, RoleNotFoundException { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - updateRoles_generic(USER_3_ID, USER_3, request); - } - - @Test - public void updateTheme_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - updateTheme_generic(USER_1_ID, USER_1, USER_THEME_DARK_DTO); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateTheme_researcherDark_succeeds() throws UserNotFoundException { - - /* test */ - updateTheme_generic(USER_1_ID, USER_1, USER_THEME_DARK_DTO); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateTheme_researcherLightSame_succeeds() throws UserNotFoundException { - - /* test */ - updateTheme_generic(USER_1_ID, USER_1, USER_THEME_LIGHT_DTO); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateTheme_researcherDarkNotFound_fails() { - - /* test */ - assertThrows(UsernameNotFoundException.class, () -> { - updateTheme_generic(USER_1_ID, null, USER_THEME_DARK_DTO); - }); - } - - @Test - public void find_anonymous_fails() { - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - find_generic(USER_2_ID); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void find_researcher_fails() { - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - when(userRepository.findById(USER_2_ID)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - find_generic(USER_2_ID); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void find_developer_succeeds() throws UserNotFoundException, OrcidMalformedException { - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - find_generic(USER_1_ID); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void find_researcherNotFound_succeeds() { - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.empty()); - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - find_generic(USER_1_ID); - }); - } - - @Test - public void list_anonymous_succeeds() { - - /* test */ - list_generic(); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void list_researcher_succeeds() { - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - list_generic(); - } - - @Test - public void update_anonymous_fails() { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_2_FIRSTNAME) - .lastname(USER_2_LASTNAME) - .affiliation(USER_2_AFFILIATION) - .orcid(USER_2_ORCID_UNCOMPRESSED) - .build(); - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - update_generic(USER_2_ID, USER_2, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void update_developerModifyOther_succeeds() throws UserNotFoundException, OrcidMalformedException { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID_UNCOMPRESSED) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - final UserDto response = update_generic(USER_1_ID, USER_1, request); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_FIRSTNAME, response.getFirstname()); - assertEquals(USER_1_LASTNAME, response.getLastname()); - assertEquals(USER_1_ORCID_UNCOMPRESSED, response.getOrcid()); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void update_developerModifyOtherNotFound_fails() { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID_UNCOMPRESSED) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.empty()); - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - update_generic(USER_1_ID, USER_1, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void update_developer_succeeds() throws UserNotFoundException, OrcidMalformedException { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID_UNCOMPRESSED) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - final UserDto response = update_generic(USER_1_ID, USER_1, request); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_FIRSTNAME, response.getFirstname()); - assertEquals(USER_1_LASTNAME, response.getLastname()); - assertEquals(USER_1_ORCID_UNCOMPRESSED, response.getOrcid()); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void update_researcher_succeeds() throws UserNotFoundException, OrcidMalformedException { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID_UNCOMPRESSED) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - final UserDto response = update_generic(USER_1_ID, USER_1, request); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_FIRSTNAME, response.getFirstname()); - assertEquals(USER_1_LASTNAME, response.getLastname()); - assertEquals(USER_1_ORCID_UNCOMPRESSED, response.getOrcid()); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void update_researcherModifyOther_fails() { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid(USER_1_ORCID_UNCOMPRESSED) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - when(userRepository.findById(USER_2_ID)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - update_generic(USER_2_ID, USER_2, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void update_researcherInvalidOrcid_fails() { - final UserUpdateDto request = UserUpdateDto.builder() - .firstname(USER_1_FIRSTNAME) - .lastname(USER_1_LASTNAME) - .affiliation(USER_1_AFFILIATION) - .orcid("0000-0003-4216-3020") - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(OrcidMalformedException.class, () -> { - update_generic(USER_1_ID, USER_1, request); - }); - } - - @Test - public void register_anonymous_succeeds() throws UserNameExistsException, UserEmailFailedException, - BrokerUserCreationException, OrcidMalformedException, RoleNotFoundException, UserEmailExistsException, - NotAllowedException { - - /* test */ - final UserDto response = register_generic(USER_1_USERNAME, USER_1, null, USER_1_SIGNUP_REQUEST_DTO); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void register_researcher_fails() { - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - register_generic(USER_2_USERNAME, USER_2, USER_1_PRINCIPAL, USER_2_SIGNUP_REQUEST_DTO); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void register_developer_fails() { - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - register_generic(USER_1_USERNAME, USER_1, USER_2_PRINCIPAL, USER_1_SIGNUP_REQUEST_DTO); - }); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void register_dataSteward_fails() { - - /* mock */ - when(userRepository.findByUsername(USER_3_USERNAME)) - .thenReturn(Optional.of(USER_3)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - register_generic(USER_1_USERNAME, USER_1, USER_3_PRINCIPAL, USER_1_SIGNUP_REQUEST_DTO); - }); - } - - @Test - public void updateEmail_anonymous_fails() { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_1_EMAIL) - .build(); - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - updateEmail_generic(USER_1_ID, null, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateEmail_researcher_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_1_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - updateEmail_generic(USER_1_ID, USER_1, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void updateEmail_developer_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_2_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - updateEmail_generic(USER_2_ID, USER_2, request); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void updateEmail_dataSteward_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_3_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_3_USERNAME)) - .thenReturn(Optional.of(USER_3)); - - /* test */ - updateEmail_generic(USER_3_ID, USER_3, request); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateEmail_differentUser_fails() { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_1_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - updateEmail_generic(USER_2_ID, USER_2, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updateEmail_notExists_fails() { - final UserEmailDto request = UserEmailDto.builder() - .email(USER_1_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - updateEmail_generic(USER_1_ID, null, request); - }); - } - - @Test - public void updatePassword_anonymous_fails() { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* test */ - assertThrows(AuthenticationCredentialsNotFoundException.class, () -> { - updatePassword_generic(USER_1_ID, USER_1_USERNAME, null, USER_1_DETAILS_DTO, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updatePassword_researcher_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException, BrokerUserCreationException { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - updatePassword_generic(USER_1_ID, USER_1_USERNAME, USER_1, USER_1_DETAILS_DTO, request); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void updatePassword_developer_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException, BrokerUserCreationException { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - updatePassword_generic(USER_2_ID, USER_2_USERNAME, USER_2, USER_2_DETAILS_DTO, request); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void updatePassword_dataSteward_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException, BrokerUserCreationException { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_3_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_3_USERNAME)) - .thenReturn(Optional.of(USER_3)); - - /* test */ - updatePassword_generic(USER_3_ID, USER_3_USERNAME, USER_3, USER_3_DETAILS_DTO, request); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updatePassword_researcherRabbitMqRoles_succeeds() throws UserNotFoundException, UserEmailFailedException, - OrcidMalformedException, BrokerUserCreationException { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - updatePassword_generic(USER_1_ID, USER_1_USERNAME, USER_1, USER_1_DETAILS_WITH_TAGS_DTO, request); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updatePassword_differentUser_fails() { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - updatePassword_generic(USER_2_ID, USER_2_USERNAME, USER_2, USER_2_DETAILS_DTO, request); - }); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void updatePassword_notExists_fails() { - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(AccessDeniedException.class, () -> { - updatePassword_generic(USER_1_ID, USER_1_USERNAME, null, USER_1_DETAILS_DTO, request); - }); - } - - @Test - public void forgot_anonymous_succeeds() throws UserNotFoundException, NotAllowedException, UserEmailFailedException, - OrcidMalformedException { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_1_USERNAME, USER_1_EMAIL)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - forgot_generic(USER_1, null, request); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void forgot_researcher_fails() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_1_USERNAME) - .email(USER_1_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_1_USERNAME, USER_1_EMAIL)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - forgot_generic(USER_1, USER_1_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void forgot_developer_fails() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_2_USERNAME) - .email(USER_2_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_2_USERNAME, USER_2_EMAIL)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - forgot_generic(USER_2, USER_2_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void forgot_dataSteward_fails() { - final UserForgotDto request = UserForgotDto.builder() - .username(USER_3_USERNAME) - .email(USER_3_EMAIL) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_3_USERNAME, USER_3_EMAIL)) - .thenReturn(Optional.of(USER_3)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - forgot_generic(USER_3, USER_3_PRINCIPAL, request); - }); - } - - @Test - public void reset_anonymous_succeeds() throws UserNotFoundException, NotAllowedException, UserEmailFailedException, - BrokerUserCreationException, SecretInvalidException { - final UserResetDto request = UserResetDto.builder() - .password(USER_1_PASSWORD) - .token(TOKEN_1_TOKEN) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_1_USERNAME, USER_1_EMAIL)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - reset_generic(USER_1, null, request); - } - - @Test - @WithMockUser(username = USER_1_USERNAME, roles = {"RESEARCHER"}) - public void reset_researcher_fails() { - final UserResetDto request = UserResetDto.builder() - .password(USER_1_PASSWORD) - .token(TOKEN_1_TOKEN) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_1_USERNAME, USER_1_EMAIL)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - reset_generic(USER_1, USER_1_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_2_USERNAME, roles = {"DEVELOPER"}) - public void reset_developer_fails() { - final UserResetDto request = UserResetDto.builder() - .password(USER_2_PASSWORD) - .token(TOKEN_2_TOKEN) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_2_USERNAME, USER_2_EMAIL)) - .thenReturn(Optional.of(USER_2)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - reset_generic(USER_2, USER_2_PRINCIPAL, request); - }); - } - - @Test - @WithMockUser(username = USER_3_USERNAME, roles = {"DATA_STEWARD"}) - public void reset_dataSteward_fails() { - final UserResetDto request = UserResetDto.builder() - .password(USER_3_PASSWORD) - .token(TOKEN_3_TOKEN) - .build(); - - /* mock */ - when(userRepository.findByUsernameOrEmail(USER_3_USERNAME, USER_3_EMAIL)) - .thenReturn(Optional.of(USER_3)); - - /* test */ - assertThrows(NotAllowedException.class, () -> { - reset_generic(USER_3, USER_3_PRINCIPAL, request); - }); - } - - /* ################################################################################################### */ - /* ## GENERIC TEST CASES ## */ - /* ################################################################################################### */ - - protected void updateRoles_generic(Long userId, User user, UserRolesDto data) throws UserNotFoundException, - RoleUniqueException, OrcidMalformedException, RoleNotFoundException { - - /* mock */ - if (user == null) { - when(userRepository.findById(userId)) - .thenReturn(Optional.empty()); - } else { - when(userRepository.findById(userId)) - .thenReturn(Optional.of(user)); - } - when(userRepository.save(any(User.class))) - .thenReturn(user); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.updateRoles(userId, data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody()); - } - - protected void updateTheme_generic(Long userId, User user, UserThemeSetDto data) throws UserNotFoundException { - - /* mock */ - if (user == null) { - when(userRepository.findByUsername(anyString())) - .thenReturn(Optional.empty()); - when(userRepository.findById(userId)) - .thenReturn(Optional.empty()); - } else { - when(userRepository.findByUsername(user.getUsername())) - .thenReturn(Optional.of(user)); - when(userRepository.findById(userId)) - .thenReturn(Optional.of(user)); - } - when(userRepository.save(any(User.class))) - .thenReturn(user); - - /* test */ - final ResponseEntity<Void> response = userEndpoint.updateTheme(userId, data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNull(response.getBody()); - } - - protected void find_generic(Long userId) throws UserNotFoundException, OrcidMalformedException { - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.find(userId); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getBody()); - final UserDto body = response.getBody(); - assertEquals(USER_1_ID, body.getId()); - assertEquals(USER_1_USERNAME, body.getUsername()); - assertEquals(USER_1_EMAIL, body.getEmail()); - } - - protected void list_generic() { - - /* mock */ - when(userRepository.findAll()) - .thenReturn(List.of(USER_2)); - - /* test */ - final ResponseEntity<List<UserBriefDto>> response = userEndpoint.list(); - assertEquals(HttpStatus.OK, response.getStatusCode()); - assertNotNull(response.getBody()); - final List<UserBriefDto> body = response.getBody(); - assertEquals(1, body.size()); - assertEquals(USER_2_ID, body.get(0).getId()); - assertEquals(USER_2_USERNAME, body.get(0).getUsername()); - assertEquals(List.of("ROLE_DEVELOPER"), body.get(0).getRoles()); - } - - protected UserDto update_generic(Long userId, User user, UserUpdateDto data) throws UserNotFoundException, - OrcidMalformedException { - - /* mock */ - when(userRepository.save(any(User.class))) - .thenReturn(user); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.update(userId, data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody()); - return response.getBody(); - } - - protected UserDto register_generic(String username, User user, Principal principal, SignupRequestDto data) - throws OrcidMalformedException, UserNameExistsException, UserEmailFailedException, - BrokerUserCreationException, RoleNotFoundException, UserEmailExistsException, NotAllowedException { - - /* mock */ - when(userRepository.save(any(User.class))) - .thenReturn(user); - doNothing() - .when(queueService) - .createUser(eq(username), eq(data)); - when(timeSecretService.create(any(User.class))) - .thenReturn(TIME_SECRET_1); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.register(data, principal); - assertEquals(HttpStatus.CREATED, response.getStatusCode()); - assertNotNull(response.getBody()); - return response.getBody(); - } - - protected void updateEmail_generic(Long userId, User user, UserEmailDto data) - throws OrcidMalformedException, UserEmailFailedException, UserNotFoundException { - - /* mock */ - if (user == null) { - when(userRepository.findById(userId)) - .thenReturn(Optional.empty()); - } else { - when(userRepository.findById(userId)) - .thenReturn(Optional.of(user)); - } - when(userRepository.save(any(User.class))) - .thenReturn(user); - when(timeSecretService.create(any(User.class))) - .thenReturn(TIME_SECRET_1); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.updateEmail(userId, data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody()); - } - - protected void updatePassword_generic(Long userId, String username, User user, UserDetailsDto userDetails, - UserPasswordDto data) throws OrcidMalformedException, - UserEmailFailedException, UserNotFoundException, BrokerUserCreationException { - - /* mock */ - if (user == null) { - when(userRepository.findById(userId)) - .thenReturn(Optional.empty()); - } else { - when(userRepository.findById(userId)) - .thenReturn(Optional.of(user)); - } - when(queueService.findUser(username)) - .thenReturn(userDetails); - doNothing() - .when(queueService) - .modifyUserPassword(eq(user), any(CreateUserDto.class)); - when(userRepository.save(any(User.class))) - .thenReturn(user); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.updatePassword(userId, data); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody()); - } - - protected void forgot_generic(User user, Principal principal, UserForgotDto data) throws UserNotFoundException, - NotAllowedException, UserEmailFailedException, OrcidMalformedException { - - /* mock */ - when(timeSecretService.create(any(User.class))) - .thenReturn(TIME_SECRET_1); - when(userRepository.save(any(User.class))) - .thenReturn(user); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - final ResponseEntity<UserDto> response = userEndpoint.forgot(data, principal); - assertEquals(HttpStatus.ACCEPTED, response.getStatusCode()); - assertNotNull(response.getBody()); - } - - protected void reset_generic(User user, Principal principal, UserResetDto data) throws UserNotFoundException, - NotAllowedException, UserEmailFailedException, BrokerUserCreationException, SecretInvalidException { - final HttpServletResponse mock = new MockHttpServletResponse(); - - /* mock */ - when(timeSecretService.invalidate(data.getToken())) - .thenReturn(user); - when(userRepository.save(any(User.class))) - .thenReturn(user); - when( userRepository.findById(user.getId())) - .thenReturn(Optional.of(user)); - doNothing() - .when(mailService) - .send(eq(user), anyString(), anyString(), any(Context.class)); - - /* test */ - userEndpoint.reset(data, mock, principal); - final String header = mock.getHeader("Location"); - assertNotNull(header); - assertTrue(header.contains("/login")); - assertEquals(302, mock.getStatus()); - } - - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java deleted file mode 100644 index 739d2c6d6f..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/gateway/BrokerServiceGatewayUnitTest.java +++ /dev/null @@ -1,180 +0,0 @@ -package at.tuwien.gateway; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.config.ReadyConfig; -import at.tuwien.exception.BrokerUserCreationException; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.RestTemplate; - -import java.net.URI; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.any; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class BrokerServiceGatewayUnitTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - @Qualifier("gatewayRestTemplate") - private RestTemplate restTemplate; - - @Autowired - private BrokerServiceGateway brokerServiceGateway; - - @Test - public void createUser_succeeds() throws BrokerUserCreationException { - final CreateUserDto request = CreateUserDto.builder() - .password(USER_1_PASSWORD) - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.CREATED) - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - brokerServiceGateway.createUser(USER_1_USERNAME, request); - } - - @Test - public void createUser_fails() { - final CreateUserDto request = CreateUserDto.builder() - .password(USER_1_PASSWORD) - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NO_CONTENT) - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - assertThrows(BrokerUserCreationException.class, () -> { - brokerServiceGateway.createUser(USER_1_USERNAME, request); - }); - } - - @Test - public void findUser_succeeds() throws BrokerUserCreationException { - final ResponseEntity<UserDetailsDto> mock = ResponseEntity.ok() - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(UserDetailsDto.class))) - .thenReturn(mock); - - /* test */ - brokerServiceGateway.findUser(USER_1_USERNAME); - } - - @Test - public void findUser_fails() { - final ResponseEntity<UserDetailsDto> mock = ResponseEntity.status(HttpStatus.NOT_FOUND) - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.GET), any(HttpEntity.class), eq(UserDetailsDto.class))) - .thenReturn(mock); - - /* test */ - assertThrows(BrokerUserCreationException.class, () -> { - brokerServiceGateway.findUser(USER_1_USERNAME); - }); - } - - @Test - public void modifyHostPermissions_succeeds() throws BrokerUserCreationException { - final GrantVirtualHostPermissionsDto request = GrantVirtualHostPermissionsDto.builder() - .read(".*") - .write(".*") - .configure(".*") - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.CREATED) - .build(); - - /* mock */ - when(restTemplate.exchange(any(URI.class), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - brokerServiceGateway.modifyHostPermissions(USER_1_USERNAME, request); - } - - @Test - public void modifyHostPermissions_fails() { - final GrantVirtualHostPermissionsDto request = GrantVirtualHostPermissionsDto.builder() - .read(".*") - .write(".*") - .configure(".*") - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NOT_FOUND) - .build(); - - /* mock */ - when(restTemplate.exchange(any(URI.class), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - assertThrows(BrokerUserCreationException.class, () -> { - brokerServiceGateway.modifyHostPermissions(USER_1_USERNAME, request); - }); - } - - @Test - public void modifyUserPassword_succeeds() throws BrokerUserCreationException { - final CreateUserDto request = CreateUserDto.builder() - .password(USER_1_PASSWORD) - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.NO_CONTENT) - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - brokerServiceGateway.modifyUserPassword(USER_1_USERNAME, request); - } - - @Test - public void modifyUserPassword_fails() { - final CreateUserDto request = CreateUserDto.builder() - .password(USER_1_PASSWORD) - .build(); - final ResponseEntity<Void> mock = ResponseEntity.status(HttpStatus.OK) - .build(); - - /* mock */ - when(restTemplate.exchange(anyString(), eq(HttpMethod.PUT), any(HttpEntity.class), eq(Void.class))) - .thenReturn(mock); - - /* test */ - assertThrows(BrokerUserCreationException.class, () -> { - brokerServiceGateway.modifyUserPassword(USER_1_USERNAME, request); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java deleted file mode 100644 index dd7f291d2e..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/handlers/ApiExceptionHandlerTest.java +++ /dev/null @@ -1,50 +0,0 @@ -package at.tuwien.handlers; - -import at.tuwien.BaseUnitTest; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; -import org.springframework.core.type.filter.RegexPatternTypeFilter; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.lang.reflect.Method; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Set; -import java.util.regex.Pattern; - -import static org.junit.jupiter.api.Assertions.assertTrue; - -@Log4j2 -@ExtendWith(SpringExtension.class) -@SpringBootTest -public class ApiExceptionHandlerTest extends BaseUnitTest { - - @Test - public void handle_succeeds() throws ClassNotFoundException { - final List<Method> handlers = Arrays.asList(ApiExceptionHandler.class.getMethods()); - final List<Class<?>> exceptions = getExceptions(); - - /* test */ - for (Class<?> exception : exceptions) { - final boolean response = handlers.stream().anyMatch(h -> Arrays.asList(h.getParameterTypes()).contains(exception)); - assertTrue(response, "Exception " + exception.getName() + " does not have a corresponding handle method in the endpoint"); - } - } - - private List<Class<?>> getExceptions() throws ClassNotFoundException { - final ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); - provider.addIncludeFilter(new RegexPatternTypeFilter(Pattern.compile(".*"))); - final Set<BeanDefinition> beans = provider.findCandidateComponents("at.tuwien.exception"); - final List<Class<?>> exceptions = new LinkedList<>(); - for (BeanDefinition bean : beans) { - exceptions.add(Class.forName(bean.getBeanClassName())); - } - return exceptions; - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java deleted file mode 100644 index ce1d9be02d..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceIntegrationTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.User; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.UserRepository; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Disabled; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class AuthenticationServiceIntegrationTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private UserRepository userRepository; - - @MockBean - private TimeSecretRepository timeSecretRepository; - - @Autowired - private AuthenticationService authenticationService; - - @BeforeEach - public void beforeEach() { - TIME_SECRET_1.setUser(USER_1); - TIME_SECRET_2.setUser(USER_2); - } - - @Test - @Disabled - public void authenticate_fails() { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_1_USERNAME) - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - when(timeSecretRepository.findByToken(TIME_SECRET_1_TOKEN)) - .thenReturn(Optional.of(TIME_SECRET_1)); - - /* test */ - assertThrows(BadCredentialsException.class, () -> { - authenticationService.authenticate(request); - }); - } - - @Test - @Disabled - public void authenticate_verified_succeeds() { - final LoginRequestDto request = LoginRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .build(); - - /* mock */ - when(userRepository.findByUsername(USER_2_USERNAME)) - .thenReturn(Optional.of(USER_2)); - when(timeSecretRepository.findByToken(TIME_SECRET_2_TOKEN)) - .thenReturn(Optional.of(TIME_SECRET_2)); - - /* test */ - assertThrows(BadCredentialsException.class, () -> { - authenticationService.authenticate(request); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceUnitTest.java deleted file mode 100644 index 87ea00ffa0..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/AuthenticationServiceUnitTest.java +++ /dev/null @@ -1,45 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.auth.MariaDbPassword; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.User; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.UserRepository; -import com.rabbitmq.client.Channel; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.security.authentication.BadCredentialsException; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class AuthenticationServiceUnitTest extends BaseUnitTest { - - @MockBean - private Channel channel; - - @Test - public void authenticate_verified_succeeds() { - final String plaintext = "dbrepo"; - final String cipher = "*2F3AA43960B265DA32530022AE62B16E97BE51C3"; - - /* mock */ - - /* test */ - final String response = MariaDbPassword.encode(plaintext); - assertEquals(cipher, response); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/MailServiceUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/MailServiceUnitTest.java deleted file mode 100644 index 184ee0ca37..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/MailServiceUnitTest.java +++ /dev/null @@ -1,64 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.config.ReadyConfig; -import at.tuwien.exception.UserEmailFailedException; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.mail.MailSendException; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.TestPropertySource; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.thymeleaf.context.Context; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.doThrow; - -@Log4j2 -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@TestPropertySource(properties = {"spring.mail.username=test"}) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class MailServiceUnitTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @Autowired - private MailService mailService; - - @MockBean - private JavaMailSender mailSender; - - @Test - public void send_succeeds() throws UserEmailFailedException { - final Context context = new Context(); - context.setVariable("username", USER_1_USERNAME); - - /* test */ - mailService.send(USER_1, "Test", "mail-welcome.txt", context); - } - - @Test - public void send_fails() { - final Context context = new Context(); - context.setVariable("username", USER_1_USERNAME); - - /* mock */ - doThrow(MailSendException.class).when(mailSender) - .send(any(SimpleMailMessage.class)); - - /* test */ - assertThrows(UserEmailFailedException.class, () -> { - mailService.send(USER_1, "Test", "mail-welcome.txt", context); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java deleted file mode 100644 index ba84081446..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/QueueServiceIntegrationTest.java +++ /dev/null @@ -1,114 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.config.DockerConfig; -import at.tuwien.config.RabbitMqConfig; -import at.tuwien.config.ReadyConfig; -import at.tuwien.dto.AmqpUserBriefDto; -import at.tuwien.exception.*; -import at.tuwien.repositories.ImageRepository; -import at.tuwien.repositories.UserRepository; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.exception.NotModifiedException; -import com.github.dockerjava.api.model.Network; -import com.github.dockerjava.api.model.PortBinding; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.RestTemplate; - -import java.util.Arrays; - -import static at.tuwien.config.DockerConfig.*; -import static org.junit.jupiter.api.Assertions.assertEquals; - -@Log4j2 -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class QueueServiceIntegrationTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - /* keep */ - @Autowired - @Qualifier("junitRestTemplate") - private RestTemplate restTemplate; - - @Autowired - private ImageRepository imageRepository; - - @Autowired - private QueueService queueService; - - @Autowired - private UserRepository userRepository; - - @Autowired - private RabbitMqConfig amqpConfig; - - @BeforeAll - public static void beforeAll() { - afterAll(); - DockerConfig.createAllNetworks(); - } - - @BeforeEach - public void beforeEach() throws InterruptedException { - afterEach(); - DockerConfig.createAllNetworks(); - /* create container */ - final PortBinding binding = PortBinding.parse("15672:15672"); - final CreateContainerResponse response1 = dockerClient.createContainerCmd(IMAGE_BROKER_IMAGE + ":" + IMAGE_BROKER_TAG) - .withHostConfig(hostConfig.withNetworkMode("fda-public").withPortBindings(binding)) - .withName(CONTAINER_BROKER_NAME) - .withIpv4Address(CONTAINER_BROKER_IP) - .withHostName(CONTAINER_BROKER_INTERNAL_NAME) - .withVolumes() - .exec(); - CONTAINER_BROKER.setHash(response1.getId()); - startContainer(CONTAINER_BROKER); - /* metadata database */ - imageRepository.save(IMAGE_1); - userRepository.save(USER_1); - } - - @AfterEach - public void afterEach() { - DockerConfig.removeAllContainers(); - DockerConfig.removeAllNetworks(); - } - - @AfterAll - public static void afterAll() { - DockerConfig.removeAllContainers(); - DockerConfig.removeAllNetworks(); - } - - @Test - public void updatePassword_succeeds() throws BrokerUserCreationException { - final String USER_1_OLD_PASSWORD = "other"; - final CreateUserDto request = CreateUserDto.builder() - .password(USER_1_PASSWORD) - .tags("administrator") - .build(); - - /* mock */ - amqpConfig.addUser(USER_1_USERNAME, USER_1_OLD_PASSWORD, "administrator"); - amqpConfig.grantAccess(USER_1_USERNAME); - queueService.modifyUserPassword(USER_1, request); - - /* test */ - final AmqpUserBriefDto response2 = amqpConfig.whoami(USER_1_USERNAME, USER_1_PASSWORD); - assertEquals(USER_1_USERNAME, response2.getName()); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java deleted file mode 100644 index bf4653e348..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TimeSecretUnitTest.java +++ /dev/null @@ -1,60 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.User; -import at.tuwien.exception.SecretInvalidException; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.UserRepository; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; -import static org.mockito.Mockito.any; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class TimeSecretUnitTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private TimeSecretRepository timeSecretRepository; - - @MockBean - private UserRepository userRepository; - - @Autowired - private TimeSecretService timeSecretService; - - @Test - public void updateVerification_succeeds() throws SecretInvalidException { - - /* mock */ - when(timeSecretRepository.findByToken(TIME_SECRET_1_TOKEN)) - .thenReturn(Optional.of(TIME_SECRET_1)) - .thenReturn(Optional.empty()); - when(userRepository.save(any(User.class))) - .thenReturn(USER_1); - when(timeSecretRepository.save(any(TimeSecret.class))) - .thenReturn(TIME_SECRET_1); - - /* test */ - timeSecretService.invalidate(TIME_SECRET_1_TOKEN); - assertThrows(SecretInvalidException.class, () -> { - timeSecretService.invalidate(TIME_SECRET_1_TOKEN); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java deleted file mode 100644 index fb48095bf6..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/TokenIntegrationTest.java +++ /dev/null @@ -1,89 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.auth.JwtUtils; -import at.tuwien.config.H2Utils; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.Token; -import at.tuwien.exception.TokenNotEligableException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.repositories.UserRepository; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.BeforeEach; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import javax.servlet.ServletException; - - -import static org.junit.jupiter.api.Assertions.assertThrows; - -@Log4j2 -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class TokenIntegrationTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @Autowired - private TokenService tokenService; - - @Autowired - private UserRepository userRepository; - - @Autowired - private TokenRepository tokenRepository; - - @Autowired - private JwtUtils jwtUtils; - - @Autowired - private H2Utils h2Utils; - - @BeforeEach - public void beforeEach() { - userRepository.save(USER_1); - h2Utils.runScript("view.sql"); - } - - @Test - public void check_succeeds() throws ServletException, UserNotFoundException, TokenNotEligableException { - - /* mock */ - tokenService.create(USER_1_PRINCIPAL); - final Token token = tokenService.create(USER_1_PRINCIPAL); - - /* test */ - tokenService.check(token.getToken()); - } - - @Test - public void check_revoked_fails() throws UserNotFoundException, TokenNotEligableException { - - /* mock */ - final Token token = tokenService.create(USER_1_PRINCIPAL); - - /* test */ - assertThrows(ServletException.class, () -> { - tokenService.check(token.getToken()); - }); - } - - @Test - public void create_userNotFound_fails() { - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - tokenService.create(USER_2_PRINCIPAL); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java deleted file mode 100644 index c72d988189..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceIntegrationTest.java +++ /dev/null @@ -1,240 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.RoleTypeDto; -import at.tuwien.api.user.UserPasswordDto; -import at.tuwien.api.user.UserRolesDto; -import at.tuwien.config.AuthenticationConfig; -import at.tuwien.config.DockerConfig; -import at.tuwien.config.RabbitMqConfig; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.ImageRepository; -import at.tuwien.repositories.UserRepository; -import com.github.dockerjava.api.command.CreateContainerResponse; -import com.github.dockerjava.api.model.PortBinding; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.*; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.test.annotation.DirtiesContext; -import org.springframework.test.context.junit.jupiter.SpringExtension; -import org.springframework.web.client.RestTemplate; - -import java.util.List; - -import static at.tuwien.config.DockerConfig.*; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.when; - -@Log4j2 -@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD) -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class UserServiceIntegrationTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private AuthenticationConfig authenticationConfig; - - @Autowired - private UserService userService; - - /* keep */ - @Autowired - @Qualifier("junitRestTemplate") - private RestTemplate restTemplate; - - @Autowired - private ImageRepository imageRepository; - - @Autowired - private UserRepository userRepository; - - @Autowired - private RabbitMqConfig amqpConfig; - - @BeforeAll - public static void beforeAll() { - afterAll(); - DockerConfig.createAllNetworks(); - } - - @BeforeEach - public void beforeEach() { - afterEach(); - DockerConfig.createAllNetworks(); - /* metadata database */ - imageRepository.save(IMAGE_1); - userRepository.save(USER_1); - } - - @AfterEach - public void afterEach() { - DockerConfig.removeAllContainers(); - DockerConfig.removeAllNetworks(); - } - - @AfterAll - public static void afterAll() { - DockerConfig.removeAllContainers(); - DockerConfig.removeAllNetworks(); - } - - @Test - public void create_succeeds() throws UserNameExistsException, RoleNotFoundException, UserEmailExistsException, - InterruptedException { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_2_EMAIL) - .build(); - - /* mock */ - final CreateContainerResponse response1 = dockerClient.createContainerCmd(IMAGE_BROKER_IMAGE + ":" + IMAGE_BROKER_TAG) - .withHostConfig(hostConfig.withNetworkMode("fda-public").withPortBindings(PortBinding.parse("15672:15672"))) - .withName(CONTAINER_BROKER_NAME) - .withIpv4Address(CONTAINER_BROKER_IP) - .withHostName(CONTAINER_BROKER_INTERNAL_NAME) - .withVolumes() - .exec(); - CONTAINER_BROKER.setHash(response1.getId()); - startContainer(CONTAINER_BROKER); - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - final User response = userService.create(request); - assertEquals(USER_2_USERNAME, response.getUsername()); - assertEquals(USER_2_EMAIL, response.getEmail()); - } - - @Test - public void updatePassword_succeeds() throws UserNotFoundException, BrokerUserCreationException, - InterruptedException { - final String USER_1_OLD_PASSWORD = "other"; - final UserPasswordDto request = UserPasswordDto.builder() - .password(USER_1_PASSWORD) - .build(); - - /* mock */ - final CreateContainerResponse response1 = dockerClient.createContainerCmd(IMAGE_BROKER_IMAGE + ":" + IMAGE_BROKER_TAG) - .withHostConfig(hostConfig.withNetworkMode("fda-public").withPortBindings(PortBinding.parse("15672:15672"))) - .withName(CONTAINER_BROKER_NAME) - .withIpv4Address(CONTAINER_BROKER_IP) - .withHostName(CONTAINER_BROKER_INTERNAL_NAME) - .withVolumes() - .exec(); - CONTAINER_BROKER.setHash(response1.getId()); - startContainer(CONTAINER_BROKER); - amqpConfig.addUser(USER_1_USERNAME, USER_1_OLD_PASSWORD, "administrator"); - amqpConfig.grantAccess(USER_1_USERNAME); - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - final User response = userService.updatePassword(USER_1_ID, request); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - } - - @Test - public void create_isSuperUser_succeeds() throws UserNameExistsException, RoleNotFoundException, - UserEmailExistsException { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_2_EMAIL) - .build(); - - /* mock */ - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{USER_2_USERNAME}); - - /* test */ - final User response = userService.create(request); - assertEquals(USER_2_USERNAME, response.getUsername()); - assertEquals(USER_2_EMAIL, response.getEmail()); - assertEquals(List.of(RoleType.ROLE_RESEARCHER, RoleType.ROLE_DEVELOPER, RoleType.ROLE_DATA_STEWARD), response.getRoles()); - } - - @Test - public void create_noRole_succeeds() throws UserNameExistsException, RoleNotFoundException, - UserEmailExistsException { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_2_USERNAME) - .password(USER_2_PASSWORD) - .email(USER_2_EMAIL) - .build(); - - /* mock */ - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - final User response = userService.create(request); - assertEquals(USER_2_USERNAME, response.getUsername()); - assertEquals(USER_2_EMAIL, response.getEmail()); - assertEquals(List.of(), response.getRoles()); - } - - @Test - public void findByUsernameOrEmail_username_succeeds() throws UserNotFoundException { - - /* test */ - final User response = userService.findByUsernameOrEmail(USER_1_USERNAME, null); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - } - - @Test - public void findByUsernameOrEmail_email_succeeds() throws UserNotFoundException { - - /* test */ - final User response = userService.findByUsernameOrEmail(null, USER_1_EMAIL); - assertEquals(USER_1_ID, response.getId()); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - } - - @Test - public void findByUsernameOrEmail_fails() { - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - userService.findByUsernameOrEmail(USER_2_USERNAME, USER_2_EMAIL); - }); - } - - @Test - public void updateRoles_notUnique_fails() { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER, RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - assertThrows(DataIntegrityViolationException.class, () -> { - userService.updateRoles(USER_1_ID, request); - }); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java b/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java deleted file mode 100644 index 96165079e4..0000000000 --- a/fda-authentication-service/rest-service/src/test/java/at/tuwien/service/UserServiceUnitTest.java +++ /dev/null @@ -1,257 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.BaseUnitTest; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.RoleTypeDto; -import at.tuwien.api.user.UserRolesDto; -import at.tuwien.config.AuthenticationConfig; -import at.tuwien.config.ReadyConfig; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.impl.UserServiceImpl; -import lombok.extern.log4j.Log4j2; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.mock.mockito.MockBean; -import org.springframework.test.context.junit.jupiter.SpringExtension; - -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.*; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.Mockito.when; - -@Log4j2 -@SpringBootTest -@ExtendWith(SpringExtension.class) -public class UserServiceUnitTest extends BaseUnitTest { - - @MockBean - private ReadyConfig readyConfig; - - @MockBean - private AuthenticationConfig authenticationConfig; - - @MockBean - private UserRepository userRepository; - - @Autowired - private UserServiceImpl userService; - - @Test - public void create_isNotSuperUser_succeeds() throws UserNameExistsException, RoleNotFoundException, - UserEmailExistsException { - - /* mock */ - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - final User response = create_generic(false, false); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - assertEquals(List.of(RoleType.ROLE_RESEARCHER), response.getRoles()); - } - - @Test - public void create_emailExists_fails() { - - /* mock */ - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - assertThrows(UserEmailExistsException.class, () -> { - create_generic(false, true); - }); - } - - @Test - public void create_usernameExists_fails() { - - /* mock */ - when(authenticationConfig.getDefaultRoles()) - .thenReturn(new RoleType[]{RoleType.ROLE_RESEARCHER}); - when(authenticationConfig.getSuperUsers()) - .thenReturn(new String[]{}); - - /* test */ - assertThrows(UserNameExistsException.class, () -> { - create_generic(true, false); - }); - } - - @Test - public void updateRoles_idempotent_succeeds() throws UserNotFoundException, RoleUniqueException, - RoleNotFoundException { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* test */ - final User response = updateRoles_generic(USER_1_ID, USER_1, request); - assertEquals(USER_1_USERNAME, response.getUsername()); - assertEquals(USER_1_EMAIL, response.getEmail()); - assertEquals(List.of(RoleType.ROLE_RESEARCHER), response.getRoles()); - } - - @Test - public void updateRoles_addResearcherRole_succeeds() throws UserNotFoundException, RoleUniqueException, - RoleNotFoundException { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER)) - .build(); - - /* mock */ - - /* test */ - final User response = updateRoles_generic(USER_3_ID, USER_3, request); - assertEquals(USER_3_USERNAME, response.getUsername()); - assertEquals(USER_3_EMAIL, response.getEmail()); - assertEquals(List.of(RoleType.ROLE_RESEARCHER), response.getRoles()); - } - - @Test - public void updateRoles_addMoreRoles_succeeds() throws UserNotFoundException, RoleUniqueException, - RoleNotFoundException { - final UserRolesDto request = UserRolesDto.builder() - .roles(List.of(RoleTypeDto.ROLE_RESEARCHER, RoleTypeDto.ROLE_DEVELOPER)) - .build(); - - /* mock */ - userRepository.save(USER_1); - userRepository.save(USER_2); - userRepository.save(USER_3); - - /* test */ - final User response = updateRoles_generic(USER_3_ID, USER_3, request); - assertEquals(USER_3_USERNAME, response.getUsername()); - assertEquals(USER_3_EMAIL, response.getEmail()); - assertEquals(List.of(RoleType.ROLE_RESEARCHER, RoleType.ROLE_DEVELOPER), response.getRoles()); - } - - @Test - public void findAll_succeeds() { - - /* mock */ - when(userRepository.findAll()) - .thenReturn(List.of(USER_1, USER_2, USER_3)); - - /* test */ - final List<User> response = userService.findAll(); - assertEquals(USER_1, response.get(0)); - assertEquals(USER_2, response.get(1)); - assertEquals(USER_3, response.get(2)); - } - - @Test - public void find_succeeds() throws UserNotFoundException { - - /* mock */ - when(userRepository.findById(USER_1_ID)) - .thenReturn(Optional.of(USER_1)); - - /* test */ - final User response = userService.find(USER_1_ID); - assertEquals(USER_1, response); - } - - @Test - public void find_fails() { - - /* mock */ - when(userRepository.findById(USER_2_ID)) - .thenReturn(Optional.empty()); - - /* test */ - assertThrows(UserNotFoundException.class, () -> { - userService.find(USER_2_ID); - }); - } - - @Test - public void validateOrcid_null_succeeds() { - - /* test */ - final boolean response = userService.validateOrcid(null); - assertTrue(response); - } - - @Test - public void validateOrcid_short_fails() { - - /* test */ - final boolean response = userService.validateOrcid("ABC"); - assertFalse(response); - } - - @Test - public void validateOrcid_containsX_succeeds() { - - /* test */ - final boolean response = userService.validateOrcid("0000-0003-4216-302X"); - assertTrue(response); - } - - /* ################################################################################################### */ - /* ## GENERIC TEST CASES ## */ - /* ################################################################################################### */ - - protected User create_generic(boolean usernameExists, boolean emailExists) throws UserNameExistsException, - RoleNotFoundException, UserEmailExistsException { - final SignupRequestDto request = SignupRequestDto.builder() - .username(USER_1_USERNAME) - .password(USER_1_PASSWORD) - .email(USER_1_EMAIL) - .build(); - - /* mock */ - if (usernameExists) { - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.of(USER_1)); - } else { - when(userRepository.findByUsername(USER_1_USERNAME)) - .thenReturn(Optional.empty()); - } - if (emailExists) { - when(userRepository.findByEmail(USER_1_EMAIL)) - .thenReturn(Optional.of(USER_1)); - } else { - when(userRepository.findByEmail(USER_1_EMAIL)) - .thenReturn(Optional.empty()); - } - when(userRepository.save(any(User.class))) - .thenReturn(USER_1); - - /* test */ - return userService.create(request); - } - - protected User updateRoles_generic(Long userId, User user, UserRolesDto data) throws RoleNotFoundException, - UserNotFoundException, RoleUniqueException { - - /* mock */ - if (user != null) { - when(userRepository.findById(userId)) - .thenReturn(Optional.of(user)); - } else { - when(userRepository.findById(userId)) - .thenReturn(Optional.empty()); - } - when(userRepository.save(any(User.class))) - .thenReturn(user); - - /* test */ - return userService.updateRoles(userId, data); - } - -} diff --git a/fda-authentication-service/rest-service/src/test/resources/application.properties b/fda-authentication-service/rest-service/src/test/resources/application.properties deleted file mode 100644 index d57104b1ea..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/application.properties +++ /dev/null @@ -1,30 +0,0 @@ -# enable local spring profile -spring.profiles.active=local - -# disable discovery -spring.cloud.discovery.enabled = false - -# disable cloud config and config discovery -spring.cloud.config.discovery.enabled = false -spring.cloud.config.enabled = false - -# disable datasource -spring.datasource.url=jdbc:h2:mem:testdb;DATABASE_TO_UPPER=false;DB_CLOSE_ON_EXIT=FALSE;INIT=runscript from './src/test/resources/schema.sql' -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.username=sa -spring.datasource.password=password -spring.jpa.database-platform=org.hibernate.dialect.H2Dialect -spring.jpa.hibernate.ddl-auto=create-drop -spring.jpa.show-sql=false - -# disable mail health check -management.health.mail.enabled=false - -# rabbitmq -spring.rabbitmq.username=guest -spring.rabbitmq.password=guest - -# user -fda.gateway.endpoint=http://172.31.0.2:15672/api/ -fda.mail.verify=true -fda.token.max=1 \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/test/resources/schema.sql b/fda-authentication-service/rest-service/src/test/resources/schema.sql deleted file mode 100644 index a668576dc0..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/schema.sql +++ /dev/null @@ -1,20 +0,0 @@ -CREATE SCHEMA IF NOT EXISTS `fda`; -SET SCHEMA `fda`; -DROP TABLE IF EXISTS fda.mdb_concepts; -CREATE TABLE fda.mdb_concepts -( - uri VARCHAR(500) not null, - name VARCHAR(255), - created timestamp NOT NULL DEFAULT NOW(), - created_by bigint, - PRIMARY KEY (uri) -); -DROP TABLE IF EXISTS fda.mdb_units; -CREATE TABLE fda.mdb_units -( - uri VARCHAR(500) not null, - name VARCHAR(255), - created timestamp NOT NULL DEFAULT NOW(), - created_by bigint, - PRIMARY KEY (uri) -); \ No newline at end of file diff --git a/fda-authentication-service/rest-service/src/test/resources/templates/mail-password-changed.txt b/fda-authentication-service/rest-service/src/test/resources/templates/mail-password-changed.txt deleted file mode 100644 index 8115274478..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/templates/mail-password-changed.txt +++ /dev/null @@ -1,13 +0,0 @@ -Dear [[${username}]], - -Your password was successfully changed! - -In case this wasn't you, please contact us as soon as possible by replying to this mail! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/test/resources/templates/mail-request-password-reset.txt b/fda-authentication-service/rest-service/src/test/resources/templates/mail-request-password-reset.txt deleted file mode 100644 index 32670c5549..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/templates/mail-request-password-reset.txt +++ /dev/null @@ -1,13 +0,0 @@ -Dear [[${username}]], - -You can reset your password using the link below: - -[[${website}]]/reset?token=[[${token}]] - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/test/resources/templates/mail-verify-email.txt b/fda-authentication-service/rest-service/src/test/resources/templates/mail-verify-email.txt deleted file mode 100644 index d028698e04..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/templates/mail-verify-email.txt +++ /dev/null @@ -1,15 +0,0 @@ -Dear [[${username}]], - -Now please verify your e-mail address by clicking this link: - -[[${website}]]/api/user/token?token=[[${token}]] - -We are looking forward to your data in the repository! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/test/resources/templates/mail-welcome.txt b/fda-authentication-service/rest-service/src/test/resources/templates/mail-welcome.txt deleted file mode 100644 index 56c8369d22..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/templates/mail-welcome.txt +++ /dev/null @@ -1,17 +0,0 @@ -Dear [[${username}]], - -You have successfully created an account on the demo instance of DBRepo, a joint deployment of the TU Vienna and the University of Vienna as part of the FAIR Data Austria project. Now please verify your e-mail address by clicking this link: - -[[${website}]]/api/user/secret?token=[[${token}]] - -Please note that we reserve the right to make changes and new deployments to this demo instance. Your data will not be saved in such a case, and you may need to create a new account. If you want to receive updates on DBRepo, please subscribe to our mailing list: https://lists.univie.ac.at/mailman/listinfo/fairdata_dbrepo - -We are looking forward to your test data in the repository! - -Kind regards, -DBRepo - --------- - -[[${website}]]/ -https://dbrepo-docs.ossdip.at/ diff --git a/fda-authentication-service/rest-service/src/test/resources/view.sql b/fda-authentication-service/rest-service/src/test/resources/view.sql deleted file mode 100644 index b23c5436c0..0000000000 --- a/fda-authentication-service/rest-service/src/test/resources/view.sql +++ /dev/null @@ -1,5 +0,0 @@ --- Modified for H2 --- Assume id=1 is invalid --- Assume id=2 is still valid token -CREATE VIEW IF NOT EXISTS fda.mdb_invalid_tokens AS -(SELECT `id`, `token_hash`, `creator`, `created`, `expires`, `last_used` FROM fda.`mdb_tokens` WHERE `id` = 1); \ No newline at end of file diff --git a/fda-authentication-service/service_ready b/fda-authentication-service/service_ready deleted file mode 100644 index b2e4f9df68..0000000000 --- a/fda-authentication-service/service_ready +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -if [ -f /ready ]; then - echo "service is ready and accepting connections" - exit 0 -fi -exit 1 \ No newline at end of file diff --git a/fda-authentication-service/services/pom.xml b/fda-authentication-service/services/pom.xml deleted file mode 100644 index afe6b5ca0b..0000000000 --- a/fda-authentication-service/services/pom.xml +++ /dev/null @@ -1,42 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - <parent> - <artifactId>fda-authentication-service</artifactId> - <groupId>at.tuwien</groupId> - <version>1.1.0-alpha</version> - </parent> - - <artifactId>services</artifactId> - <version>1.1.0-alpha</version> - <name>fda-authentication-service-services</name> - - <build> - <plugins> - <plugin> - <groupId>org.apache.maven.plugins</groupId> - <artifactId>maven-compiler-plugin</artifactId> - <configuration> - <source>${java.version}</source> - <target>${java.version}</target> - <annotationProcessorPaths> - <path> - <groupId>org.projectlombok</groupId> - <artifactId>lombok</artifactId> - <version>${lombok.version}</version> - </path> - <!-- keep this order https://stackoverflow.com/questions/47676369/mapstruct-and-lombok-not-working-together#answer-65021876 --> - <path> - <groupId>org.mapstruct</groupId> - <artifactId>mapstruct-processor</artifactId> - <version>${mapstruct.version}</version> - </path> - </annotationProcessorPaths> - </configuration> - </plugin> - </plugins> - </build> - -</project> \ No newline at end of file diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthEntrypoint.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthEntrypoint.java deleted file mode 100644 index 16120259d0..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthEntrypoint.java +++ /dev/null @@ -1,23 +0,0 @@ -package at.tuwien.auth; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.core.AuthenticationException; -import org.springframework.security.web.AuthenticationEntryPoint; -import org.springframework.stereotype.Component; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@Slf4j -@Component -public class AuthEntrypoint implements AuthenticationEntryPoint { - - @Override - public void commence(HttpServletRequest request, HttpServletResponse response, - AuthenticationException authException) throws IOException { - log.error("Unauthorized error: {}", authException.getMessage()); - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Error: Unauthorized"); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java deleted file mode 100644 index 0d23f8f317..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/AuthTokenFilter.java +++ /dev/null @@ -1,70 +0,0 @@ -package at.tuwien.auth; - -import at.tuwien.service.TokenService; -import at.tuwien.service.impl.UserDetailsServiceImpl; -import lombok.extern.slf4j.Slf4j; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.security.web.authentication.WebAuthenticationDetailsSource; -import org.springframework.util.StringUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -@Slf4j -public class AuthTokenFilter extends OncePerRequestFilter { - - private final JwtUtils jwtUtils; - private final TokenService tokenService; - private final UserDetailsServiceImpl userDetailsService; - - public AuthTokenFilter(JwtUtils jwtUtils, TokenService tokenService, UserDetailsServiceImpl userDetailsService) { - this.jwtUtils = jwtUtils; - this.tokenService = tokenService; - this.userDetailsService = userDetailsService; - } - - @Override - protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) - throws ServletException, IOException { - final String jwt = parseJwt(request); - if (jwt != null && jwtUtils.validateJwtToken(jwt)) { - tokenService.check(jwt); - final String username = jwtUtils.getUserNameFromJwtToken(jwt); - final UserDetails userDetails; - try { - userDetails = userDetailsService.loadUserByUsername(username); - } catch (UsernameNotFoundException e) { - /* ignore */ - response.setStatus(401); - return; - } - final UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken( - userDetails, null, userDetails.getAuthorities()); - authentication.setDetails(new WebAuthenticationDetailsSource().buildDetails(request)); - - SecurityContextHolder.getContext().setAuthentication(authentication); - } - filterChain.doFilter(request, response); - } - - /** - * Parses the token from the HTTP header of the request - * - * @param request The request. - * @return The token. - */ - protected String parseJwt(HttpServletRequest request) { - String headerAuth = request.getHeader("Authorization"); - if (StringUtils.hasText(headerAuth) && headerAuth.startsWith("Bearer ")) { - return headerAuth.substring(7, headerAuth.length()); - } - return null; - } -} \ No newline at end of file diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java deleted file mode 100644 index b4ce3b84be..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/JwtUtils.java +++ /dev/null @@ -1,58 +0,0 @@ -package at.tuwien.auth; - -import com.auth0.jwt.JWT; -import com.auth0.jwt.algorithms.Algorithm; -import com.auth0.jwt.exceptions.JWTDecodeException; -import com.auth0.jwt.interfaces.DecodedJWT; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.stereotype.Component; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Date; - -@Slf4j -@Component -public class JwtUtils { - - @Value("${jwt.secret}") - private String secret; - - @Value("${jwt.expiration.ms}") - private Integer expire; - - public String generateJwtToken(String username, Instant expire) { - final Algorithm algorithm = Algorithm.HMAC512(secret); - return JWT.create() - .withSubject(username) - .withIssuedAt(new Date()) - .withExpiresAt(Date.from(expire)) - .withClaim("rnd", Instant.now().getNano()) /* increase hash entropy */ - .sign(algorithm); - } - - public String generateJwtToken(String username) { - return generateJwtToken(username, Instant.now().plus(expire, ChronoUnit.MILLIS)); - } - - public String getUserNameFromJwtToken(String token) { - return JWT.decode(token) - .getSubject(); - } - - public static String toHash(String token) { - return DigestUtils.sha256Hex(token); - } - - public boolean validateJwtToken(String authToken) { - try { - final DecodedJWT jwt = JWT.decode(authToken); - return jwt.getExpiresAt().after(new Date(Instant.now().getEpochSecond())); - } catch (JWTDecodeException e) { - log.error("Invalid JWT signature: {}", e.getMessage()); - } - return false; - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/MariaDbPassword.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/MariaDbPassword.java deleted file mode 100644 index 26a2f4b459..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/MariaDbPassword.java +++ /dev/null @@ -1,24 +0,0 @@ -package at.tuwien.auth; - -import org.apache.commons.codec.digest.DigestUtils; - -import javax.xml.bind.DatatypeConverter; - -public class MariaDbPassword { - - /** - * Encodes a plain password to the MariaDB cipher text, is equivalent to the MySQL function - * <p> - * <code>SELECT CONCAT('*', UPPER(SHA1(UNHEX(SHA1('the_password')))))</code> - * <p> - * Source: <a href="https://stackoverflow.com/questions/50914611/how-to-create-password-hash-for-mysql-externally">https://stackoverflow.com/questions/50914611/how-to-create-password-hash-for-mysql-externally</a> - * - * @param plain The plain password - * @return The password cipher for MariaDB 10.5 - */ - public static String encode(String plain) { - return "*" + DigestUtils.sha1Hex(DatatypeConverter.parseHexBinary(DigestUtils.sha1Hex(plain))) - .toUpperCase(); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/auth/UserPermissionEvaluator.java b/fda-authentication-service/services/src/main/java/at/tuwien/auth/UserPermissionEvaluator.java deleted file mode 100644 index a2b0ea5364..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/auth/UserPermissionEvaluator.java +++ /dev/null @@ -1,50 +0,0 @@ -package at.tuwien.auth; - -import at.tuwien.entities.user.User; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.service.UserService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.access.PermissionEvaluator; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.stereotype.Component; - -import java.io.Serializable; - -@Slf4j -@Component -public class UserPermissionEvaluator implements PermissionEvaluator { - - private final UserService userService; - private final UserDetailsService userDetailsService; - - @Autowired - public UserPermissionEvaluator(UserService userService, UserDetailsService userDetailsService) { - this.userService = userService; - this.userDetailsService = userDetailsService; - } - - @Override - public boolean hasPermission(Authentication auth, Object targetDomainObject, Object permission) { - if (auth == null || !(targetDomainObject instanceof Long) || !(permission instanceof String)) { - return false; - } - final UserDetails caller = userDetailsService.loadUserByUsername(auth.getName()); - final Long targetDomainId = (Long) targetDomainObject; - final User domainObject; - try { - domainObject = userService.find(targetDomainId); - } catch (UserNotFoundException e) { - log.error("User with id {} was not found", targetDomainId); - return false; - } - return caller.getUsername().equals(domainObject.getUsername()); - } - - @Override - public boolean hasPermission(Authentication auth, Serializable targetId, String targetType, Object permission) { - return false; - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/AmqpConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/AmqpConfig.java deleted file mode 100644 index e6078c9cfd..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/AmqpConfig.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.config; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; - -@Getter -@Configuration -public class AmqpConfig { - - @Value("${spring.rabbitmq.username}") - private String amqpUsername; - - @Value("${spring.rabbitmq.password}") - private String amqpPassword; - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/AuthenticationConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/AuthenticationConfig.java deleted file mode 100644 index 733abd0077..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/AuthenticationConfig.java +++ /dev/null @@ -1,21 +0,0 @@ -package at.tuwien.config; - -import at.tuwien.entities.user.RoleType; -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Configuration; - -@Getter -@Configuration -public class AuthenticationConfig { - - @Value("${fda.token.max}") - private Integer tokenCount; - - @Value("${fda.default_roles}") - private RoleType[] defaultRoles; - - @Value("${fda.superusers}") - private String[] superUsers; - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/GatewayConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/GatewayConfig.java deleted file mode 100644 index 9edae043cf..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/GatewayConfig.java +++ /dev/null @@ -1,27 +0,0 @@ -package at.tuwien.config; - -import lombok.Getter; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.client.RestTemplate; -import org.springframework.web.util.DefaultUriBuilderFactory; - -@Log4j2 -@Getter -@Configuration -public class GatewayConfig { - - @Value("${fda.gateway.endpoint}") - private String gatewayEndpoint; - - @Bean("gatewayRestTemplate") - public RestTemplate restTemplate() { - final RestTemplate restTemplate = new RestTemplate(); - restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(gatewayEndpoint)); - log.debug("gateway rest template with endpoint={}", gatewayEndpoint); - return restTemplate; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/JacksonConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/JacksonConfig.java deleted file mode 100644 index fba7f99cf2..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/JacksonConfig.java +++ /dev/null @@ -1,31 +0,0 @@ -package at.tuwien.config; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.databind.SerializationFeature; -import com.fasterxml.jackson.datatype.jdk8.Jdk8Module; -import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; -import lombok.extern.slf4j.Slf4j; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; - -import java.util.Date; -import java.util.TimeZone; - -@Slf4j -@Configuration -public class JacksonConfig { - - @Bean - public ObjectMapper objectMapper() throws JsonProcessingException { - final ObjectMapper objectMapper = new ObjectMapper(); - objectMapper.findAndRegisterModules(); - objectMapper.registerModule(new Jdk8Module()); - objectMapper.registerModule(new JavaTimeModule()); - objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); - objectMapper.setTimeZone(TimeZone.getTimeZone("UTC")); - log.debug("current time is {}", objectMapper.writeValueAsString(new Date())); - return objectMapper; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/MailConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/MailConfig.java deleted file mode 100644 index 150b38ba43..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/MailConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package at.tuwien.config; - -import lombok.Getter; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.thymeleaf.spring5.SpringTemplateEngine; -import org.thymeleaf.templatemode.TemplateMode; -import org.thymeleaf.templateresolver.ClassLoaderTemplateResolver; - -import java.nio.charset.StandardCharsets; - -@Log4j2 -@Getter -@Configuration -public class MailConfig { - - @Value("${fda.mail.from}") - private String mailFrom; - - @Value("${fda.mail.replyto}") - private String mailReplyTo; - - @Value("${fda.mail.prefix}") - private String mailPrefix; - - @Value("${spring.mail.username}") - private String mailUsername; - - @Value("${fda.mail.verify}") - private Boolean mailVerify; - - @Value("${fda.website}") - private String website; - - @Bean - public SpringTemplateEngine springTemplateEngine() { - final SpringTemplateEngine springTemplateEngine = new SpringTemplateEngine(); - springTemplateEngine.addTemplateResolver(emailTemplateResolver()); - return springTemplateEngine; - } - - private ClassLoaderTemplateResolver emailTemplateResolver() { - final ClassLoaderTemplateResolver emailTemplateResolver = new ClassLoaderTemplateResolver(); - emailTemplateResolver.setPrefix(mailPrefix + "templates/"); - emailTemplateResolver.setSuffix(".txt"); - emailTemplateResolver.setTemplateMode(TemplateMode.TEXT); - emailTemplateResolver.setCharacterEncoding(StandardCharsets.UTF_8.name()); - emailTemplateResolver.setCacheable(false); - return emailTemplateResolver; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/SecurityConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/SecurityConfig.java deleted file mode 100644 index ade1184dd6..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/SecurityConfig.java +++ /dev/null @@ -1,25 +0,0 @@ -package at.tuwien.config; - -import lombok.Getter; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; -import org.springframework.security.crypto.password.PasswordEncoder; - -import lombok.extern.log4j.Log4j2; - -@Log4j2 -@Getter -@Configuration -public class SecurityConfig { - - @Value("${fda.website}") - private String website; - - @Bean - public PasswordEncoder passwordEncoder() { - return new BCryptPasswordEncoder(); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java b/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java deleted file mode 100644 index 56851d0743..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/config/WebSecurityConfig.java +++ /dev/null @@ -1,123 +0,0 @@ -package at.tuwien.config; - -import at.tuwien.auth.AuthTokenFilter; -import at.tuwien.auth.JwtUtils; -import at.tuwien.service.TokenService; -import at.tuwien.service.impl.UserDetailsServiceImpl; -import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; -import io.swagger.v3.oas.annotations.security.SecurityScheme; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.http.HttpMethod; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; -import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; -import org.springframework.security.config.annotation.web.builders.HttpSecurity; -import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; -import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; -import org.springframework.security.config.http.SessionCreationPolicy; -import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter; -import org.springframework.web.cors.CorsConfiguration; -import org.springframework.web.cors.UrlBasedCorsConfigurationSource; -import org.springframework.web.filter.CorsFilter; - -import javax.servlet.http.HttpServletResponse; - -@Configuration -@EnableWebSecurity -@EnableGlobalMethodSecurity(prePostEnabled = true) -@SecurityScheme( - name = "bearerAuth", - type = SecuritySchemeType.HTTP, - bearerFormat = "JWT", - scheme = "bearer" -) -public class WebSecurityConfig extends WebSecurityConfigurerAdapter { - - private final JwtUtils jwtUtils; - private final TokenService tokenService; - private final SecurityConfig securityConfig; - private final UserDetailsServiceImpl userDetailsService; - - @Autowired - public WebSecurityConfig(JwtUtils jwtUtils, TokenService tokenService, SecurityConfig securityConfig, - UserDetailsServiceImpl userDetailsService) { - this.jwtUtils = jwtUtils; - this.tokenService = tokenService; - this.securityConfig = securityConfig; - this.userDetailsService = userDetailsService; - } - - @Bean - public AuthTokenFilter authTokenFilter() { - return new AuthTokenFilter(jwtUtils, tokenService, userDetailsService); - } - - @Override - public void configure(AuthenticationManagerBuilder authenticationManagerBuilder) throws Exception { - authenticationManagerBuilder.userDetailsService(userDetailsService) - .passwordEncoder(securityConfig.passwordEncoder()); - } - - @Bean - @Override - public AuthenticationManager authenticationManagerBean() throws Exception { - return super.authenticationManagerBean(); - } - - @Override - protected void configure(HttpSecurity http) throws Exception { - /* enable CORS and disable CSRF */ - http = http.cors().and().csrf().disable(); - /* set session management to stateless */ - http = http - .sessionManagement() - .sessionCreationPolicy(SessionCreationPolicy.STATELESS) - .and(); - /* set unauthorized requests exception handler */ - http = http - .exceptionHandling() - .authenticationEntryPoint( - (request, response, ex) -> { - response.sendError(HttpServletResponse.SC_UNAUTHORIZED, - ex.getMessage() - ); - } - ).and(); - /* set permissions on endpoints */ - http.authorizeRequests() - /* our internal endpoints */ - .antMatchers(HttpMethod.GET, "/actuator/prometheus/**").permitAll() - /* our public endpoints */ - .antMatchers(HttpMethod.GET, "/api/user/secret").permitAll() - .antMatchers(HttpMethod.GET, "/api/user/secret/resend").permitAll() - .antMatchers(HttpMethod.POST, "/api/user").permitAll() - .antMatchers(HttpMethod.GET, "/api/user").permitAll() - .antMatchers(HttpMethod.PUT, "/api/user").permitAll() - .antMatchers(HttpMethod.PUT, "/api/user/reset").permitAll() - .antMatchers(HttpMethod.POST, "/api/auth").permitAll() - .antMatchers("/v3/api-docs.yaml", - "/v3/api-docs/**", - "/swagger-ui/**", - "/swagger-ui.html").permitAll() - /* our private endpoints */ - .anyRequest().authenticated(); - /* add JWT token filter */ - http.addFilterBefore(authTokenFilter(), - UsernamePasswordAuthenticationFilter.class - ); - } - - @Bean - public CorsFilter corsFilter() { - final UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); - final CorsConfiguration config = new CorsConfiguration(); - config.setAllowCredentials(true); - config.addAllowedOrigin("*"); - config.addAllowedHeader("*"); - config.addAllowedMethod("*"); - source.registerCorsConfiguration("/**", config); - return new CorsFilter(source); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationInvalidException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationInvalidException.java deleted file mode 100644 index a485cff74f..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationInvalidException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.FORBIDDEN) -public class AuthenticationInvalidException extends Exception { - - public AuthenticationInvalidException(String msg) { - super(msg); - } - - public AuthenticationInvalidException(String msg, Throwable thr) { - super(msg, thr); - } - - public AuthenticationInvalidException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationMalformedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationMalformedException.java deleted file mode 100644 index d6068f9457..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/AuthenticationMalformedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.BAD_REQUEST) -public class AuthenticationMalformedException extends Exception { - - public AuthenticationMalformedException(String msg) { - super(msg); - } - - public AuthenticationMalformedException(String msg, Throwable thr) { - super(msg, thr); - } - - public AuthenticationMalformedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/BrokerUserCreationException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/BrokerUserCreationException.java deleted file mode 100644 index 3aaa811b70..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/BrokerUserCreationException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_ACCEPTABLE) -public class BrokerUserCreationException extends Exception { - - public BrokerUserCreationException(String msg) { - super(msg); - } - - public BrokerUserCreationException(String msg, Throwable thr) { - super(msg, thr); - } - - public BrokerUserCreationException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java deleted file mode 100644 index 44c3d430f9..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/NotAllowedException.java +++ /dev/null @@ -1,21 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.METHOD_NOT_ALLOWED) -public class NotAllowedException extends Exception { - - public NotAllowedException(String msg) { - super(msg); - } - - public NotAllowedException(String msg, Throwable thr) { - super(msg, thr); - } - - public NotAllowedException(Throwable thr) { - super(thr); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/OrcidMalformedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/OrcidMalformedException.java deleted file mode 100644 index 29c85b4914..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/OrcidMalformedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.BAD_REQUEST) -public class OrcidMalformedException extends Exception { - - public OrcidMalformedException(String msg) { - super(msg); - } - - public OrcidMalformedException(String msg, Throwable thr) { - super(msg, thr); - } - - public OrcidMalformedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleNotFoundException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleNotFoundException.java deleted file mode 100644 index 7453900c2f..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleNotFoundException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_FOUND) -public class RoleNotFoundException extends Exception { - - public RoleNotFoundException(String msg) { - super(msg); - } - - public RoleNotFoundException(String msg, Throwable thr) { - super(msg, thr); - } - - public RoleNotFoundException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleUniqueException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleUniqueException.java deleted file mode 100644 index 36b75c1ba8..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/RoleUniqueException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.BAD_REQUEST) -public class RoleUniqueException extends Exception { - - public RoleUniqueException(String msg) { - super(msg); - } - - public RoleUniqueException(String msg, Throwable thr) { - super(msg, thr); - } - - public RoleUniqueException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/SecretInvalidException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/SecretInvalidException.java deleted file mode 100644 index cff7cbd321..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/SecretInvalidException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.EXPECTATION_FAILED) -public class SecretInvalidException extends Exception { - - public SecretInvalidException(String msg) { - super(msg); - } - - public SecretInvalidException(String msg, Throwable thr) { - super(msg, thr); - } - - public SecretInvalidException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotEligableException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotEligableException.java deleted file mode 100644 index fef3797aad..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotEligableException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.EXPECTATION_FAILED) -public class TokenNotEligableException extends Exception { - - public TokenNotEligableException(String msg) { - super(msg); - } - - public TokenNotEligableException(String msg, Throwable thr) { - super(msg, thr); - } - - public TokenNotEligableException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotFoundException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotFoundException.java deleted file mode 100644 index a376306d77..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenNotFoundException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_FOUND) -public class TokenNotFoundException extends Exception { - - public TokenNotFoundException(String msg) { - super(msg); - } - - public TokenNotFoundException(String msg, Throwable thr) { - super(msg, thr); - } - - public TokenNotFoundException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenRevokedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenRevokedException.java deleted file mode 100644 index 09b7b3c0b0..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/TokenRevokedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.UNAUTHORIZED) -public class TokenRevokedException extends Exception { - - public TokenRevokedException(String msg) { - super(msg); - } - - public TokenRevokedException(String msg, Throwable thr) { - super(msg, thr); - } - - public TokenRevokedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailAlreadyVerifiedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailAlreadyVerifiedException.java deleted file mode 100644 index 18bd71a999..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailAlreadyVerifiedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.FAILED_DEPENDENCY) -public class UserEmailAlreadyVerifiedException extends Exception { - - public UserEmailAlreadyVerifiedException(String msg) { - super(msg); - } - - public UserEmailAlreadyVerifiedException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserEmailAlreadyVerifiedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailExistsException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailExistsException.java deleted file mode 100644 index 6983cbab78..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailExistsException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.EXPECTATION_FAILED) -public class UserEmailExistsException extends Exception { - - public UserEmailExistsException(String msg) { - super(msg); - } - - public UserEmailExistsException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserEmailExistsException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailFailedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailFailedException.java deleted file mode 100644 index 4dfd543caf..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailFailedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.PRECONDITION_REQUIRED) -public class UserEmailFailedException extends Exception { - - public UserEmailFailedException(String msg) { - super(msg); - } - - public UserEmailFailedException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserEmailFailedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailNotVerifiedException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailNotVerifiedException.java deleted file mode 100644 index 412a0fe066..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserEmailNotVerifiedException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.I_AM_A_TEAPOT) -public class UserEmailNotVerifiedException extends Exception { - - public UserEmailNotVerifiedException(String msg) { - super(msg); - } - - public UserEmailNotVerifiedException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserEmailNotVerifiedException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNameExistsException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNameExistsException.java deleted file mode 100644 index ea7be028e3..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNameExistsException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.CONFLICT) -public class UserNameExistsException extends Exception { - - public UserNameExistsException(String msg) { - super(msg); - } - - public UserNameExistsException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserNameExistsException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java b/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java deleted file mode 100644 index 6af10ae8da..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/exception/UserNotFoundException.java +++ /dev/null @@ -1,20 +0,0 @@ -package at.tuwien.exception; - -import org.springframework.http.HttpStatus; -import org.springframework.web.bind.annotation.ResponseStatus; - -@ResponseStatus(code = HttpStatus.NOT_FOUND) -public class UserNotFoundException extends Exception { - - public UserNotFoundException(String msg) { - super(msg); - } - - public UserNotFoundException(String msg, Throwable thr) { - super(msg, thr); - } - - public UserNotFoundException(Throwable thr) { - super(thr); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java b/fda-authentication-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java deleted file mode 100644 index b2404f5e75..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/gateway/BrokerServiceGateway.java +++ /dev/null @@ -1,40 +0,0 @@ -package at.tuwien.gateway; - - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.exception.BrokerUserCreationException; - - -public interface BrokerServiceGateway { - - /** - * Creates a user at the Broker Service. - * - * @param username The user name. - * @param data The user data. - * @throws BrokerUserCreationException The broker did not create a user. - */ - void createUser(String username, CreateUserDto data) throws BrokerUserCreationException; - - UserDetailsDto findUser(String username) throws BrokerUserCreationException; - - /** - * Modified host permissions - * - * @param username The user name. - * @param data The user data. - * @throws BrokerUserCreationException The broker did not modify the user. - */ - void modifyHostPermissions(String username, GrantVirtualHostPermissionsDto data) throws BrokerUserCreationException; - - /** - * Modify a user password for a user at the Queue Service - * - * @param username The user name. - * @param data The user modification data. - * @throws BrokerUserCreationException The broker did not modify a user. - */ - void modifyUserPassword(String username, CreateUserDto data) throws BrokerUserCreationException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java deleted file mode 100644 index 1c60c24afb..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/gateway/impl/BrokerServiceGatewayImpl.java +++ /dev/null @@ -1,109 +0,0 @@ -package at.tuwien.gateway.impl; - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.config.AmqpConfig; -import at.tuwien.config.GatewayConfig; -import at.tuwien.exception.BrokerUserCreationException; -import at.tuwien.gateway.BrokerServiceGateway; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.codec.binary.Base64; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.http.*; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; - -import java.net.URI; -import java.nio.charset.Charset; - -@Slf4j -@Service -public class BrokerServiceGatewayImpl implements BrokerServiceGateway { - - private final AmqpConfig amqpConfig; - private final RestTemplate restTemplate; - private final GatewayConfig gatewayConfig; - - @Autowired - public BrokerServiceGatewayImpl(@Qualifier("gatewayRestTemplate") RestTemplate restTemplate, AmqpConfig amqpConfig, - GatewayConfig gatewayConfig) { - this.amqpConfig = amqpConfig; - this.restTemplate = restTemplate; - this.gatewayConfig = gatewayConfig; - } - - @Override - public void createUser(String username, CreateUserDto data) throws BrokerUserCreationException { - /* create user */ - final String createUrl = "/users/" + username; - log.debug("create user, username={}, url={}, data={}", username, createUrl, data); - final ResponseEntity<Void> createResponse = restTemplate.exchange(createUrl, HttpMethod.PUT, - new HttpEntity<>(data, getHeaders()), Void.class); - if (!createResponse.getStatusCode().equals(HttpStatus.CREATED)) { - log.error("Failed to create user at broker service: {}", createResponse.getStatusCode()); - throw new BrokerUserCreationException("Failed to create user at broker service"); - } - log.info("Created user at broker service with username {}", username); - } - - @Override - public UserDetailsDto findUser(String username) throws BrokerUserCreationException { - /* create user */ - final String findUrl = "/users/" + username; - log.debug("find user, username={}, url={}", username, findUrl); - final ResponseEntity<UserDetailsDto> findResponse = restTemplate.exchange(findUrl, HttpMethod.GET, - new HttpEntity<>(null, getHeaders()), UserDetailsDto.class); - if (!findResponse.getStatusCode().equals(HttpStatus.OK)) { - log.error("Failed to find user at broker service: {}", findResponse.getStatusCode()); - throw new BrokerUserCreationException("Failed to create user at broker service"); - } - log.info("Found user at broker service with username {}", username); - return findResponse.getBody(); - } - - @Override - public void modifyHostPermissions(String username, GrantVirtualHostPermissionsDto data) throws BrokerUserCreationException { - /* create user */ - final URI modifyUri = URI.create(gatewayConfig.getGatewayEndpoint() + "/permissions/%2F/" + username); - log.debug("modify host permissions, username= {}, url={}, data={}", username, modifyUri, data); - final ResponseEntity<Void> createResponse = restTemplate.exchange(modifyUri, HttpMethod.PUT, - new HttpEntity<>(data, getHeaders()), Void.class); - if (!createResponse.getStatusCode().equals(HttpStatus.CREATED)) { - log.error("Failed to modify user permissions at broker service: {}", createResponse.getStatusCode()); - throw new BrokerUserCreationException("Failed to modify user permissions at broker service"); - } - log.info("Modified user permissions at broker service for user with username {}", username); - } - - @Override - public void modifyUserPassword(String username, CreateUserDto data) throws BrokerUserCreationException { - /* modify at broker service */ - final String modifyUrl = "/users/" + username; - log.debug("modify user password, username={}, url={}, data={}", username, modifyUrl, data); - final ResponseEntity<Void> response = restTemplate.exchange(modifyUrl, HttpMethod.PUT, - new HttpEntity<>(data, getHeaders()), Void.class); - if (!response.getStatusCode().equals(HttpStatus.NO_CONTENT)) { - log.error("Failed to update user password at queue service: {}", response.getStatusCode()); - throw new BrokerUserCreationException("Failed to update user password at queue service"); - } - log.info("Updated user password at queue service for username {}", username); - } - - /** - * Retrieves the authentication headers from the configuration for the broker service. - * - * @return The headers. - */ - private HttpHeaders getHeaders() { - log.debug("authenticate at broker service with username={}", amqpConfig.getAmqpUsername()); - return new HttpHeaders() {{ - String auth = amqpConfig.getAmqpUsername() + ":" + amqpConfig.getAmqpPassword(); - byte[] encodedAuth = Base64.encodeBase64(auth.getBytes(Charset.defaultCharset())); - String authHeader = "Basic " + new String(encodedAuth); - set("Authorization", authHeader); - }}; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AmqpMapper.java b/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AmqpMapper.java deleted file mode 100644 index 8660009d07..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AmqpMapper.java +++ /dev/null @@ -1,19 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.amqp.GrantVirtualHostPermissionsDto; -import org.mapstruct.Mapper; - -@Mapper(componentModel = "spring") -public interface AmqpMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AmqpMapper.class); - - default GrantVirtualHostPermissionsDto defaultVirtualHostUserPermissions() { - return GrantVirtualHostPermissionsDto.builder() - .configure("") - .read("") - .write("") - .build(); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AuthenticationMapper.java b/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AuthenticationMapper.java deleted file mode 100644 index 95db950dec..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/AuthenticationMapper.java +++ /dev/null @@ -1,22 +0,0 @@ -package at.tuwien.mapper; - -import org.apache.commons.codec.digest.DigestUtils; -import org.mapstruct.Mapper; - -import java.util.Arrays; -import java.util.List; - -@Mapper(componentModel = "spring") -public interface AuthenticationMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(AuthenticationMapper.class); - - default String authorizationToTokenHash(String authorization) { - final List<String> parts = Arrays.asList(authorization.split(" ")); - log.trace("authorization was split into sub-parts (only first 10 letters): {}", parts.stream().map(p -> p.substring(0,10))); - final String token = parts.get(1); - log.trace("extracted token {} (only first 10 letters)", token.substring(0, 10)); - return DigestUtils.sha256Hex(token); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/UserMapper.java b/fda-authentication-service/services/src/main/java/at/tuwien/mapper/UserMapper.java deleted file mode 100644 index 790495eab6..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/mapper/UserMapper.java +++ /dev/null @@ -1,130 +0,0 @@ -package at.tuwien.mapper; - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.auth.JwtResponseDto; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.auth.TokenBriefDto; -import at.tuwien.api.auth.TokenDto; -import at.tuwien.api.user.*; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.OrcidMalformedException; -import org.mapstruct.Mapper; -import org.mapstruct.Mapping; -import org.mapstruct.Mappings; -import org.springframework.security.core.GrantedAuthority; -import org.springframework.security.core.authority.SimpleGrantedAuthority; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Objects; -import java.util.stream.Collectors; - -@Mapper(componentModel = "spring") -public interface UserMapper { - - org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(UserMapper.class); - - User signupRequestDtoToUser(SignupRequestDto data); - - UserDetailsDto userToUserDetailsDto(User data); - - RoleType roleTypeDtoToRoleType(RoleTypeDto data); - - UserPasswordDto userResetDtoToUserPasswordDto(UserResetDto data); - - UserBriefDto userToUserBriefDto(User data); - - CreateUserDto userPasswordDtoToCreateUserDto(UserPasswordDto data); - - @Mappings({ - @Mapping(source = "token", target = "token"), - @Mapping(source = "expires", target = "expires") - }) - TokenDto tokenToTokenDto(Token data); - - TokenBriefDto tokenToTokenBriefDto(Token data); - - @Transactional(readOnly = true) - default JwtResponseDto principalToJwtResponseDto(Object data) { - final UserDetailsDto details = (UserDetailsDto) data; - return JwtResponseDto.builder() - .id(details.getId()) - .username(details.getUsername()) - .email(details.getEmail()) - .roles(details.getAuthorities() - .stream() - .filter(Objects::nonNull) - .map(GrantedAuthority::getAuthority) - .collect(Collectors.toList())) - .build(); - } - - @Transactional(readOnly = true) - default UserDto userToUserDto(User data) throws OrcidMalformedException { - return UserDto.builder() - .id(data.getId()) - .username(data.getUsername()) - .email(data.getEmail()) - .password(data.getPassword()) - .firstname(data.getFirstname()) - .lastname(data.getLastname()) - .titlesBefore(data.getTitlesBefore()) - .titlesAfter(data.getTitlesAfter()) - .emailVerified(data.getEmailVerified()) - .affiliation(data.getAffiliation()) - .themeDark(data.getThemeDark()) - .orcid(userToUncompressedOrcid(data)) - .authorities(data.getRoles() - .stream() - .map(this::roleTypeToGrantedAuthorityDto) - .collect(Collectors.toList())) - .roles(data.getRoles() - .stream() - .map(Enum::name) - .collect(Collectors.toList())) - .build(); - } - - default GrantedAuthority roleTypeToGrantedAuthority(RoleType data) { - return new SimpleGrantedAuthority(data.name()); - } - - default GrantedAuthorityDto roleTypeToGrantedAuthorityDto(RoleType data) { - return GrantedAuthorityDto.builder() - .authority(data.name()) - .build(); - } - - default String userUpdateDtoToCompressedOrcid(UserUpdateDto data) { - if (data.getOrcid() == null) { - return null; - } - return data.getOrcid().replace("-", ""); - } - - default String userToUncompressedOrcid(User data) throws OrcidMalformedException { - if (data.getOrcid() == null) { - return null; - } - if (data.getOrcid().length() != 16) { - log.error("Provided ORCID is not compressed"); - log.debug("provided orcid {} is not compressed, length is {}", data.getOrcid(), data.getOrcid().length()); - throw new OrcidMalformedException("Provided ORCID is not compressed"); - } - return new StringBuilder(data.getOrcid().substring(0, 4)) - .append("-") - .append(data.getOrcid(), 4, 8) - .append("-") - .append(data.getOrcid(), 8, 12) - .append("-") - .append(data.getOrcid(), 12, 16) - .toString(); - } - - /* keep */ - default String roleTypeToString(RoleType data) { - return data.name(); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/ImageRepository.java b/fda-authentication-service/services/src/main/java/at/tuwien/repositories/ImageRepository.java deleted file mode 100644 index fd41fb2513..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/ImageRepository.java +++ /dev/null @@ -1,9 +0,0 @@ -package at.tuwien.repositories; - -import at.tuwien.entities.container.image.ContainerImage; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -@Repository -public interface ImageRepository extends JpaRepository<ContainerImage, Long> { -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TimeSecretRepository.java b/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TimeSecretRepository.java deleted file mode 100644 index abd287219f..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TimeSecretRepository.java +++ /dev/null @@ -1,14 +0,0 @@ -package at.tuwien.repositories; - -import at.tuwien.entities.user.TimeSecret; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface TimeSecretRepository extends JpaRepository<TimeSecret, Long> { - - Optional<TimeSecret> findByToken(String token); - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TokenRepository.java b/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TokenRepository.java deleted file mode 100644 index 0d3fed6a14..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/TokenRepository.java +++ /dev/null @@ -1,23 +0,0 @@ -package at.tuwien.repositories; - -import at.tuwien.entities.user.Token; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.data.jpa.repository.Query; -import org.springframework.data.repository.query.Param; -import org.springframework.stereotype.Repository; - -import java.util.List; -import java.util.Optional; - -@Repository -public interface TokenRepository extends JpaRepository<Token, Long> { - - @Query("select t from Token t where t.creator = :userid") - List<Token> findMine(@Param("userid") Long userid); - - Optional<Token> findByTokenHash(String tokenHash); - - - Optional<Token> findByInvalidTokenHash(@Param("hash") String tokenHash); - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/UserRepository.java b/fda-authentication-service/services/src/main/java/at/tuwien/repositories/UserRepository.java deleted file mode 100644 index 833445712c..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/repositories/UserRepository.java +++ /dev/null @@ -1,18 +0,0 @@ -package at.tuwien.repositories; - -import at.tuwien.entities.user.User; -import org.springframework.data.jpa.repository.JpaRepository; -import org.springframework.stereotype.Repository; - -import java.util.Optional; - -@Repository -public interface UserRepository extends JpaRepository<User, Long> { - - Optional<User> findByUsername(String username); - - Optional<User> findByEmail(String email); - - Optional<User> findByUsernameOrEmail(String username, String email); - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java deleted file mode 100644 index 9b5f86b9ce..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/AuthenticationService.java +++ /dev/null @@ -1,46 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.api.auth.JwtResponseDto; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.user.UserModifyPasswordDto; -import at.tuwien.exception.TokenRevokedException; -import at.tuwien.exception.UserEmailNotVerifiedException; -import at.tuwien.exception.UserNotFoundException; -import org.springframework.transaction.annotation.Transactional; - -import java.security.Principal; - -public interface AuthenticationService { - - /** - * Authenticates a user with given credentials - * - * @param data The credentials. - * @return The token, if successful - * @throws UserEmailNotVerifiedException The user email is not verified. - * @throws UserNotFoundException The user was not found by username. - */ - JwtResponseDto authenticate(LoginRequestDto data) throws UserEmailNotVerifiedException, UserNotFoundException; - - void verifyToken(String authorization) throws TokenRevokedException; - - /** - * Authenticate a user with username and a reset token as credentials. - * - * @param data The credentials. - * @return The token if successful. - * @throws UserEmailNotVerifiedException The user email is not verified. - * @throws UserNotFoundException The user was not found by username. - */ - JwtResponseDto authenticate(UserModifyPasswordDto data) throws UserEmailNotVerifiedException, - UserNotFoundException; - - /** - * Renews a token for a given principal - * TODO limit rate of renewal to 1/hour - * - * @param principal The principal. - * @return The token, if successful - */ - JwtResponseDto renew(Principal principal); -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/MailService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/MailService.java deleted file mode 100644 index d08538e044..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/MailService.java +++ /dev/null @@ -1,21 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.entities.user.User; -import at.tuwien.exception.UserEmailFailedException; -import org.springframework.stereotype.Service; -import org.thymeleaf.context.Context; - -@Service -public interface MailService { - - /** - * Sends a mail to a user with subject and template and passing variables through the context. - * - * @param user The user. - * @param subject The subject. - * @param template The template. - * @param context The context. - * @throws UserEmailFailedException The user email was not found. - */ - void send(User user, String subject, String template, Context context) throws UserEmailFailedException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/QueueService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/QueueService.java deleted file mode 100644 index 6598ee2f7f..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/QueueService.java +++ /dev/null @@ -1,32 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.UserPasswordDto; -import at.tuwien.entities.user.User; -import at.tuwien.exception.BrokerUserCreationException; -import org.springframework.stereotype.Service; - - -@Service -public interface QueueService { - - UserDetailsDto findUser(String username) throws BrokerUserCreationException; - - /** - * Creates a user at the Broker Service - * - * @param data The user data@throws BrokerUserCreationException The broker did not create the user. - */ - void createUser(String username, SignupRequestDto data) throws BrokerUserCreationException; - - /** - * Modify a user password at the Broker Service - * - * @param user The user data. - * @param data The user password.. - * @throws BrokerUserCreationException The broker did not modify the user. - */ - void modifyUserPassword(User user, CreateUserDto data) throws BrokerUserCreationException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/TimeSecretService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/TimeSecretService.java deleted file mode 100644 index a907a76883..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/TimeSecretService.java +++ /dev/null @@ -1,34 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.User; -import at.tuwien.exception.SecretInvalidException; - -public interface TimeSecretService { - - /** - * Find token by random string. - * - * @param token The random string. - * @return The token. - * @throws SecretInvalidException The token was not found or has expired. - */ - TimeSecret find(String token) throws SecretInvalidException; - - /** - * Create a token with random string. - * - * @param user The user. - * @return The token. - */ - TimeSecret create(User user); - - /** - * Invalidate a token for a given user. - * - * @param token The token. - * @return The user, if successful. - * @throws SecretInvalidException THe token was not found or has expired. - */ - User invalidate(String token) throws SecretInvalidException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java deleted file mode 100644 index caeeb8d41f..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/TokenService.java +++ /dev/null @@ -1,70 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.entities.user.Token; -import at.tuwien.exception.TokenNotEligableException; -import at.tuwien.exception.TokenNotFoundException; -import at.tuwien.exception.UserNotFoundException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.servlet.ServletException; -import java.security.Principal; -import java.util.List; - -@Service -public interface TokenService { - - /** - * Finds all tokens in the metadata database for a given user principal. - * - * @param principal The user principal. - * @return The list of tokens, if successful. - * @throws UserNotFoundException The user does not exist in the metadata database. - */ - List<Token> findAll(Principal principal) throws UserNotFoundException; - - /** - * Creates a token for a given user principal. - * - * @param principal The user principal. - * @return The created token, if successful. - * @throws UserNotFoundException The user does not exist in the metadata database. - * @throws TokenNotEligableException The user is not eligable to mint developer tokens. - */ - Token create(Principal principal) throws UserNotFoundException, TokenNotEligableException; - - /** - * Finds a token by hash. - * - * @param tokenHash The token hash. - * @return The token, if successful. - * @throws TokenNotFoundException The token was not found in the metadata database. - */ - Token findOne(String tokenHash) throws TokenNotFoundException; - - /** - * Finds a token by id. - * - * @param id The token id. - * @return The token, if successful. - * @throws TokenNotFoundException The token was not found in the metadata database. - */ - Token findOne(Long id) throws TokenNotFoundException; - - /** - * Deletes a developer token in the metadata database by hash and user principal. - * - * @param tokenHash The token hash. - * @param principal The user principal. - * @throws TokenNotFoundException The token was not found in the metadata database. - * @throws UserNotFoundException The user does not exist in the metadata database. - */ - void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException; - - /** - * Checks if the developer token has not been marked as deleted - * - * @param jwt The token - */ - void check(String jwt) throws ServletException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java deleted file mode 100644 index 201ec49bff..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/UserService.java +++ /dev/null @@ -1,117 +0,0 @@ -package at.tuwien.service; - -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.*; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; - -import java.util.List; - -public interface UserService { - - /** - * Find all users. - * - * @return The list of users. - */ - List<User> findAll(); - - /** - * Finds a specific user by given id. - * - * @param id The id. - * @return The user. - * @throws UserNotFoundException The user was not found in the metadata database. - */ - User find(Long id) throws UserNotFoundException; - - /** - * Finds a specific user by given username or email. - * - * @param username The username. - * @param email The email. - * @return The user. - * @throws UserNotFoundException The user was not found in the metadata database. - */ - User findByUsernameOrEmail(String username, String email) throws UserNotFoundException; - - /** - * Finds a specific user by given username. - * - * @param username The username. - * @return The user. - * @throws UserNotFoundException The user was not found in the metadata database. - */ - User findByUsername(String username) throws UserNotFoundException; - - /** - * Creates a new user with information. - * - * @param user The information. - * @return The created user. - * @throws UserEmailExistsException The email in the information exists already. - * @throws UserNameExistsException The username exists already. - * @throws RoleNotFoundException The role specified was not found. - */ - User create(SignupRequestDto user) throws UserEmailExistsException, UserNameExistsException, RoleNotFoundException; - - /** - * Resets the user information - * - * @param data The user username or email - * @return The user. - * @throws UserNotFoundException The user was not found. - */ - User forgot(UserForgotDto data) throws UserNotFoundException; - - /** - * Updates a user with given id and updated information. - * - * @param id The id. - * @param data The updated information. - * @return The updated user. - * @throws UserNotFoundException The user was not found. - */ - User update(Long id, UserUpdateDto data) throws UserNotFoundException, OrcidMalformedException; - - /** - * Updates a user with given id and updated roles. - * - * @param id The id. - * @param data The updated roles. - * @return The updated user. - * @throws UserNotFoundException The user was not found. - * @throws RoleNotFoundException Some updated roles were not found. - */ - User updateRoles(Long id, UserRolesDto data) - throws UserNotFoundException, RoleNotFoundException, RoleUniqueException; - - /** - * Sets the theme for the provided user. - * - * @param id The user id. - * @param data The theme. - * @throws UserNotFoundException The user was not found. - */ - void updateTheme(Long id, UserThemeSetDto data) throws UserNotFoundException; - - /** - * Updates a user with the given id and updated password. - * - * @param id The id. - * @param data The updated roles. - * @return The updated user. - * @throws UserNotFoundException The user was not found. - */ - User updatePassword(Long id, UserPasswordDto data) throws UserNotFoundException, BrokerUserCreationException; - - /** - * Updates a user with the given id and updated email. - * - * @param id The id. - * @param data The updated email. - * @return The updated user. - * @throws UserNotFoundException The user was not found. - */ - User updateEmail(Long id, UserEmailDto data) throws UserNotFoundException; -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java deleted file mode 100644 index e4a304e7e3..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/AuthenticationServiceImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.auth.JwtResponseDto; -import at.tuwien.api.auth.LoginRequestDto; -import at.tuwien.api.user.UserModifyPasswordDto; -import at.tuwien.auth.JwtUtils; -import at.tuwien.config.MailConfig; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.TokenNotFoundException; -import at.tuwien.exception.TokenRevokedException; -import at.tuwien.exception.UserEmailNotVerifiedException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.mapper.AuthenticationMapper; -import at.tuwien.mapper.UserMapper; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.service.AuthenticationService; -import at.tuwien.service.UserService; -import com.auth0.jwt.exceptions.TokenExpiredException; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContextHolder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.security.Principal; -import java.time.Instant; -import java.util.Optional; - - -@Slf4j -@Service -public class AuthenticationServiceImpl implements AuthenticationService { - - private final JwtUtils jwtUtils; - private final UserMapper userMapper; - private final MailConfig mailConfig; - private final UserService userService; - private final TokenRepository tokenRepository; - private final AuthenticationMapper authenticationMapper; - private final AuthenticationManager authenticationManager; - - @Autowired - public AuthenticationServiceImpl(JwtUtils jwtUtils, UserMapper userMapper, MailConfig mailConfig, - UserService userService, TokenRepository tokenRepository, - AuthenticationMapper authenticationMapper, - AuthenticationManager authenticationManager) { - this.jwtUtils = jwtUtils; - this.userMapper = userMapper; - this.mailConfig = mailConfig; - this.userService = userService; - this.tokenRepository = tokenRepository; - this.authenticationMapper = authenticationMapper; - this.authenticationManager = authenticationManager; - } - - @Override - @Transactional(readOnly = true) - public JwtResponseDto authenticate(LoginRequestDto data) throws UserEmailNotVerifiedException, - UserNotFoundException { - final User user = userService.findByUsername(data.getUsername()); - if (mailConfig.getMailVerify() && !user.getEmailVerified()) { - log.error("E-Mail not verified for username {}", data.getUsername()); - throw new UserEmailNotVerifiedException("E-Mail not verified"); - } - final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(data.getUsername(), - data.getPassword()); - final Authentication authentication = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - final JwtResponseDto response = userMapper.principalToJwtResponseDto(authentication.getPrincipal()); - response.setToken(jwtUtils.generateJwtToken(data.getUsername())); - return response; - } - - @Override - @Transactional - public void verifyToken(String authorization) { - final String hash = authenticationMapper.authorizationToTokenHash(authorization); - final Optional<Token> optional = tokenRepository.findByTokenHash(hash); - if (optional.isEmpty()) { - log.trace("token with hash {} is not a developer token, skip update", hash); - return; - } - final Token token = optional.get(); - if (token.getExpires().isBefore(Instant.now())) { - log.error("Failed to verify token because it is expired"); - throw new TokenExpiredException("Failed to verify token"); - } - token.setLastUsed(Instant.now()); - tokenRepository.save(token); - log.info("Updated token usage of token with hash {}", hash); - } - - @Override - @Transactional(readOnly = true) - public JwtResponseDto authenticate(UserModifyPasswordDto data) throws UserEmailNotVerifiedException, - UserNotFoundException { - final User user = userService.findByUsername(data.getUsername()); - if (mailConfig.getMailVerify() && !user.getEmailVerified()) { - log.error("E-Mail not verified for username {}", data.getUsername()); - throw new UserEmailNotVerifiedException("E-Mail not verified"); - } - final UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(data.getUsername(), - data.getPassword()); - final Authentication authentication = authenticationManager.authenticate(token); - SecurityContextHolder.getContext().setAuthentication(authentication); - final JwtResponseDto response = userMapper.principalToJwtResponseDto(authentication.getPrincipal()); - response.setToken(jwtUtils.generateJwtToken(data.getUsername())); - return response; - } - - @Override - @Transactional(readOnly = true) - public JwtResponseDto renew(Principal principal) { - final UsernamePasswordAuthenticationToken token = (UsernamePasswordAuthenticationToken) principal; - final JwtResponseDto response = userMapper.principalToJwtResponseDto(token.getPrincipal()); - response.setToken(jwtUtils.generateJwtToken(principal.getName())); - return response; - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/MailServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/MailServiceImpl.java deleted file mode 100644 index 7ac3676aec..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/MailServiceImpl.java +++ /dev/null @@ -1,62 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.config.MailConfig; -import at.tuwien.entities.user.User; -import at.tuwien.exception.UserEmailFailedException; -import at.tuwien.service.MailService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.mail.MailException; -import org.springframework.mail.SimpleMailMessage; -import org.springframework.mail.javamail.JavaMailSender; -import org.springframework.mail.javamail.MimeMessageHelper; -import org.springframework.stereotype.Service; -import org.thymeleaf.context.Context; -import org.thymeleaf.spring5.SpringTemplateEngine; - -import javax.mail.MessagingException; -import javax.mail.internet.MimeMessage; -import java.net.SocketTimeoutException; -import java.nio.charset.StandardCharsets; - -@Slf4j -@Service -public class MailServiceImpl implements MailService { - - private final MailConfig mailConfig; - private final JavaMailSender mailSender; - private final SpringTemplateEngine templateEngine; - - @Autowired - public MailServiceImpl(MailConfig mailConfig, JavaMailSender mailSender, SpringTemplateEngine templateEngine) { - this.mailConfig = mailConfig; - this.mailSender = mailSender; - this.templateEngine = templateEngine; - } - - @Override - public void send(User user, String subject, String path, Context context) throws UserEmailFailedException { - log.debug("send email template with context, subject={}, path={}, context={}", subject, path, context); - log.trace("send email for user {}", user); - if (mailConfig.getMailUsername().isBlank()) { - /* local instance, not the deployment instance */ - return; - } - log.trace("add default website variable website={} to context", mailConfig.getWebsite()); - context.setVariable("website", mailConfig.getWebsite()); - final SimpleMailMessage message = new SimpleMailMessage(); - message.setTo(user.getEmail()); - message.setSubject(subject); - message.setFrom(mailConfig.getMailFrom()); - message.setReplyTo(mailConfig.getMailReplyTo()); - final String content = templateEngine.process(path, context); - message.setText(content); - try { - mailSender.send(message); - } catch (MailException e) { - log.error("Failed to send email to address {}, reason: {}", user.getEmail(), e.getMessage()); - throw new UserEmailFailedException("Failed to send message", e); - } - log.info("Sent mail to email address {}", user.getEmail()); - } -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java deleted file mode 100644 index af3c95e113..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/QueueServiceImpl.java +++ /dev/null @@ -1,52 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.amqp.CreateUserDto; -import at.tuwien.api.amqp.UserDetailsDto; -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.UserPasswordDto; -import at.tuwien.entities.user.User; -import at.tuwien.exception.BrokerUserCreationException; -import at.tuwien.gateway.BrokerServiceGateway; -import at.tuwien.mapper.AmqpMapper; -import at.tuwien.service.QueueService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; - -@Slf4j -@Service -public class QueueServiceImpl implements QueueService { - - private final AmqpMapper amqpMapper; - private final BrokerServiceGateway brokerServiceGateway; - - @Autowired - public QueueServiceImpl(AmqpMapper amqpMapper, BrokerServiceGateway brokerServiceGateway) { - this.amqpMapper = amqpMapper; - this.brokerServiceGateway = brokerServiceGateway; - } - - @Override - public UserDetailsDto findUser(String username) throws BrokerUserCreationException { - log.debug("broker service find user, username={}", username); - return brokerServiceGateway.findUser(username); - } - - @Override - public void createUser(String username, SignupRequestDto data) throws BrokerUserCreationException { - log.debug("broker service create user, username={}, data={}", username, data); - final CreateUserDto userDto = CreateUserDto.builder() - .password(data.getPassword()) - .tags("") - .build(); - brokerServiceGateway.createUser(username, userDto); - brokerServiceGateway.modifyHostPermissions(username, amqpMapper.defaultVirtualHostUserPermissions()); - } - - @Override - public void modifyUserPassword(User user, CreateUserDto data) throws BrokerUserCreationException { - log.debug("broker service create user, user={}, data={}", user, data); - brokerServiceGateway.modifyUserPassword(user.getUsername(), data); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TimeSecretServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TimeSecretServiceImpl.java deleted file mode 100644 index 2d959bfe79..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TimeSecretServiceImpl.java +++ /dev/null @@ -1,78 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.entities.user.TimeSecret; -import at.tuwien.entities.user.User; -import at.tuwien.exception.SecretInvalidException; -import at.tuwien.repositories.TimeSecretRepository; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.TimeSecretService; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.lang.RandomStringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Optional; - -@Log4j2 -@Service -public class TimeSecretServiceImpl implements TimeSecretService { - - private final UserRepository userRepository; - private final TimeSecretRepository timeSecretRepository; - - @Autowired - public TimeSecretServiceImpl(UserRepository userRepository, TimeSecretRepository timeSecretRepository) { - this.userRepository = userRepository; - this.timeSecretRepository = timeSecretRepository; - } - - @Override - @Transactional(readOnly = true) - public TimeSecret find(String token) throws SecretInvalidException { - /* check */ - final Optional<TimeSecret> entity = timeSecretRepository.findByToken(token); - if (entity.isEmpty()) { - log.error("Failed to find token: {}", token); - throw new SecretInvalidException("Failed to find token"); - } - return entity.get(); - } - - @Override - @Transactional - public TimeSecret create(User user) { - /* check */ - final TimeSecret token = TimeSecret.builder() - .processed(false) - .uid(user.getId()) - .validTo(Instant.now().plus(1, ChronoUnit.DAYS)) - .user(user) - .token(RandomStringUtils.randomAlphabetic(10)) - .build(); - final TimeSecret out = timeSecretRepository.save(token); - log.info("Created token with id {}", out.getId()); - log.trace("created token {}", out); - return out; - } - - @Override - @Transactional - public User invalidate(String token) throws SecretInvalidException { - /* check */ - final TimeSecret timeSecret = find(token); - /* verify */ - final User user = timeSecret.getUser(); - user.setEmailVerified(true); - userRepository.save(user); - log.info("Verified user with username {}", user.getUsername()); - /* invalidate */ - timeSecret.setProcessed(true); - final TimeSecret out = timeSecretRepository.save(timeSecret); - log.info("Invalidated token with id {}", out.getId()); - return user; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java deleted file mode 100644 index 067849bb17..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/TokenServiceImpl.java +++ /dev/null @@ -1,123 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.auth.JwtUtils; -import at.tuwien.entities.user.Token; -import at.tuwien.entities.user.User; -import at.tuwien.exception.TokenNotEligableException; -import at.tuwien.exception.TokenNotFoundException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.repositories.TokenRepository; -import at.tuwien.service.TokenService; -import at.tuwien.service.UserService; -import lombok.extern.log4j.Log4j2; -import org.apache.commons.codec.digest.DigestUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.servlet.ServletException; -import java.security.Principal; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.List; -import java.util.Optional; - -@Log4j2 -@Service -public class TokenServiceImpl implements TokenService { - - private final JwtUtils jwtUtils; - private final UserService userService; - private final TokenRepository tokenRepository; - - @Autowired - public TokenServiceImpl(UserService userService, JwtUtils jwtUtils, TokenRepository tokenRepository) { - this.userService = userService; - this.jwtUtils = jwtUtils; - this.tokenRepository = tokenRepository; - } - - @Override - @Transactional(readOnly = true) - public List<Token> findAll(Principal principal) throws UserNotFoundException { - final User user = userService.findByUsername(principal.getName()); - log.debug("found user for username {}", principal.getName()); - log.trace("resulted in user {}", user); - return tokenRepository.findMine(user.getId()); - } - - @Override - @Transactional - public Token create(Principal principal) throws UserNotFoundException, TokenNotEligableException { - final User user = userService.findByUsername(principal.getName()); - final Instant expires = Instant.now().plus(365, ChronoUnit.DAYS); - final String token = jwtUtils.generateJwtToken(principal.getName(), expires); - final String tokenHash = DigestUtils.sha256Hex(token); - /* save */ - final Token tmp = Token.builder() - .tokenHash(tokenHash) - .creator(user.getId()) - .expires(expires) - .build(); - final Token entity = tokenRepository.save(tmp); - entity.setToken(token); - log.info("Created token with id {}", entity.getId()); - return entity; - } - - @Override - @Transactional(readOnly = true) - public Token findOne(String tokenHash) throws TokenNotFoundException { - final Optional<Token> optional = tokenRepository.findByTokenHash(tokenHash); - if (optional.isEmpty()) { - log.error("Failed to find token with hash {}", tokenHash); - throw new TokenNotFoundException("Failed to find token"); - } - return optional.get(); - } - - @Override - @Transactional(readOnly = true) - public Token findOne(Long id) throws TokenNotFoundException { - final Optional<Token> optional = tokenRepository.findById(id); - if (optional.isEmpty()) { - log.error("Failed to find token with id {}", id); - throw new TokenNotFoundException("Failed to find token"); - } - return optional.get(); - } - - @Override - @Transactional - public void delete(String tokenHash, Principal principal) throws TokenNotFoundException, UserNotFoundException { - final Token token = findOne(tokenHash); - final User user = userService.findByUsername(principal.getName()); - if (!token.getCreator().equals(user.getId())) { - log.error("Attempted to delete foreign token"); - throw new TokenNotFoundException("Attempted to delete foreign token"); - } - token.setExpires(Instant.now()); - tokenRepository.save(token); - log.info("Deleted token with id {}", token.getId()); - } - - @Override - @Transactional - public void check(String jwt) throws ServletException { - final String tokenHash = JwtUtils.toHash(jwt); - final Optional<Token> optional = tokenRepository.findByInvalidTokenHash(tokenHash); - if (optional.isPresent()) { - log.error("Token with hash {} and id {} is marked as revoked", tokenHash, optional.get().getId()); - throw new ServletException("Token with hash " + tokenHash + " is marked as revoked"); - } - final Optional<Token> optional1 = tokenRepository.findByTokenHash(tokenHash); - if (optional1.isEmpty()) { - /* not a developer token */ - return; - } - final Token token = optional1.get(); - token.setLastUsed(Instant.now()); - tokenRepository.save(token); - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserDetailsServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserDetailsServiceImpl.java deleted file mode 100644 index a070972cdf..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserDetailsServiceImpl.java +++ /dev/null @@ -1,51 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.user.UserDetailsDto; -import at.tuwien.entities.user.User; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.mapper.UserMapper; -import at.tuwien.service.UserService; -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.security.core.userdetails.UserDetails; -import org.springframework.security.core.userdetails.UserDetailsService; -import org.springframework.security.core.userdetails.UsernameNotFoundException; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.stream.Collectors; - -@Slf4j -@Service -public class UserDetailsServiceImpl implements UserDetailsService { - - private final UserService userService; - private final UserMapper userMapper; - - @Autowired - public UserDetailsServiceImpl(UserService userService, UserMapper userMapper) { - this.userService = userService; - this.userMapper = userMapper; - } - - @Override - @Transactional(readOnly = true) - public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - final User user; - try { - user = userService.findByUsername(username); - } catch (UserNotFoundException e) { - log.error("Failed to find user with username {}, reason: {}", username, e.getMessage()); - throw new UsernameNotFoundException("Failed to find user", e); - } - log.trace("loaded user {}", user); - final UserDetailsDto details = userMapper.userToUserDetailsDto(user); - details.setAuthorities(user.getRoles() - .stream() - .map(userMapper::roleTypeToGrantedAuthority) - .collect(Collectors.toList())); - log.trace("mapped user {}", details); - return details; - } - -} diff --git a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java deleted file mode 100644 index ec48ed7d3c..0000000000 --- a/fda-authentication-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ /dev/null @@ -1,275 +0,0 @@ -package at.tuwien.service.impl; - -import at.tuwien.api.auth.SignupRequestDto; -import at.tuwien.api.user.*; -import at.tuwien.auth.MariaDbPassword; -import at.tuwien.config.AuthenticationConfig; -import at.tuwien.entities.user.RoleType; -import at.tuwien.entities.user.User; -import at.tuwien.exception.*; -import at.tuwien.exception.UserEmailExistsException; -import at.tuwien.exception.UserNameExistsException; -import at.tuwien.exception.UserNotFoundException; -import at.tuwien.mapper.UserMapper; -import at.tuwien.repositories.UserRepository; -import at.tuwien.service.UserService; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.dao.DataIntegrityViolationException; -import org.springframework.dao.DuplicateKeyException; -import org.springframework.security.crypto.password.PasswordEncoder; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import javax.validation.ConstraintViolationException; -import java.util.Arrays; -import java.util.LinkedList; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; - -@Log4j2 -@Service -public class UserServiceImpl implements UserService { - - private final UserMapper userMapper; - private final UserRepository userRepository; - private final PasswordEncoder passwordEncoder; - private final AuthenticationConfig authenticationConfig; - - @Autowired - public UserServiceImpl(UserMapper userMapper, UserRepository userRepository, - PasswordEncoder passwordEncoder, AuthenticationConfig authenticationConfig) { - this.userMapper = userMapper; - this.userRepository = userRepository; - this.passwordEncoder = passwordEncoder; - this.authenticationConfig = authenticationConfig; - } - - @Override - @Transactional(readOnly = true) - public List<User> findAll() { - return userRepository.findAll(); - } - - @Override - @Transactional(readOnly = true) - public User find(Long id) throws UserNotFoundException { - /* check */ - final Optional<User> user = userRepository.findById(id); - if (user.isEmpty()) { - log.error("User not found with id {}", id); - throw new UserNotFoundException("User not found"); - } - return user.get(); - } - - @Override - @Transactional(readOnly = true) - public User findByUsernameOrEmail(String username, String email) throws UserNotFoundException { - /* check */ - final Optional<User> user = userRepository.findByUsernameOrEmail(username, email); - if (user.isEmpty()) { - log.error("User not found with username {} or email {}", username, email); - throw new UserNotFoundException("User not found"); - } - return user.get(); - } - - @Override - @Transactional(readOnly = true) - public User findByUsername(String username) throws UserNotFoundException { - /* check */ - final Optional<User> user = userRepository.findByUsername(username); - if (user.isEmpty()) { - log.error("User not found with username {}", username); - throw new UserNotFoundException("User not found"); - } - return user.get(); - } - - @Override - @Transactional - public User create(SignupRequestDto data) throws UserEmailExistsException, UserNameExistsException { - /* duplicate */ - final Optional<User> email = userRepository.findByEmail(data.getEmail()); - if (email.isPresent()) { - log.error("Email address {} is already present in the database", data.getEmail()); - throw new UserEmailExistsException("Email taken"); - } - final Optional<User> username = userRepository.findByUsername(data.getUsername()); - if (username.isPresent()) { - log.error("Username {} is already present in the database", data.getUsername()); - throw new UserNameExistsException("Username taken"); - } - /* save */ - final User user = userMapper.signupRequestDtoToUser(data); - user.setEmailVerified(false); - final List<RoleType> roles = new LinkedList<>(Arrays.asList(authenticationConfig.getDefaultRoles())); - final List<RoleType> superUserRoles = List.of(RoleType.ROLE_RESEARCHER, RoleType.ROLE_DEVELOPER, RoleType.ROLE_DATA_STEWARD); - final List<String> developers = Arrays.asList(authenticationConfig.getSuperUsers()); - if (developers.contains(data.getUsername())) { - log.info("Assign all privileges to user with username {}", data.getUsername()); - superUserRoles.forEach(superRole -> { - if (roles.stream().noneMatch(role -> role.name().equals(superRole.name()))) { - roles.add(superRole); - } - }); - } - user.setRoles(roles); - user.setThemeDark(false); - user.setPassword(passwordEncoder.encode(data.getPassword())); - user.setDatabasePassword(MariaDbPassword.encode(data.getPassword())); - final User entity; - try { - entity = userRepository.save(user); - } catch (DataIntegrityViolationException e) { - log.error("Failed to create user with username {}", data.getUsername()); - throw new UserNameExistsException("Failed to create user with username " + data.getUsername(), e); - } - log.info("Created user with id {}", entity.getId()); - log.trace("created user {}", entity); - return entity; - } - - @Override - @Transactional - public User forgot(UserForgotDto data) throws UserNotFoundException { - /* check */ - final User user = findByUsernameOrEmail(data.getUsername(), data.getEmail()); - /* save */ - log.info("Forgot user with id {}", user.getId()); - log.trace("forgot user {}", user); - return user; - } - - @Override - @Transactional - public User update(Long id, UserUpdateDto data) throws UserNotFoundException, OrcidMalformedException { - /* check */ - final User user = find(id); - /* check */ - if (data.getOrcid() != null && !validateOrcid(data.getOrcid())) { - log.error("Checksum of the provided ORCID does not match"); - log.debug("checksum of the provided orcid {} does not match", data.getOrcid()); - throw new OrcidMalformedException(data.getOrcid()); - } - /* save */ - user.setTitlesBefore(data.getTitlesBefore()); - user.setTitlesAfter(data.getTitlesAfter()); - user.setFirstname(data.getFirstname()); - user.setLastname(data.getLastname()); - user.setUsername(user.getUsername()); - user.setAffiliation(data.getAffiliation()); - user.setOrcid(userMapper.userUpdateDtoToCompressedOrcid(data)); - log.debug("mapped data {} to new user {}", data, user); - final User entity = userRepository.save(user); - log.info("Updated user with id {}", entity.getId()); - log.trace("updated user {}", entity); - return entity; - } - - /** - * Validates a given ORCID checksum (ISO 7064 11,2) - * Source: https://support.orcid.org/hc/en-us/articles/360006897674-Structure-of-the-ORCID-Identifier - * - * @param orcid The ORCID. - * @return True if the ORCID provided is valid, false otherwise. - */ - public Boolean validateOrcid(String orcid) { - if (orcid == null) { - return true; - } - if (orcid.length() != 19) { - log.error("Provided ORCID has an invalid length"); - log.debug("provided orcid {} has an invalid length {}, is not 19", orcid, orcid.length()); - return false; - } - int total = 0; - for (int i = 0; i < orcid.length() - 1; i++) { - if (orcid.charAt(i) == '-') { - continue; - } - int digit = Character.getNumericValue(orcid.charAt(i)); - total = (total + digit) * 2; - } - int remainder = total % 11; - int result = (12 - remainder) % 11; - final String check = result == 10 ? "X" : String.valueOf(result); - log.trace("orcid checksum is '{}'", check); - return orcid.substring(18).equals(check); - } - - @Override - @Transactional - public User updateRoles(Long id, UserRolesDto data) - throws UserNotFoundException, RoleNotFoundException, RoleUniqueException { - /* check */ - final User user = find(id); - /* save */ - try { - user.setRoles(data.getRoles() - .stream() - .map(userMapper::roleTypeDtoToRoleType) - .collect(Collectors.toList())); - } catch (IllegalArgumentException e) { - log.error("Failed to map roles {}", data.getRoles()); - throw new RoleNotFoundException("Failed to map roles"); - } - log.debug("mapped roles {} to updated user {}", data, user); - final User entity; - try { - entity = userRepository.save(user); - } catch (DataIntegrityViolationException e) { - log.error("Failed to assign roles, must be unique"); - throw new RoleUniqueException("Failed to assign roles", e); - } - log.info("Updated roles of user with id {}", entity.getId()); - log.trace("roles={}", data.getRoles()); - return entity; - } - - @Override - @Transactional - public void updateTheme(Long id, UserThemeSetDto data) throws UserNotFoundException { - /* check */ - final User user = find(id); - /* save */ - user.setThemeDark(data.getThemeDark()); - final User entity = userRepository.save(user); - log.info("Updated theme of user with id {}", entity.getId()); - log.trace("theme={}", data); - } - - @Override - @Transactional - public User updatePassword(Long id, UserPasswordDto data) throws UserNotFoundException, - BrokerUserCreationException { - /* check */ - final User user = find(id); - /* save */ - final String passwd = passwordEncoder.encode(data.getPassword()); - log.trace("encoded updated user password {}", passwd); - user.setPassword(passwd); - user.setDatabasePassword(MariaDbPassword.encode(data.getPassword())); - log.trace("mapped password {} to updated user {}", passwd, user); - final User entity = userRepository.save(user); - log.info("Updated password of user with id {}", entity.getId()); - return entity; - } - - @Override - @Transactional - public User updateEmail(Long id, UserEmailDto data) throws UserNotFoundException { - /* check */ - final User user = find(id); - /* save */ - user.setEmail(data.getEmail()); - user.setEmailVerified(false); - log.trace("mapped email {} to updated user {}", data, user); - final User entity = userRepository.save(user); - log.info("Updated email of user with id {}", entity.getId()); - return entity; - } - -} -- GitLab