diff --git a/.github/workflows/build-and-deploy.yml b/.github/workflows/build-and-deploy.yml index c95d2ba..797cbe0 100644 --- a/.github/workflows/build-and-deploy.yml +++ b/.github/workflows/build-and-deploy.yml @@ -36,8 +36,9 @@ jobs: run: ./deploy/build.sh unused ${BRANCH_NAME} env: MDM_ENDPOINT: ${{ secrets.MDM_ENDPOINT }} - MDM_TASK_PASSWORD: ${{ secrets.MDM_TASK_PASSWORD }} - MDM_TASK_USER: ${{ secrets.MDM_TASK_USER }} + FDZ_ISSUER_URI: ${{ secrets.FDZ_ISSUER_URI }} + FDZ_CLIENT_ID: ${{ secrets.FDZ_CLIENT_ID }} + FDZ_CLIENT_SECRET: ${{ secrets.FDZ_CLIENT_SECRET }} - name: Deploy to AWS run: ./deploy/deploy.sh unused ${BRANCH_NAME} env: diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 66890f6..0153e72 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -28,8 +28,9 @@ jobs: run: ./deploy/build.sh unused ${BRANCH_NAME} env: MDM_ENDPOINT: ${{ secrets.MDM_ENDPOINT }} - MDM_TASK_PASSWORD: ${{ secrets.MDM_TASK_PASSWORD }} - MDM_TASK_USER: ${{ secrets.MDM_TASK_USER }} + FDZ_ISSUER_URI: ${{ secrets.FDZ_ISSUER_URI }} + FDZ_CLIENT_ID: ${{ secrets.FDZ_CLIENT_ID }} + FDZ_CLIENT_SECRET: ${{ secrets.FDZ_CLIENT_SECRET }} - name: Report build status via Slack uses: act10ns/slack@v1 if: always() diff --git a/README.md b/README.md index f760c19..e3579f2 100644 --- a/README.md +++ b/README.md @@ -10,8 +10,9 @@ Developers need to have at least `maven` and `docker` on their machines. Current The following environment variables have to be set for running the JUnit test: ```shell MDM_ENDPOINT=https://dev.metadata.fdz.dzhw.eu -MDM_TASK_USER=taskuser -MDM_TASK_PASSWORD=**** (see s3://metadatamanagement-private/sensitive_variables.tf) +FDZ_ISSUER_URI=**** (Path to Identity Provider) +FDZ_CLIENT_ID=**** (see Identity Provider) +FDZ_CLIENT_SECRET=**** (see s3://metadatamanagement-private/sensitive_variables.tf) ``` The docker image can be build with: @@ -21,7 +22,7 @@ mvn -Pdev clean install If you want to run the task against an [MDM] instance running on your local machine, you can run: ```shell -docker run -it --network=host dzhw/report-task java -jar /app/report-task.jar --task.id=dat-gra2005-ds2$ --task.version=1.2.3 --task.onBehalfOf=dataprovider --task.language=de --task.type=DATA_SET_REPORT --mdm.username=${MDM_TASK_USER} --mdm.password=${MDM_TASK_PASSWORD} --mdm.endpoint=http://127.0.0.1:8080 +docker run -it --network=host dzhw/report-task java -jar /app/report-task.jar --task.id=dat-gra2005-ds2$ --task.version=1.2.3 --task.onBehalfOf=dataprovider --task.language=de --task.type=DATA_SET_REPORT --mdm.endpoint=http://127.0.0.1:8080 ``` For further configuration options you should get familiar with [Spring Boot @ConfigurationProperties](https://www.baeldung.com/configuration-properties-in-spring-boot) and have a look into `src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config`. diff --git a/bin/run-docker-task.sh b/bin/run-docker-task.sh index 3513e3d..80848dd 100755 --- a/bin/run-docker-task.sh +++ b/bin/run-docker-task.sh @@ -1,4 +1,4 @@ #!/bin/bash # this script gives an example for running the report generation # in the docker container connecting to localhost -docker run -it --network=host dzhw/report-task java -jar /app/report-task.jar --task.id=dat-gra2005-ds2$ --task.version=1.2.3 --task.onBehalfOf=localuser --task.type=DATASET_REPORT --task.language=de --mdm.username=${MDM_TASK_USER} --mdm.password=${MDM_TASK_PASSWORD} --mdm.endpoint=http://127.0.0.1:8080 +docker run -it --network=host dzhw/report-task java -jar /app/report-task.jar --task.id=dat-gra2005-ds2$ --task.version=1.2.3 --task.onBehalfOf=localuser --task.type=DATASET_REPORT --task.language=de --mdm.endpoint=http://127.0.0.1:8080 diff --git a/pom.xml b/pom.xml index 0ce76fa..f240d3b 100644 --- a/pom.xml +++ b/pom.xml @@ -102,6 +102,10 @@ org.springframework.boot spring-boot-starter-logging + + org.springframework.boot + spring-boot-starter-oauth2-client + org.springframework.boot spring-boot-starter-test diff --git a/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/MdmProperties.java b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/MdmProperties.java index 0faf69c..35ef4f7 100644 --- a/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/MdmProperties.java +++ b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/MdmProperties.java @@ -23,14 +23,4 @@ public class MdmProperties { */ @NotEmpty private String endpoint; - /** - * The username which this task uses to interact with the API. - */ - @NotEmpty - private String username; - /** - * The password which this task uses to interact with the API. - */ - @NotEmpty - private String password; } diff --git a/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/oauth2/client/OAuth2ClientConfig.java b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/oauth2/client/OAuth2ClientConfig.java new file mode 100644 index 0000000..2f6a363 --- /dev/null +++ b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/config/oauth2/client/OAuth2ClientConfig.java @@ -0,0 +1,114 @@ +package eu.dzhw.fdz.metadatamanagement.tasks.reporttask.config.oauth2.client; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.InMemoryOAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProvider; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientProviderBuilder; +import org.springframework.security.oauth2.client.OAuth2AuthorizedClientService; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.ClientRegistrationRepository; +import org.springframework.security.oauth2.client.registration.ClientRegistrations; +import org.springframework.security.oauth2.client.registration.InMemoryClientRegistrationRepository; +import org.springframework.security.oauth2.core.AuthorizationGrantType; + +/** + * Configure and create all beans related to Spring Security OAuth2 Client logic. + */ +@Configuration +public class OAuth2ClientConfig { + + /** + * The client registration which will be used to attempt to connect to the identity provider + * to get an access token. + * + * @param issuerUri The URI of the identity provider (if necessary, with an additional path) + * @param clientId The ID of the Client which will try to authenticate and authorize against + * the identity provider + * @param clientSecret The secret of the Client which will try to authenticate and authorize + * against the identity provider + * @return All information necessary for Spring Security to attempt to authenticate and authorize + * a user against the identity provider + */ + @Bean + public ClientRegistration dzhwClientRegistration( + @Value("${spring.security.oauth2.client.provider.fdz.issuer-uri}") + final String issuerUri, + @Value("${spring.security.oauth2.client.registration.fdz.client-id}") + final String clientId, + @Value("${spring.security.oauth2.client.registration.fdz.client-secret}") + final String clientSecret + ) { + return ClientRegistrations + .fromIssuerLocation(issuerUri) + .registrationId("fdz") + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .clientId(clientId) + .clientSecret(clientSecret) + .build(); + } + + /** + * A Repository which stores the information needed to authenticate and authorize a client + * against the identity provider. + * + * NOTE: Normally this would be added by Spring Boot, but since report-task does not have + * a servlet this needs to be added manually. + * + * @param fdzClientRegistration information about a client registration + * @return a repository which stores client registration info + */ + @Bean + public ClientRegistrationRepository clientRegistrationRepository( + ClientRegistration fdzClientRegistration + ) { + return new InMemoryClientRegistrationRepository(fdzClientRegistration); + } + + /** + * A Service which manages OAuth2 authorized clients. + * + * NOTE: Normally this would be added by Spring Boot, but since report-task does not have + * a servlet this needs to be added manually. + * + * @param clientRegistrationRepository the repository which stores client registration info + * @return the service which will handle managing OAuth2 authorized client(s). + */ + @Bean + public OAuth2AuthorizedClientService authorizedClientService( + ClientRegistrationRepository clientRegistrationRepository + ) { + return new InMemoryOAuth2AuthorizedClientService(clientRegistrationRepository); + } + + /** + * The OAuth2 Client manager which handles the authentication and authorization attempts + * of a client using stored client registration info. + * + * @param clientRegistrationRepository the repository which stores client registration info + * @param authorizedClientService the service which manages Oauth2 authorized clients + * @return a manager which attempts to use the client registration info to authenticate and/or + * authorize an Oauth2 client. + */ + @Bean + public AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager( + ClientRegistrationRepository clientRegistrationRepository, + OAuth2AuthorizedClientService authorizedClientService + ) { + OAuth2AuthorizedClientProvider authorizedClientProvider = + OAuth2AuthorizedClientProviderBuilder.builder() + .clientCredentials() + .build(); + + AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager = + new AuthorizedClientServiceOAuth2AuthorizedClientManager( + clientRegistrationRepository, + authorizedClientService + ); + authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider); + + return authorizedClientManager; + } +} diff --git a/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/mdm/MdmRestClient.java b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/mdm/MdmRestClient.java index 0bf0785..52fcd3b 100644 --- a/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/mdm/MdmRestClient.java +++ b/src/main/java/eu/dzhw/fdz/metadatamanagement/tasks/reporttask/mdm/MdmRestClient.java @@ -2,6 +2,7 @@ import java.net.URI; +import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.web.client.RestTemplateBuilder; import org.springframework.core.io.FileSystemResource; import org.springframework.http.HttpEntity; @@ -10,6 +11,8 @@ import org.springframework.http.HttpStatus; import org.springframework.http.MediaType; import org.springframework.http.ResponseEntity; +import org.springframework.security.oauth2.client.AuthorizedClientServiceOAuth2AuthorizedClientManager; +import org.springframework.security.oauth2.client.OAuth2AuthorizeRequest; import org.springframework.stereotype.Component; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; @@ -40,6 +43,8 @@ public class MdmRestClient { private final FileSystemResource zippedEnglishTemplateDataPackageOverview; + private AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager; + /** * Create the {@link RestTemplate} from the {@link MdmProperties}. * @@ -48,14 +53,20 @@ public class MdmRestClient { * @param zippedGermanTemplateDataSetReport The zipped german data set report template folder. * @param zippedEnglishTemplateDataSetReport The zipped english data set report template folder. */ - public MdmRestClient(MdmProperties mdmProperties, RestTemplateBuilder templateBuilder, + public MdmRestClient( + @Value("${spring.security.oauth2.client.registration.fdz.client-id}") final String clientId, + AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager, + MdmProperties mdmProperties, + RestTemplateBuilder templateBuilder, FileSystemResource zippedGermanTemplateDataSetReport, FileSystemResource zippedEnglishTemplateDataSetReport, FileSystemResource zippedGermanTemplateDataPackageOverview, - FileSystemResource zippedEnglishTemplateDataPackageOverview) { + FileSystemResource zippedEnglishTemplateDataPackageOverview + ) { super(); + this.authorizedClientManager = authorizedClientManager; mdmTemplate = templateBuilder - .basicAuthentication(mdmProperties.getUsername(), mdmProperties.getPassword()) + .defaultHeader("Authorization", generateAuthorizationHeaderValue(clientId)) .rootUri(mdmProperties.getEndpoint()).build(); this.zippedGermanTemplateDataSetReport = zippedGermanTemplateDataSetReport; this.zippedEnglishTemplateDataSetReport = zippedEnglishTemplateDataSetReport; @@ -208,4 +219,23 @@ public void postTaskError(String id, String onBehalfOf, String errorMessage, Tas } } + private String generateAuthorizationHeaderValue(final String principal) { + var request = OAuth2AuthorizeRequest + .withClientRegistrationId("fdz") + .principal(principal) + .build(); + + var client = this.authorizedClientManager + .authorize(request); + if (client == null) { + throw new IllegalStateException("Could not obtain OAuth2 client"); + } + + var token = client.getAccessToken(); + + return String.format( + "Bearer %s", + token.getTokenValue() + ); + } } diff --git a/src/main/resources/application-cloud.yml b/src/main/resources/application-cloud.yml index 168e0e3..247a9cb 100644 --- a/src/main/resources/application-cloud.yml +++ b/src/main/resources/application-cloud.yml @@ -1,4 +1,13 @@ +spring: + security: + oauth2: + client: + provider: + fdz: + issuer-uri: ${FDZ_ISSUER_URI} + registration: + fdz: + client-id: ${FDZ_CLIENT_ID} + client-secret: ${FDZ_CLIENT_SECRET} mdm: endpoint: ${vcap.services.mdm.credentials.endpoint} - username: ${vcap.services.mdm.credentials.username} - password: ${vcap.services.mdm.credentials.password} \ No newline at end of file diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 8e2b880..a163a14 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -1,12 +1,22 @@ +spring: + security: + oauth2: + client: + provider: + fdz: + issuer-uri: ${FDZ_ISSUER_URI} + registration: + fdz: + client-id: ${FDZ_CLIENT_ID} + client-secret: ${FDZ_CLIENT_SECRET} task: latex-input-dir: /app/doc latex-process-working-dir: /app/doc pdf-report: /app/doc/Main.pdf latex-process-command: make + mdm: endpoint: http://localhost:8080 - username: aerjaeklrj - password: kajfja logging: level: root: warn diff --git a/src/test/resources/application.yml b/src/test/resources/application.yml index 5189011..3de5786 100644 --- a/src/test/resources/application.yml +++ b/src/test/resources/application.yml @@ -1,3 +1,14 @@ +spring: + security: + oauth2: + client: + provider: + fdz: + issuer-uri: ${FDZ_ISSUER_URI} + registration: + fdz: + client-id: ${FDZ_CLIENT_ID} + client-secret: ${FDZ_CLIENT_SECRET} task: latex-input-dir: target/test-classes/doc latex-process-working-dir: . @@ -10,8 +21,6 @@ task: type: DATA_SET_REPORT mdm: endpoint: ${MDM_ENDPOINT} - username: ${MDM_TASK_USER} - password: ${MDM_TASK_PASSWORD} logging: level: root: warn