diff --git a/.env.unix.example b/.env.unix.example index bbc4ce743d04a6d9bcba22baef1ee7199fdfe0c9..2e4923867fc8febcd1db501b16baf212a99781df 100644 --- a/.env.unix.example +++ b/.env.unix.example @@ -14,4 +14,7 @@ BROKER_PASSWORD=fda KEYCLOAK_ADMIN=fda KEYCLOAK_ADMIN_PASSWORD=fda BROKER_CONSUMERS=2 -WEBSITE=http://example.com \ No newline at end of file +WEBSITE=http://example.com +KEY_ALIAS=server +KEY_STORE_PATH=/server.keystore +KEY_STORE_PASSWORD=password \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 07f8df8fa9704a08e043d468bf13efaf917b99c2..4fffbf9e7e8162e93d1dbecc3ab5c01056244e8c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -235,8 +235,10 @@ services: ports: - "9099:9099" depends_on: + fda-authentication-service: + condition: service_healthy fda-metadata-db: - condition: service_started + condition: service_healthy logging: driver: json-file diff --git a/fda-authentication-service/Dockerfile b/fda-authentication-service/Dockerfile index 38e737360bfc76a6984dcb88bf2f39d70fcba15f..243d9689185ffeca3c9a26047976352686e12e30 100644 --- a/fda-authentication-service/Dockerfile +++ b/fda-authentication-service/Dockerfile @@ -13,8 +13,8 @@ ENV KC_DB=mariadb WORKDIR /opt/keycloak -# for demonstration purposes only, please make sure to use proper certificates in production instead -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 +COPY ./server.keystore ./conf/server.keystore + RUN /opt/keycloak/bin/kc.sh build ###### SECOND STAGE ###### diff --git a/fda-authentication-service/dbrepo-realm.json b/fda-authentication-service/dbrepo-realm.json index 0e26cf80c915b19958a8a38e106a8fc2afc9fda0..2007120dde24222cf9ed5bb4f52e0a56a497f580 100644 --- a/fda-authentication-service/dbrepo-realm.json +++ b/fda-authentication-service/dbrepo-realm.json @@ -26,7 +26,7 @@ "oauth2DeviceCodeLifespan" : 600, "oauth2DevicePollingInterval" : 5, "enabled" : true, - "sslRequired" : "none", + "sslRequired" : "external", "registrationAllowed" : false, "registrationEmailAsUsername" : false, "rememberMe" : false, @@ -843,7 +843,7 @@ "otpPolicyLookAheadWindow" : 1, "otpPolicyPeriod" : 30, "otpPolicyCodeReusable" : false, - "otpSupportedApplications" : [ "totpAppGoogleName", "totpAppFreeOTPName", "totpAppMicrosoftAuthenticatorName" ], + "otpSupportedApplications" : [ "totpAppMicrosoftAuthenticatorName", "totpAppGoogleName", "totpAppFreeOTPName" ], "webAuthnPolicyRpEntityName" : "keycloak", "webAuthnPolicySignatureAlgorithms" : [ "ES256" ], "webAuthnPolicyRpId" : "", @@ -1761,7 +1761,7 @@ "subType" : "anonymous", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "oidc-usermodel-property-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-full-name-mapper", "saml-role-list-mapper", "saml-user-property-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-attribute-mapper", "saml-user-property-mapper", "oidc-address-mapper", "oidc-usermodel-attribute-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-full-name-mapper" ] } }, { "id" : "1849e52a-b8c9-44a8-af3d-ee19376a1ed1", @@ -1787,7 +1787,7 @@ "subType" : "authenticated", "subComponents" : { }, "config" : { - "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "oidc-full-name-mapper", "oidc-usermodel-property-mapper", "saml-role-list-mapper", "oidc-usermodel-attribute-mapper", "saml-user-attribute-mapper", "oidc-address-mapper", "oidc-sha256-pairwise-sub-mapper" ] + "allowed-protocol-mapper-types" : [ "saml-user-property-mapper", "saml-role-list-mapper", "oidc-sha256-pairwise-sub-mapper", "oidc-usermodel-property-mapper", "saml-user-attribute-mapper", "oidc-usermodel-attribute-mapper", "oidc-address-mapper", "oidc-full-name-mapper" ] } } ], "org.keycloak.userprofile.UserProfileProvider" : [ { @@ -1845,7 +1845,7 @@ "internationalizationEnabled" : false, "supportedLocales" : [ ], "authenticationFlows" : [ { - "id" : "5eb1d49c-38cd-41a8-9557-13bcffb1a642", + "id" : "6fa20b3b-44fc-40ba-b310-bb2e729d53b9", "alias" : "Account verification options", "description" : "Method with which to verity the existing account", "providerId" : "basic-flow", @@ -1867,7 +1867,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "29e68803-3d8d-4fe5-af03-0814e35a6ed3", + "id" : "c4f55400-b209-4eea-b996-70a19bea428e", "alias" : "Authentication Options", "description" : "Authentication options.", "providerId" : "basic-flow", @@ -1896,7 +1896,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "aeadaa3b-f3d2-47ef-9e0a-ea4b737fe684", + "id" : "e43378cd-51b1-4978-8083-c01b8ab9e4c7", "alias" : "Browser - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1918,7 +1918,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "4c958b93-579e-45da-8471-a6aebc439641", + "id" : "db5f2382-dabd-4a38-901b-95eff1d60c14", "alias" : "Direct Grant - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1940,7 +1940,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "6878c43f-0aa7-40c6-bf28-f984029e893b", + "id" : "29a20ae2-7a1a-44fd-888a-fac7954cfe01", "alias" : "First broker login - Conditional OTP", "description" : "Flow to determine if the OTP is required for the authentication", "providerId" : "basic-flow", @@ -1962,7 +1962,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "aae75803-fb3c-4c74-9ee7-73fc897aad05", + "id" : "3aa766d9-06ec-4073-9143-d810e12a2233", "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", @@ -1984,7 +1984,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "0987a7fe-5e31-40b1-be1f-6cd4a5a76e43", + "id" : "6a72a53f-bf1e-4c06-a366-71a4d963d5d0", "alias" : "Reset - Conditional OTP", "description" : "Flow to determine if the OTP should be reset or not. Set to REQUIRED to force.", "providerId" : "basic-flow", @@ -2006,7 +2006,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "68078236-033e-4f17-b02f-fb5f401be3fe", + "id" : "5cb272da-a960-4429-b090-e676b358bb80", "alias" : "User creation or linking", "description" : "Flow for the existing/non-existing user alternatives", "providerId" : "basic-flow", @@ -2029,7 +2029,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "28c746e7-fc22-4c61-872a-26670fffb8ed", + "id" : "58c9a9c9-3a95-45bf-9c40-d2926246158a", "alias" : "Verify Existing Account by Re-authentication", "description" : "Reauthentication of existing account", "providerId" : "basic-flow", @@ -2051,7 +2051,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "502957db-db9d-4138-a5e3-90fce22bff15", + "id" : "976a2b1b-0a72-4c7b-9ebb-356595ad08b4", "alias" : "browser", "description" : "browser based authentication", "providerId" : "basic-flow", @@ -2087,7 +2087,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "9312b6c7-f999-4285-ae9f-e365666066e7", + "id" : "63b290b2-4828-457b-9ed4-c68aa477cd2b", "alias" : "clients", "description" : "Base authentication for clients", "providerId" : "client-flow", @@ -2123,7 +2123,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "31c2af88-b37c-4fdb-8dec-23a5ba145114", + "id" : "9c557280-9319-45c7-8656-198765ab5f23", "alias" : "direct grant", "description" : "OpenID Connect Resource Owner Grant", "providerId" : "basic-flow", @@ -2152,7 +2152,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "c11ae35d-772a-4a1f-9934-a1945b45d617", + "id" : "6b0351e2-9b6b-475a-b59b-045096c2bbfa", "alias" : "docker auth", "description" : "Used by Docker clients to authenticate against the IDP", "providerId" : "basic-flow", @@ -2167,7 +2167,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "0485179d-460c-4ad0-b0b2-02bbf57eb012", + "id" : "39d51855-eabd-4b38-b2da-04046c3d156c", "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", @@ -2190,7 +2190,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "aa1b06b4-1c12-4f7c-9d5a-6422ab5bb89a", + "id" : "ee67da7f-0738-4ef6-a07f-cb814a171dc6", "alias" : "forms", "description" : "Username, password, otp and other auth forms.", "providerId" : "basic-flow", @@ -2212,7 +2212,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "fc52dc5f-16ee-499e-ad0a-ba04093efc33", + "id" : "011fb692-45ac-487f-8124-56836be0fbfe", "alias" : "http challenge", "description" : "An authentication flow based on challenge-response HTTP Authentication Schemes", "providerId" : "basic-flow", @@ -2234,7 +2234,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "576fb8d9-96b4-464a-a2b5-655c61755ec0", + "id" : "7fe214c5-6462-4965-8a37-6c5753f27a97", "alias" : "registration", "description" : "registration flow", "providerId" : "basic-flow", @@ -2250,7 +2250,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "a026a910-1a04-4eb0-9fe2-6eb72139a39b", + "id" : "e140eca6-cb4e-4db9-811a-f2c8a012d873", "alias" : "registration form", "description" : "registration form", "providerId" : "form-flow", @@ -2286,7 +2286,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "0d0e32f3-7030-42fa-be83-528281d9eca0", + "id" : "715bab3d-905a-4f91-8c62-b141cfd89002", "alias" : "reset credentials", "description" : "Reset credentials for a user if they forgot their password or something", "providerId" : "basic-flow", @@ -2322,7 +2322,7 @@ "userSetupAllowed" : false } ] }, { - "id" : "ec850fcc-5b8c-4109-9cf4-a16f84cd7804", + "id" : "83dd0a05-4fc5-47d3-84d8-f6f2641e4de3", "alias" : "saml ecp", "description" : "SAML ECP Profile Authentication Flow", "providerId" : "basic-flow", @@ -2338,13 +2338,13 @@ } ] } ], "authenticatorConfig" : [ { - "id" : "d42b19d3-2efe-4879-9ce0-7c055a3d949e", + "id" : "c07fe13c-bb0d-451b-955e-078376f851db", "alias" : "create unique user config", "config" : { "require.password.update.after.registration" : "false" } }, { - "id" : "f1c71d63-f83f-432d-b9b4-e1a97095b5e1", + "id" : "5b25a33e-a4dc-4827-a4e8-62fc1f383cef", "alias" : "review profile config", "config" : { "update.profile.on.first.login" : "missing" diff --git a/fda-authentication-service/docker-entrypoint.sh b/fda-authentication-service/docker-entrypoint.sh index ca82a7badaf62a1fe8aef07e992df833d17755b6..d8e0fdcd850f1e8f867b99c22926aef8c341589e 100644 --- a/fda-authentication-service/docker-entrypoint.sh +++ b/fda-authentication-service/docker-entrypoint.sh @@ -1,6 +1,6 @@ #!/bin/bash -/app/service-register.sh authentication-service 8080 -(while sleep 60; do bash /app/service-register.sh authentication-service 8080; done) & +/app/service-register.sh authentication-service 8443 8080 +(while sleep 60; do bash /app/service-register.sh authentication-service 8443 8080; done) & /opt/keycloak/bin/kc.sh start-dev --import-realm diff --git a/fda-authentication-service/server.keystore b/fda-authentication-service/server.keystore new file mode 100644 index 0000000000000000000000000000000000000000..93e5c28b23293910dac23a3e974cb485524a3a17 Binary files /dev/null and b/fda-authentication-service/server.keystore differ diff --git a/fda-authentication-service/service-register.sh b/fda-authentication-service/service-register.sh index 72734426bbe787ca64d394dd514b79f4561cf2c6..2d7f64be971f7e14fad690f3e6a9b708f092ab26 100755 --- a/fda-authentication-service/service-register.sh +++ b/fda-authentication-service/service-register.sh @@ -9,7 +9,8 @@ EUREKA_URI="http://$EUREKA_HOST:$EUREKA_PORT" SERVICE_NAME="$1" SERVICE_PROTOCOL="http" SERVICE_HOST="$1" -SERVICE_PORT="${2:-9000}" +SECURE_PORT="${2:-9000}" +SERVICE_PORT="${3:-9000}" SERVICE_URI="$SERVICE_PROTOCOL://$SERVICE_HOST:$SERVICE_PORT" HOME_URI="$SERVICE_URI/realms/dbrepo" @@ -72,8 +73,8 @@ cat <<EOF > /tmp/json.json "@enabled": "true" }, "securePort": { - "\$": "$SERVICE_PORT", - "@enabled": "false" + "\$": "$SECURE_PORT", + "@enabled": "true" }, "vipAddress": "$SERVICE_HOST", "secureVipAddress": "$SERVICE_HOST", diff --git a/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index 00c5a1f3dcd48020120630ab3568a58b71c9479e..37617f6ccf2abcfe03e994bfa44e3c445532951a 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-container-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -37,7 +37,7 @@ public class ReadyConfig { imageService.pull(imageRepository, imageTag); } else { log.debug("image {}:{} is present on the host", imageRepository, imageTag); - log.debug("skip pulling image {}:{}", imageRepository, imageTag); + log.trace("skip pulling image {}:{}", imageRepository, imageTag); } Files.touch(new File(readyPath)); log.info("Service is ready"); diff --git a/fda-container-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java b/fda-container-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java index 682ced6609f9ffee066ef95322d4975c532d17d0..ccb3dbae797cfd70d52009355c2f07405a78082b 100644 --- a/fda-container-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java +++ b/fda-container-service/services/src/main/java/at/tuwien/service/impl/ImageServiceImpl.java @@ -162,7 +162,6 @@ public class ImageServiceImpl implements ImageService { public boolean exists(String repository, String tag) { final List<Image> images = dockerClient.listImagesCmd() .exec(); - log.trace("found images {}", images); return images.stream() .filter(i -> Objects.nonNull(i.getRepoTags())) .filter(i -> i.getRepoTags().length > 0) diff --git a/fda-gateway-service/Dockerfile b/fda-gateway-service/Dockerfile index f8aea4c73b96ce8de9d78476a7685fd5540fe30f..a270239d58b0b4b44aefba904aa8d007fca8d720 100644 --- a/fda-gateway-service/Dockerfile +++ b/fda-gateway-service/Dockerfile @@ -21,6 +21,12 @@ ENV METADATA_USERNAME=postgres ENV METADATA_PASSWORD=postgres ENV GATEWAY_ENDPOINT=http://gateway-service:9095 ENV LOG_LEVEL=debug +ENV KEY_ALIAS=server +ENV KEY_PASS=password +ENV KEY_STORE=/server.keystore +ENV KEY_STORE_PASS=password + +COPY ./server.keystore /server.keystore COPY ./service_ready /usr/bin RUN chmod +x /usr/bin/service_ready diff --git a/fda-gateway-service/pom.xml b/fda-gateway-service/pom.xml index 210f10156c6a28fcbd4cc216c2a153ef49a20659..5110b896d822418f6133b6fc918fcf1f4dcbaebc 100644 --- a/fda-gateway-service/pom.xml +++ b/fda-gateway-service/pom.xml @@ -12,6 +12,14 @@ <version>1.1.0-alpha</version> <name>fda-gateway-service</name> <description>Demo project for Spring Boot</description> + <url>https://dbrepo-docs.ossdip.at</url> + <developers> + <developer> + <name>Martin Weise</name> + <email>martin.weise@tuwien.ac.at</email> + <organization>TU Wien</organization> + </developer> + </developers> <packaging>pom</packaging> <modules> diff --git a/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java index f4eae349a40b40c119e4c2d6545390c035b66c1a..2250fa50884df3f47b0b063975aea74f06203f80 100644 --- a/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-gateway-service/rest-service/src/main/java/at/tuwien/config/ReadyConfig.java @@ -18,9 +18,7 @@ public class ReadyConfig { private String readyPath; @EventListener(ApplicationReadyEvent.class) - public void init() throws IOException, InterruptedException { - log.info("Wait more for gateway start"); - Thread.sleep(20 * 1000L); + public void init() throws IOException { Files.touch(new File(readyPath)); } diff --git a/fda-gateway-service/rest-service/src/main/resources/application-docker.yml b/fda-gateway-service/rest-service/src/main/resources/application-docker.yml index 7c42301ea62bed1ffde75db2e3e4b462b83c39c5..b1eda86566fd3e7bfc350f2f730f3dd72965d776 100644 --- a/fda-gateway-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-gateway-service/rest-service/src/main/resources/application-docker.yml @@ -22,8 +22,20 @@ spring: name: gateway-service cloud: loadbalancer.ribbon.enabled: false + gateway: + httpclient: + ssl: + useInsecureTrustManager: true management.endpoints.web.exposure.include: health,info,prometheus -server.port: 9095 +server: + port: 9095 + ssl: + enabled: true + key-alias: "${KEY_ALIAS}" + key-store: "${KEY_STORE}" + key-store-type: jks + key-store-password: "${KEY_STORE_PASS}" + key-password: "${KEY_PASS}" logging: pattern.console: "%d %highlight(%-5level) %msg%n" level: diff --git a/fda-gateway-service/rest-service/src/main/resources/application-local.yml b/fda-gateway-service/rest-service/src/main/resources/application-local.yml index 6c2aeb22df2b9c71a815e3017ea755a7e0698402..ab294c2a578b643cb24002204804c6d3953b0b93 100644 --- a/fda-gateway-service/rest-service/src/main/resources/application-local.yml +++ b/fda-gateway-service/rest-service/src/main/resources/application-local.yml @@ -22,8 +22,20 @@ spring: name: gateway-service cloud: loadbalancer.ribbon.enabled: false + gateway: + httpclient: + ssl: + useInsecureTrustManager: true management.endpoints.web.exposure.include: health,info,prometheus -server.port: 9095 +server: + port: 9095 + ssl: + enabled: true + key-alias: server + key-store: "./server.keystore" + key-store-type: jks + key-store-password: password + key-password: password logging: pattern.console: "%d %highlight(%-5level) %msg%n" level: diff --git a/fda-gateway-service/rest-service/src/main/resources/application.yml b/fda-gateway-service/rest-service/src/main/resources/application.yml index 21e4e554b3f5be85eec0dd124c6a76d83b9c7667..94983082cbc97883df236924c7bb61226f877aee 100644 --- a/fda-gateway-service/rest-service/src/main/resources/application.yml +++ b/fda-gateway-service/rest-service/src/main/resources/application.yml @@ -17,9 +17,23 @@ spring: time_zone: UTC application: name: gateway-service + cloud: + loadbalancer.ribbon.enabled: false + gateway: + httpclient: + ssl: + useInsecureTrustManager: true springdoc.swagger-ui.enabled: false management.endpoints.web.exposure.include: health,info,prometheus -server.port: 9095 +server: + port: 9095 + ssl: + enabled: true + key-alias: "${KEY_ALIAS}" + key-store: "${KEY_STORE}" + key-store-type: jks + key-store-password: "${KEY_STORE_PASS}" + key-password: "${KEY_PASS}" logging: pattern.console: "%d %highlight(%-5level) %msg%n" level: diff --git a/fda-gateway-service/server.keystore b/fda-gateway-service/server.keystore new file mode 100644 index 0000000000000000000000000000000000000000..93e5c28b23293910dac23a3e974cb485524a3a17 Binary files /dev/null and b/fda-gateway-service/server.keystore differ diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java similarity index 53% rename from fda-metadata-db/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java rename to fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java index c92b4913c8d02e5f44677bd7d18cfdb0362081be..d300e6696719e5206c61e57269c26e40a71e1f3d 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/JwtResponseDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CreateUserDto.java @@ -3,6 +3,8 @@ package at.tuwien.api.auth; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; +import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import java.util.List; @@ -12,22 +14,26 @@ import java.util.List; @Builder @AllArgsConstructor @NoArgsConstructor -public class JwtResponseDto { +public class CreateUserDto { @NotNull - @ToString.Exclude - private String token; - - private String type; - - private Long id; + @Schema(example = "true") + private Boolean enabled; + @NotBlank @Schema(example = "user") private String username; + @NotBlank + @Email @Schema(example = "user@example.com") private String email; - private List<String> roles; + private String firstName; + + private String lastName; + + @NotNull + private List<CredentialDto> credentials; } diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CredentialDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CredentialDto.java new file mode 100644 index 0000000000000000000000000000000000000000..fe1400d3ab94ffd7a0ce46c571ae99b5f463b7a9 --- /dev/null +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/CredentialDto.java @@ -0,0 +1,29 @@ +package at.tuwien.api.auth; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.*; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Getter +@Setter +@ToString +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CredentialDto { + + @NotBlank + @Schema(example = "password") + private String type; + + @NotBlank + @Schema(example = "abc123") + private String value; + + @NotNull + @Schema(example = "false") + private Boolean temporary; + +} diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java index 7f6f3937bcb251604d9ec979ae8649bceb685d42..3f6784bdd0eef1322ac391408e1e6e88e0b6cb5f 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/SignupRequestDto.java @@ -4,6 +4,7 @@ import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import javax.validation.constraints.Email; +import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; import javax.validation.constraints.Pattern; @@ -15,12 +16,12 @@ import javax.validation.constraints.Pattern; @NoArgsConstructor public class SignupRequestDto { - @NotNull + @NotBlank @Pattern(regexp = "^[a-z0-9]{3,}$") @Schema(example = "user") private String username; - @NotNull + @NotBlank @Email @Schema(example = "user@example.com") private String email; diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenBriefDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenBriefDto.java deleted file mode 100644 index cbb50605403f1009acba86ac22b56d2e335f27e0..0000000000000000000000000000000000000000 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenBriefDto.java +++ /dev/null @@ -1,43 +0,0 @@ -package at.tuwien.api.auth; - -import com.fasterxml.jackson.annotation.JsonFormat; -import com.fasterxml.jackson.annotation.JsonProperty; -import io.swagger.v3.oas.annotations.media.Schema; -import lombok.*; - -import javax.validation.constraints.NotBlank; -import javax.validation.constraints.NotNull; -import java.time.Instant; - -@Getter -@Setter -@ToString -@Builder -@AllArgsConstructor -@NoArgsConstructor -public class TokenBriefDto { - - @NotNull - private Long id; - - @NotBlank - @JsonProperty("token_hash") - @Schema(example = "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03") - private String tokenHash; - - @NotNull - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant created; - - @JsonProperty("last_used") - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant lastUsed; - - @NotNull - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant expires; - -} diff --git a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenDto.java b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenDto.java index 1cf442509c4207dd697b5afa37431a087da65ed0..cf3399dbe9a865c5b741426c40bb854fe2b84acf 100644 --- a/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenDto.java +++ b/fda-metadata-db/api/src/main/java/at/tuwien/api/auth/TokenDto.java @@ -1,13 +1,11 @@ package at.tuwien.api.auth; -import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonProperty; import io.swagger.v3.oas.annotations.media.Schema; import lombok.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; -import java.time.Instant; @Getter @Setter @@ -17,31 +15,44 @@ import java.time.Instant; @NoArgsConstructor public class TokenDto { - @NotNull - private Long id; + @NotBlank + @JsonProperty("access_token") + @Schema(example = "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJBbFdvalRsa1dBSVVoYTJLWjFvOEluWEtNbVAzUTg0STZiMFFHYkR6aEpvIn0.eyJleHAiOjE2ODAyNjgyNjgsImlhdCI6MTY4MDI2ODIwOCwianRpIjoiNjkwNjRlNTQtODNhNS00NGYxLWE3OTItNWFjOWU4OTA5YTlkIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJzdWIiOiI4MjQ2OWMyMS0yYjNjLTRmMDctODg1Yi1hMzViMGQ5YTJhNjYiLCJ0eXAiOiJCZWFyZXIiLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNDQ1ODA3ZWUtMjg3Ni00NjFkLWE4ZjMtNGQyN2IzMGMyMWZhIiwiYWNyIjoiMSIsInNjb3BlIjoicHJvZmlsZSBlbWFpbCIsInNpZCI6IjQ0NTgwN2VlLTI4NzYtNDYxZC1hOGYzLTRkMjdiMzBjMjFmYSIsImVtYWlsX3ZlcmlmaWVkIjpmYWxzZSwicHJlZmVycmVkX3VzZXJuYW1lIjoiZmRhIn0.IOQxqWvlPDV9WuFOeLVG-ayexbK8OqylPABghEMSbMpmNlQhSAjbjaMY31uU-uADZRHB-mC8bmRS5PoWNtanuhz0lORDCeissFsbv0UL9Q42CaxG75vFAAD5WsdIHIr-dtEjEiXYtu-qwdg83griAUeO119TTdgldyPxo4jWzNw0ui6W7r4LqP4fSk31iJfxR5urgs5k6Ctzg-fXCORT31-nKz_YJQwLoPO9j4afX_1mnCXY5qFGMSrmPKzB0CArZfUpa_4nqt4Y768yOC3gigAyCjXtvXKkgCmARPSRjERGDdTb6SGbAwRDiVHVy9wy7XZwOcCFMEra9H7mV0Mx2A") + private String accessToken; @NotBlank - @JsonProperty("token_hash") - @Schema(example = "5891b5b522d5df086d0ff0b110fbd9d21bb4fc7163af34d08286a2e846f6be03") - private String tokenHash; + @JsonProperty("refresh_token") + @Schema(example = "eyJhbGciOiJIUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICI4YTczZGYwZS03NzMwLTRiZDEtOGVhOC1mZTdjZWViNmMxYWMifQ.eyJleHAiOjE2ODAyNzAwMDgsImlhdCI6MTY4MDI2ODIwOCwianRpIjoiNWYyNDIwNDItNmJmZi00ZTQ2LTg2NTAtNDBhY2E3YjVkZjMyIiwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3JlYWxtcy9tYXN0ZXIiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjgwODAvcmVhbG1zL21hc3RlciIsInN1YiI6IjgyNDY5YzIxLTJiM2MtNGYwNy04ODViLWEzNWIwZDlhMmE2NiIsInR5cCI6IlJlZnJlc2giLCJhenAiOiJhZG1pbi1jbGkiLCJzZXNzaW9uX3N0YXRlIjoiNDQ1ODA3ZWUtMjg3Ni00NjFkLWE4ZjMtNGQyN2IzMGMyMWZhIiwic2NvcGUiOiJwcm9maWxlIGVtYWlsIiwic2lkIjoiNDQ1ODA3ZWUtMjg3Ni00NjFkLWE4ZjMtNGQyN2IzMGMyMWZhIn0.-GltWGkIaKUJ4AqRYnGHblTr0ygZm2CsRQB6zz5ePm4") + private String refreshToken; + + @NotBlank + @JsonProperty("token_type") + @Schema(example = "Bearer") + private String tokenType; @NotNull - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant created; + @JsonProperty("expires_in") + @Schema(example = "60") + private Long expiresIn; - @JsonProperty("last_used") - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant lastUsed; + @NotNull + @JsonProperty("session_state") + @Schema(example = "445807ee-2876-461d-a8f3-4d27b30c21fa") + private String sessionState; @NotNull - @Schema(example = "2020-08-04 11:12:00") - @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSXXX", timezone = "UTC") - private Instant expires; + @JsonProperty("scope") + @Schema(example = "profile email") + private String scope; - @NotBlank - @Schema(example = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c") - private String token; + @NotNull + @JsonProperty("refresh_expires_in") + @Schema(example = "1800") + private Long refreshExpiresIn; + + @NotNull + @JsonProperty("not-before-policy") + @Schema(example = "0") + private Long notBeforePolicy; } diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java new file mode 100644 index 0000000000000000000000000000000000000000..fb7010325f5165428e8a09c3ddb4b9e19fc8bcf8 --- /dev/null +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/auth/Realm.java @@ -0,0 +1,31 @@ +package at.tuwien.entities.auth; + +import lombok.*; + +import javax.persistence.*; + +@Data +@Entity +@Builder +@AllArgsConstructor +@NoArgsConstructor +@ToString +@EqualsAndHashCode(onlyExplicitlyIncluded = true) +@Table(name = "realm") +public class Realm { + + @Id + @EqualsAndHashCode.Include + @Column(nullable = false) + private String id; + + @Column(nullable = false) + private Boolean enabled; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String sslRequired; + +} diff --git a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java index abd946bdb9231d74479b880e4eb822b6ae14dad3..29f7222a7159524f446b764964ba361d3fea32d7 100644 --- a/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java +++ b/fda-metadata-db/entities/src/main/java/at/tuwien/entities/user/User.java @@ -36,7 +36,7 @@ public class User { @Column(name = "last_name") private String lastname; - @Column(name = "realm id") + @Column(name = "realm_id") private String realmId; @Column(unique = true, nullable = false) diff --git a/fda-query-service/pom.xml b/fda-query-service/pom.xml index 67fb838c96353c66bd00f0725042783a442a0630..35ec3a29280f9b41b5d1a3170670329ef5185957 100644 --- a/fda-query-service/pom.xml +++ b/fda-query-service/pom.xml @@ -297,9 +297,6 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> -<!-- <configuration>--> -<!-- <outputDirectory>docs</outputDirectory>--> -<!-- </configuration>--> </plugin> <plugin> <groupId>com.soebes.maven.plugins</groupId> diff --git a/fda-user-service/Dockerfile b/fda-user-service/Dockerfile index dd9fe0250e12a17d5bfd81ce0070530badd5385a..a772cacf6ff3420cd7568106ee27c38a346285bb 100644 --- a/fda-user-service/Dockerfile +++ b/fda-user-service/Dockerfile @@ -24,6 +24,9 @@ FROM openjdk:11-jre-slim as runtime ENV METADATA_DB=fda ENV METADATA_USERNAME=root ENV METADATA_PASSWORD=dbrepo +ENV GATEWAY_ENDPOINT=http://gateway-service:9095 +ENV KEYCLOAK_ADMIN=fda +ENV KEYCLOAK_ADMIN_PASSWORD=fda ENV LOG_LEVEL=debug ENV DBREPO_CLIENT_SECRET=client-secret ENV CLIENT_ID=dbrepo-client diff --git a/fda-user-service/pom.xml b/fda-user-service/pom.xml index f06580d159c91afa43a80c0dcb785fcb21b7f434..989dafab4ceaf1fb5bc6dbcdf3027cb8821e8b3d 100644 --- a/fda-user-service/pom.xml +++ b/fda-user-service/pom.xml @@ -118,29 +118,6 @@ <groupId>org.springframework.data</groupId> <artifactId>spring-data-elasticsearch</artifactId> </dependency> - <!-- AMPQ --> - <dependency> - <groupId>com.rabbitmq</groupId> - <artifactId>amqp-client</artifactId> - <version>${rabbit-amqp-client.version}</version> - </dependency> - <!-- Docker --> - <dependency> - <groupId>com.github.docker-java</groupId> - <artifactId>docker-java</artifactId> - <version>${docker.version}</version> - <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> - </dependency> <!-- IDE --> <dependency> <groupId>org.projectlombok</groupId> @@ -237,9 +214,7 @@ <exclude>at/tuwien/handlers/**/*</exclude> <exclude>at/tuwien/exception/**/*</exclude> <exclude>at/tuwien/config/**/*</exclude> - <exclude>**/RabbitMqServiceImpl.class</exclude> - <exclude>**/ServiceSeeder.class</exclude> - <exclude>**/FdaQueryServiceApplication.class</exclude> + <exclude>**/FdaUserServiceApplication.class</exclude> </excludes> </configuration> <executions> @@ -267,9 +242,6 @@ <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-project-info-reports-plugin</artifactId> <version>3.0.0</version> -<!-- <configuration>--> -<!-- <outputDirectory>docs</outputDirectory>--> -<!-- </configuration>--> </plugin> <plugin> <groupId>com.soebes.maven.plugins</groupId> diff --git a/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java index 7bb8dc31490b8468f1a772fda01ffcb4b242ea9d..20c41199280f2df5ac0bee8385707cc46bf0634a 100644 --- a/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java +++ b/fda-user-service/rest-service/src/main/java/at/tuwien/endpoint/UserEndpoint.java @@ -2,6 +2,9 @@ package at.tuwien.endpoint; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.user.UserBriefDto; +import at.tuwien.exception.RealmNotFoundException; +import at.tuwien.exception.RemoteUnavailableException; +import at.tuwien.exception.UserNotFoundException; import at.tuwien.mapper.UserMapper; import at.tuwien.service.UserService; import io.micrometer.core.annotation.Timed; @@ -48,7 +51,8 @@ public class UserEndpoint { @Transactional @Timed(value = "user.create", description = "Time needed to create a user in the metadata database") @Operation(summary = "Create a user") - public ResponseEntity<UserBriefDto> create(SignupRequestDto data) { + public ResponseEntity<?> create(SignupRequestDto data) throws RealmNotFoundException, UserNotFoundException, + RemoteUnavailableException { log.debug("endpoint create a user, data={}", data); final UserBriefDto dto = userMapper.userToUserBriefDto(userService.create(data)); log.trace("create user resulted in dto {}", dto); diff --git a/fda-user-service/rest-service/src/main/resources/application-docker.yml b/fda-user-service/rest-service/src/main/resources/application-docker.yml index 084cc82986478d9fd376495e9f52bf2c93e2f42c..486101dd8e32882c69e4066d1911a030e07be3ce 100644 --- a/fda-user-service/rest-service/src/main/resources/application-docker.yml +++ b/fda-user-service/rest-service/src/main/resources/application-docker.yml @@ -35,6 +35,11 @@ eureka: client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ fda: ready.path: /ready + gateway.endpoint: "${GATEWAY_ENDPOINT}" + keycloak: + endpoint: https://authentication-service:8443/ + username: "${KEYCLOAK_ADMIN}" + password: "${KEYCLOAK_ADMIN_PASSWORD}" jwt: issuer: "${JWT_ISSUER}" public_key: "${JWT_PUBKEY}" \ No newline at end of file diff --git a/fda-user-service/rest-service/src/main/resources/application-local.yml b/fda-user-service/rest-service/src/main/resources/application-local.yml index 4b86aa03a823e192a3525861c367164eee7a000c..c6ac95610eda5541b3765e8e5ca10939c1b53294 100644 --- a/fda-user-service/rest-service/src/main/resources/application-local.yml +++ b/fda-user-service/rest-service/src/main/resources/application-local.yml @@ -32,9 +32,14 @@ logging: org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver: debug eureka: instance.hostname: user-service - client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ + client.serviceUrl.defaultZone: http://localhost:9090/eureka/ fda: - ready.path: /ready + ready.path: ./ready + gateway.endpoint: http://localhost:9095 + keycloak: + endpoint: https://localhost:8443/ + username: fda + password: fda jwt: issuer: http://localhost:8080/realms/dbrepo public_key: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqqnHQ2BWWW9vDNLRCcxD++xZg/16oqMo/c1l+lcFEjjAIJjJp/HqrPYU/U9GvquGE6PbVFtTzW1KcKawOW+FJNOA3CGo8Q1TFEfz43B8rZpKsFbJKvQGVv1Z4HaKPvLUm7iMm8Hv91cLduuoWx6Q3DPe2vg13GKKEZe7UFghF+0T9u8EKzA/XqQ0OiICmsmYPbwvf9N3bCKsB/Y10EYmZRb8IhCoV9mmO5TxgWgiuNeCTtNCv2ePYqL/U0WvyGFW0reasIK8eg3KrAUj8DpyOgPOVBn3lBGf+3KFSYi+0bwZbJZWqbC/Xlk20Go1YfeJPRIt7ImxD27R/lNjgDO/MwIDAQAB \ No newline at end of file diff --git a/fda-user-service/rest-service/src/main/resources/application.yml b/fda-user-service/rest-service/src/main/resources/application.yml index 9ba4e1547a5795546ef952e72819193e32d670c3..5f93e7a6182e1972a150ad884e9385752b4c8c0d 100644 --- a/fda-user-service/rest-service/src/main/resources/application.yml +++ b/fda-user-service/rest-service/src/main/resources/application.yml @@ -35,6 +35,11 @@ eureka: client.serviceUrl.defaultZone: http://discovery-service:9090/eureka/ fda: ready.path: /ready + gateway.endpoint: "${GATEWAY_ENDPOINT}" + keycloak: + endpoint: https://authentication-service:8443/ + username: "${KEYCLOAK_ADMIN}" + password: "${KEYCLOAK_ADMIN_PASSWORD}" jwt: issuer: "${JWT_ISSUER}" public_key: "${JWT_PUBKEY}" diff --git a/fda-user-service/rest-service/src/test/resources/application.properties b/fda-user-service/rest-service/src/test/resources/application.properties index 4eea490a9199d7e0155b1f33cf3500a5e1f2863f..ddba279a3bfcf1940585c1d6cb4c4608cd3f54c9 100644 --- a/fda-user-service/rest-service/src/test/resources/application.properties +++ b/fda-user-service/rest-service/src/test/resources/application.properties @@ -21,11 +21,6 @@ spring.jpa.show-sql=false logging.level.root=error logging.level.at.tuwien.=trace -# broker service -spring.rabbitmq.host=dbrepo-broker-service -spring.rabbitmq.username=guest -spring.rabbitmq.password=guest - # search service fda.consumers=2 fda.gateway.endpoint: http://localhost:15672 diff --git a/fda-user-service/services/src/main/java/at/tuwien/config/GatewayConfig.java b/fda-user-service/services/src/main/java/at/tuwien/config/GatewayConfig.java new file mode 100644 index 0000000000000000000000000000000000000000..540b5d96f4e750a5fdea93f0d2da7a46e7c5c44c --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/config/GatewayConfig.java @@ -0,0 +1,40 @@ +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 GatewayConfig { + + @Value("${fda.gateway.endpoint}") + private String gatewayEndpoint; + + @Value("${fda.keycloak.endpoint}") + private String keycloakEndpoint; + + @Value("${fda.keycloak.username}") + private String keycloakUsername; + + @Value("${fda.keycloak.password}") + private String keycloakPassword; + + @Bean + public RestTemplate gatewayRestTemplate() { + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(gatewayEndpoint)); + return restTemplate; + } + + @Bean + public RestTemplate keycloakRestTemplate() { + final RestTemplate restTemplate = new RestTemplate(); + restTemplate.setUriTemplateHandler(new DefaultUriBuilderFactory(keycloakEndpoint)); + return restTemplate; + } + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java b/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java index 2250fa50884df3f47b0b063975aea74f06203f80..388480c9be24fb50e83699c7fd2f1a92bd39f8d5 100644 --- a/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java +++ b/fda-user-service/services/src/main/java/at/tuwien/config/ReadyConfig.java @@ -1,7 +1,10 @@ package at.tuwien.config; +import at.tuwien.exception.RealmNotFoundException; +import at.tuwien.service.RealmService; import com.google.common.io.Files; import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.context.event.ApplicationReadyEvent; import org.springframework.context.annotation.Configuration; @@ -17,8 +20,16 @@ public class ReadyConfig { @Value("${fda.ready.path}") private String readyPath; + private final RealmService realmService; + + @Autowired + public ReadyConfig(RealmService realmService) { + this.realmService = realmService; + } + @EventListener(ApplicationReadyEvent.class) - public void init() throws IOException { + public void init() throws IOException, RealmNotFoundException { + realmService.update("master"); Files.touch(new File(readyPath)); } diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java new file mode 100644 index 0000000000000000000000000000000000000000..1750cfb525c2947f8f13837b5e89ed7ddc46f8fd --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/exception/RealmNotFoundException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NOT_FOUND) +public class RealmNotFoundException extends Exception { + + public RealmNotFoundException(String msg) { + super(msg); + } + + public RealmNotFoundException(String msg, Throwable thr) { + super(msg, thr); + } + + public RealmNotFoundException(Throwable thr) { + super(thr); + } + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/exception/RemoteUnavailableException.java b/fda-user-service/services/src/main/java/at/tuwien/exception/RemoteUnavailableException.java new file mode 100644 index 0000000000000000000000000000000000000000..3f6700d06b277d59ccbf812136faec277261357d --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/exception/RemoteUnavailableException.java @@ -0,0 +1,21 @@ +package at.tuwien.exception; + +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ResponseStatus; + +@ResponseStatus(code = HttpStatus.NO_CONTENT) +public class RemoteUnavailableException extends Exception { + + public RemoteUnavailableException(String msg) { + super(msg); + } + + public RemoteUnavailableException(String msg, Throwable thr) { + super(msg, thr); + } + + public RemoteUnavailableException(Throwable thr) { + super(thr); + } + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/gateway/AuthenticationServiceGateway.java b/fda-user-service/services/src/main/java/at/tuwien/gateway/AuthenticationServiceGateway.java new file mode 100644 index 0000000000000000000000000000000000000000..3d41955d06f05e0897e2bc27db4c784ed6ad3b86 --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/gateway/AuthenticationServiceGateway.java @@ -0,0 +1,11 @@ +package at.tuwien.gateway; + +import at.tuwien.api.auth.CreateUserDto; +import at.tuwien.api.auth.TokenDto; +import at.tuwien.exception.RemoteUnavailableException; + +public interface AuthenticationServiceGateway { + TokenDto getToken() throws RemoteUnavailableException; + + void createUser(String token, CreateUserDto data) throws RemoteUnavailableException; +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/gateway/impl/AuthenticationServiceGatewayImpl.java b/fda-user-service/services/src/main/java/at/tuwien/gateway/impl/AuthenticationServiceGatewayImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..19e2544e29a596e34296cb6179c29e3f0443783c --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/gateway/impl/AuthenticationServiceGatewayImpl.java @@ -0,0 +1,77 @@ +package at.tuwien.gateway.impl; + +import at.tuwien.api.auth.CreateUserDto; +import at.tuwien.api.auth.TokenDto; +import at.tuwien.config.GatewayConfig; +import at.tuwien.exception.RemoteUnavailableException; +import at.tuwien.gateway.AuthenticationServiceGateway; +import lombok.extern.log4j.Log4j2; +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.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.ResourceAccessException; +import org.springframework.web.client.RestTemplate; + +@Log4j2 +@Service +public class AuthenticationServiceGatewayImpl implements AuthenticationServiceGateway { + + private final RestTemplate restTemplate; + private final GatewayConfig gatewayConfig; + + @Autowired + public AuthenticationServiceGatewayImpl(@Qualifier("keycloakRestTemplate") RestTemplate restTemplate, + GatewayConfig gatewayConfig) { + this.restTemplate = restTemplate; + this.gatewayConfig = gatewayConfig; + } + + @Override + public TokenDto getToken() throws RemoteUnavailableException { + final HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", MediaType.APPLICATION_FORM_URLENCODED.toString()); + final MultiValueMap<String, String> payload = new LinkedMultiValueMap<>(); + payload.add("username", gatewayConfig.getKeycloakUsername()); + payload.add("password", gatewayConfig.getKeycloakPassword()); + payload.add("grant_type", "password"); + payload.add("client_id", "admin-cli"); + final String url = "/realms/master/protocol/openid-connect/token"; + log.debug("call authentication service {}", url); + final ResponseEntity<TokenDto> response; + try { + response = restTemplate.exchange(url, HttpMethod.POST, new HttpEntity<>(payload, headers), TokenDto.class); + } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { + log.error("Failed to obtain admin token: {}", e.getMessage()); + throw new RemoteUnavailableException("Failed to obtain admin token", e); + } + if (response.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { + log.error("Failed to obtain admin token: credentials are invalid"); + throw new RemoteUnavailableException("Failed to obtain admin token: credentials are invalid"); + } + return response.getBody(); + } + + @Override + public void createUser(String token, CreateUserDto data) throws RemoteUnavailableException { + final HttpHeaders headers = new HttpHeaders(); + headers.add("Content-Type", MediaType.APPLICATION_JSON.toString()); + headers.add("Accept", MediaType.APPLICATION_JSON.toString()); + headers.add("Authorization", "Bearer: " + token); + final ResponseEntity<Void> response; + try { + response = restTemplate.exchange("/admin/realms/dbrepo/users", HttpMethod.POST, new HttpEntity<>(data, headers), Void.class); + } catch (ResourceAccessException | HttpServerErrorException.ServiceUnavailable e) { + log.error("Failed to create user: {}", e.getMessage()); + throw new RemoteUnavailableException("Failed to create user", e); + } + if (response.getStatusCode().equals(HttpStatus.UNAUTHORIZED)) { + log.error("Failed to create user: credentials are invalid"); + throw new RemoteUnavailableException("Failed to create user: credentials are invalid"); + } + } + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java index cba134ae89d4f6aeae126b925437bb92bff55d21..3a60fc1c3df990bb9cdf524fe62db5d4eee73c65 100644 --- a/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java +++ b/fda-user-service/services/src/main/java/at/tuwien/mapper/UserMapper.java @@ -1,5 +1,6 @@ package at.tuwien.mapper; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.api.user.GrantedAuthorityDto; import at.tuwien.api.user.UserBriefDto; @@ -21,7 +22,7 @@ public interface UserMapper { UserBriefDto userToUserBriefDto(User data); - User signupRequestDtoToUser(SignupRequestDto data); + CreateUserDto signupRequestDtoToCreateUserDto(SignupRequestDto data); default GrantedAuthority grantedAuthorityDtoToGrantedAuthority(GrantedAuthorityDto data) { final GrantedAuthority authority = new SimpleGrantedAuthority(data.getAuthority()); diff --git a/fda-user-service/services/src/main/java/at/tuwien/repository/elastic/UserIdxRepository.java b/fda-user-service/services/src/main/java/at/tuwien/repository/elastic/UserIdxRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..812cb439f61d9b708aac09589d28b9c39d100d8d --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/repository/elastic/UserIdxRepository.java @@ -0,0 +1,10 @@ +package at.tuwien.repository.elastic; + +import at.tuwien.api.identifier.IdentifierDto; +import at.tuwien.api.user.UserDto; +import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface UserIdxRepository extends ElasticsearchRepository<UserDto, Long> { +} \ No newline at end of file diff --git a/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..db0443c0a78c3c8c2d653edf15901c9a3f2e4686 --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/repository/jpa/RealmRepository.java @@ -0,0 +1,14 @@ +package at.tuwien.repository.jpa; + +import at.tuwien.entities.auth.Realm; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +import java.util.Optional; + +@Repository +public interface RealmRepository extends JpaRepository<Realm, String> { + + Optional<Realm> findByName(String name); + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java b/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java new file mode 100644 index 0000000000000000000000000000000000000000..fc3f15f2880c009c9cbbbe034f36e7ac5fbbd6ae --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/service/RealmService.java @@ -0,0 +1,10 @@ +package at.tuwien.service; + +import at.tuwien.entities.auth.Realm; +import at.tuwien.exception.RealmNotFoundException; + +public interface RealmService { + Realm find(String name) throws RealmNotFoundException; + + Realm update(String name) throws RealmNotFoundException; +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java index d9371a4695a522f4a079dc8cb53ee95309eb7a3b..abf06036cb4eac51fe001e25e938dc90f8b5bf99 100644 --- a/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java +++ b/fda-user-service/services/src/main/java/at/tuwien/service/UserService.java @@ -3,6 +3,8 @@ package at.tuwien.service; import at.tuwien.api.auth.SignupRequestDto; import at.tuwien.entities.container.Container; import at.tuwien.entities.user.User; +import at.tuwien.exception.RealmNotFoundException; +import at.tuwien.exception.RemoteUnavailableException; import at.tuwien.exception.UserNotFoundException; import java.security.Principal; @@ -26,7 +28,7 @@ public interface UserService { */ User findByUsername(String username) throws UserNotFoundException; - User create(SignupRequestDto data); + User create(SignupRequestDto data) throws RealmNotFoundException, RemoteUnavailableException, UserNotFoundException; /** * Finds a user by id. diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java b/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java new file mode 100644 index 0000000000000000000000000000000000000000..249876f68dd6d70830025e69f3c93b4e57d5e09a --- /dev/null +++ b/fda-user-service/services/src/main/java/at/tuwien/service/impl/RealmServiceImpl.java @@ -0,0 +1,43 @@ +package at.tuwien.service.impl; + +import at.tuwien.entities.auth.Realm; +import at.tuwien.exception.RealmNotFoundException; +import at.tuwien.repository.jpa.RealmRepository; +import at.tuwien.service.RealmService; +import lombok.extern.log4j.Log4j2; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Optional; + +@Log4j2 +@Service +public class RealmServiceImpl implements RealmService { + + final RealmRepository realmRepository; + + @Autowired + public RealmServiceImpl(RealmRepository realmRepository) { + this.realmRepository = realmRepository; + } + + @Override + public Realm find(String name) throws RealmNotFoundException { + final Optional<Realm> optional = realmRepository.findByName(name); + if (optional.isEmpty()) { + log.error("Failed to find realm with name '{}'", name); + throw new RealmNotFoundException("Failed to find realm"); + } + return optional.get(); + } + + @Override + public Realm update(String name) throws RealmNotFoundException { + final Realm realm = find("master"); + realm.setSslRequired("NONE"); + final Realm entity = realmRepository.save(realm); + log.info("Disabled SSL for realm with name '{}'", name); + return entity; + } + +} diff --git a/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java index 5578d9186b04dce27aa8f54d826a4bc3334d2a9b..50bbea2634ad74620f38e0edd15fa130f63033be 100644 --- a/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java +++ b/fda-user-service/services/src/main/java/at/tuwien/service/impl/UserServiceImpl.java @@ -1,8 +1,13 @@ package at.tuwien.service.impl; +import at.tuwien.api.auth.CreateUserDto; import at.tuwien.api.auth.SignupRequestDto; +import at.tuwien.api.auth.TokenDto; import at.tuwien.entities.user.User; +import at.tuwien.exception.RealmNotFoundException; +import at.tuwien.exception.RemoteUnavailableException; import at.tuwien.exception.UserNotFoundException; +import at.tuwien.gateway.AuthenticationServiceGateway; import at.tuwien.mapper.UserMapper; import at.tuwien.repository.jpa.UserRepository; import at.tuwien.service.UserService; @@ -12,7 +17,6 @@ import org.springframework.stereotype.Service; import java.util.List; import java.util.Optional; -import java.util.UUID; @Log4j2 @Service @@ -20,11 +24,14 @@ public class UserServiceImpl implements UserService { private final UserMapper userMapper; private final UserRepository userRepository; + private final AuthenticationServiceGateway authenticationServiceGateway; @Autowired - public UserServiceImpl(UserMapper userMapper, UserRepository userRepository) { + public UserServiceImpl(UserMapper userMapper, UserRepository userRepository, + AuthenticationServiceGateway authenticationServiceGateway) { this.userMapper = userMapper; this.userRepository = userRepository; + this.authenticationServiceGateway = authenticationServiceGateway; } @Override @@ -43,14 +50,20 @@ public class UserServiceImpl implements UserService { } @Override - public User create(SignupRequestDto data) { - final User user = userMapper.signupRequestDtoToUser(data); - user.setRealmId("82c39861-d877-4667-a0f3-4daa2ee230e0"); - user.setEmailVerified(false); - user.setId(UUID.randomUUID().toString()); - final User entity = userRepository.save(user); - log.info("Created user with id {}", entity.getId()); - return entity; + public User create(SignupRequestDto data) throws RealmNotFoundException, RemoteUnavailableException, + UserNotFoundException { + final TokenDto dto = authenticationServiceGateway.getToken(); + log.debug("obtained authentication token"); + final CreateUserDto userDto = userMapper.signupRequestDtoToCreateUserDto(data); + authenticationServiceGateway.createUser(dto.getAccessToken(), userDto); + final Optional<User> optional = userRepository.findByUsername(data.getUsername()); + if (optional.isEmpty()) { + /* should never occur */ + throw new UserNotFoundException("User not found with username '" + data.getUsername() + "'"); + } + final User user = optional.get(); + log.info("Created user with id {}", user.getId()); + return user; } @Override diff --git a/server.keystore b/server.keystore new file mode 100644 index 0000000000000000000000000000000000000000..93e5c28b23293910dac23a3e974cb485524a3a17 Binary files /dev/null and b/server.keystore differ