From 181c4daade8c9b4991d1fac12c4a112ef7e7dd45 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 08:52:06 +0100 Subject: [PATCH 01/14] added link to conceptboard for overview --- readme.adoc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/readme.adoc b/readme.adoc index 31bf65d..ebd6680 100644 --- a/readme.adoc +++ b/readme.adoc @@ -9,6 +9,8 @@ Ports: * ServiceA - 8081 * ServiceB - 8082 +// https://app.conceptboard.com/board/fasi-2ncp-d6e1-fuhk-zpge + image::overview.png[overview] From a73c3e823ab660fbe91908d19208e015141baf56 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 09:29:14 +0100 Subject: [PATCH 02/14] change group-id --- ServiceA/pom.xml | 2 +- ServiceB/pom.xml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ServiceA/pom.xml b/ServiceA/pom.xml index a79e56d..0988911 100644 --- a/ServiceA/pom.xml +++ b/ServiceA/pom.xml @@ -8,7 +8,7 @@ 2.6.3 - com.example + de.synyx.cl.oauth.example ServiceA 0.0.1-SNAPSHOT ServiceA diff --git a/ServiceB/pom.xml b/ServiceB/pom.xml index 096406c..05bd028 100644 --- a/ServiceB/pom.xml +++ b/ServiceB/pom.xml @@ -8,7 +8,7 @@ 2.6.3 - com.example + de.synyx.cl.oauth.example ServiceB 0.0.1-SNAPSHOT demo From 09d7b72253f23c12b5e58df21aa98be587559e6f Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 09:29:45 +0100 Subject: [PATCH 03/14] first step to own protocol-provider --- .gitignore | 2 + docker-compose.yml | 1 + keycloak-service-c-protocol-mapper/pom.xml | 62 +++++++++++++ keycloak-service-c-protocol-mapper/readme.md | 7 ++ .../example/keycloak/AttributeMapper.java | 83 ++++++++++++++++++ .../src/main/module/changeProvider.xsl | 30 +++++++ .../src/main/module/module.xml | 13 +++ .../org.keycloak.protocol.ProtocolMapper | 4 + overview.png | Bin 45912 -> 85671 bytes readme.adoc | 1 + 10 files changed, 203 insertions(+) create mode 100755 keycloak-service-c-protocol-mapper/pom.xml create mode 100644 keycloak-service-c-protocol-mapper/readme.md create mode 100644 keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java create mode 100644 keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl create mode 100644 keycloak-service-c-protocol-mapper/src/main/module/module.xml create mode 100644 keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper diff --git a/.gitignore b/.gitignore index 9f11b75..632a353 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .idea/ +*.iml +target/ \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 9eb35e1..8f311f3 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -5,6 +5,7 @@ services: environment: KEYCLOAK_PASSWORD: admin123 KEYCLOAK_USER: admin + DB_VENDOR: h2 KEYCLOAK_LOGLEVEL: INFO ROOT_LOGLEVEL: INFO ports: diff --git a/keycloak-service-c-protocol-mapper/pom.xml b/keycloak-service-c-protocol-mapper/pom.xml new file mode 100755 index 0000000..4d4cdc8 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/pom.xml @@ -0,0 +1,62 @@ + + + 4.0.0 + Keycloak service-c protocol mapper + + de.synyx.cl.oauth.example + keycloak-service-c-protocol-mapper + 0.0.1-SNAPSHOT + jar + + + Provides an SPI Implementation to provide additional + claims for the session. + + + + 16.1.0 + 11 + + + + ${project.artifactId} + + + org.apache.maven.plugins + maven-compiler-plugin + + 7 + 7 + + + + + + + + org.keycloak + keycloak-core + ${keycloak-version} + provided + + + org.keycloak + keycloak-server-spi + ${keycloak-version} + provided + + + org.keycloak + keycloak-server-spi-private + ${keycloak-version} + provided + + + org.keycloak + keycloak-services + ${keycloak-version} + provided + + + diff --git a/keycloak-service-c-protocol-mapper/readme.md b/keycloak-service-c-protocol-mapper/readme.md new file mode 100644 index 0000000..716f136 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/readme.md @@ -0,0 +1,7 @@ + + +```shell +mvn clean verify + +docker cp target/keycloak-service-c-protocol-mapper.jar keycloak:/opt/jboss/keycloak/standalone/deployments/ +``` \ No newline at end of file diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java new file mode 100644 index 0000000..18254d0 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java @@ -0,0 +1,83 @@ +package de.synyx.cl.oauth.example.keycloak; + +import org.keycloak.models.ClientSessionContext; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.ProtocolMapperModel; +import org.keycloak.models.UserSessionModel; +import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper; +import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; +import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; +import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; +import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper; +import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.IDToken; + +import java.util.ArrayList; +import java.util.List; + +/** + * inspired by https://github.com/mschwartau/keycloak-custom-protocol-mapper-example + */ +public class AttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { + + /* + * A config which keycloak uses to display a generic dialog to configure the token. + */ + private static final List configProperties = new ArrayList<>(); + + /* + * The ID of the token mapper. Is public, because we need this id in our data-setup project to + * configure the protocol mapper in keycloak. + */ + public static final String PROVIDER_ID = "oidc-service-c-protocol-mapper"; + + static { + // The builtin protocol mapper let the user define under which claim name (key) + // the protocol mapper writes its value. To display this option in the generic dialog + // in keycloak, execute the following method. + OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties); + // The builtin protocol mapper let the user define for which tokens the protocol mapper + // is executed (access token, id token, user info). To add the config options for the different types + // to the dialog execute the following method. Note that the following method uses the interfaces + // this token mapper implements to decide which options to add to the config. So if this token + // mapper should never be available for some sort of options, e.g. like the id token, just don't + // implement the corresponding interface. + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AttributeMapper.class); + } + + @Override + public String getDisplayCategory() { + return "Token mapper"; + } + + @Override + public String getDisplayType() { + return "Service C Attribute Mapper"; + } + + @Override + public String getHelpText() { + return "Adds information from service c to claim"; + } + + @Override + public List getConfigProperties() { + return configProperties; + } + + @Override + public String getId() { + return PROVIDER_ID; + } + + @Override + protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) { + // adds our data to the token. Uses the parameters like the claim name which were set by the user + // when this protocol mapper was configured in keycloak. Note that the parameters which can + // be configured in keycloak for this protocol mapper were set in the static intializer of this class. + // + // Sets a static "Hello world" string, but we could write a dynamic value like a group attribute here too. + OIDCAttributeMapperHelper.mapClaim(token, mappingModel, "hello world"); + } + +} diff --git a/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl b/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl new file mode 100644 index 0000000..80930e5 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl @@ -0,0 +1,30 @@ + + + + + + + + + + module:de.synyx.cl.oauth.example.keycloak.keycloak-service-c-protocol-mapper + + classpath:${jboss.home.dir}/providers/* + + + + + + + + + + + + diff --git a/keycloak-service-c-protocol-mapper/src/main/module/module.xml b/keycloak-service-c-protocol-mapper/src/main/module/module.xml new file mode 100644 index 0000000..8904403 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/main/module/module.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper new file mode 100644 index 0000000..f1ae0fa --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper @@ -0,0 +1,4 @@ +# the name of this file should not be changed, this is how the Service provider API works. +# +# List here all protocol mappers which should be loaded by keycloak. +de.synyx.cl.oauth.example.keycloak.AttributeMapper diff --git a/overview.png b/overview.png index 6f2264070605ac65d778e8e8e0aecd083dcd81e0..a45f0415d8de98f2401fda878af579c3ea9c9fd6 100644 GIT binary patch literal 85671 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 literal 45912 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 IbxsLQ0O?kiF#rGn diff --git a/readme.adoc b/readme.adoc index ebd6680..fa48453 100644 --- a/readme.adoc +++ b/readme.adoc @@ -8,6 +8,7 @@ Ports: * keycloak - 8080 * ServiceA - 8081 * ServiceB - 8082 +* ServiceC - 8083 // https://app.conceptboard.com/board/fasi-2ncp-d6e1-fuhk-zpge From 779ff7d325a039fe26a5df6198b6222b8d3bf086 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 11:20:27 +0100 Subject: [PATCH 04/14] working with manual steps of mapper config --- docker-compose.yml | 38 +++++++++++++++++-- keycloak-service-c-protocol-mapper/readme.md | 2 + ...pper.java => ExternalAttributeMapper.java} | 16 ++++---- .../src/main/module/changeProvider.xsl | 30 --------------- .../src/main/module/module.xml | 13 ------- .../META-INF/jboss-deployment-structure.xml | 8 ++++ .../org.keycloak.protocol.ProtocolMapper | 2 +- keycloak_config/example-realm.yml | 9 +++++ 8 files changed, 63 insertions(+), 55 deletions(-) rename keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/{AttributeMapper.java => ExternalAttributeMapper.java} (93%) delete mode 100644 keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl delete mode 100644 keycloak-service-c-protocol-mapper/src/main/module/module.xml create mode 100644 keycloak-service-c-protocol-mapper/src/main/resources/META-INF/jboss-deployment-structure.xml rename keycloak-service-c-protocol-mapper/src/main/resources/{META-INF.services => META-INF/services}/org.keycloak.protocol.ProtocolMapper (73%) diff --git a/docker-compose.yml b/docker-compose.yml index 8f311f3..8a0464c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,17 +1,49 @@ services: keycloak: - image: quay.io/keycloak/keycloak:${KEYCLOAK_VERSION} + image: jboss/keycloak:16.1.0 + container_name: keycloak environment: - KEYCLOAK_PASSWORD: admin123 + KEYCLOAK_PASSWORD: admin KEYCLOAK_USER: admin DB_VENDOR: h2 KEYCLOAK_LOGLEVEL: INFO ROOT_LOGLEVEL: INFO + DEBUG: 'true' + DEBUG_PORT: '*:8787' ports: - "8080:8080" - "8787:8787" command: - "-c" - "standalone.xml" - - "-Dkeycloak.profile.feature.upload_scripts=enabled" \ No newline at end of file + - "-Dkeycloak.profile.feature.upload_scripts=enabled" + healthcheck: + test: curl -f http://localhost:9990 || exit 1 + interval: 5s + timeout: 5s + retries: 3 + start_period: 30s + + keycloak-cfg: + image: adorsys/keycloak-config-cli:v4.6.1-16.1.0 + container_name: keycloak-cfg + volumes: + - ./keycloak_config:/config + environment: + KEYCLOAK_URL: http://keycloak:8080/auth + KEYCLOAK_SSL-VERIFY: "true" + KEYCLOAK_USER: admin + KEYCLOAK_PASSWORD: admin + IMPORT_PATH: /config/example-realm.yml + IMPORT_FORCE: "false" + depends_on: + keycloak: + condition: service_healthy + + mailhog: + image: mailhog/mailhog + container_name: mailhog + ports: + - "1025:1025" + - "8025:8025" \ No newline at end of file diff --git a/keycloak-service-c-protocol-mapper/readme.md b/keycloak-service-c-protocol-mapper/readme.md index 716f136..2fb2f08 100644 --- a/keycloak-service-c-protocol-mapper/readme.md +++ b/keycloak-service-c-protocol-mapper/readme.md @@ -4,4 +4,6 @@ mvn clean verify docker cp target/keycloak-service-c-protocol-mapper.jar keycloak:/opt/jboss/keycloak/standalone/deployments/ + +curl -s http://localhost:8080/auth/realms/example/protocol/openid-connect/token -d grant_type=client_credentials -d client_id=ProtocolMapperTestClient -d client_secret=hWOVZkbeBx8Vl1oFp80UIBcdaEXsRGbA ``` \ No newline at end of file diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java similarity index 93% rename from keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java rename to keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index 18254d0..9e6d6c5 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/AttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -18,7 +18,7 @@ /** * inspired by https://github.com/mschwartau/keycloak-custom-protocol-mapper-example */ -public class AttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { +public class ExternalAttributeMapper extends AbstractOIDCProtocolMapper implements OIDCAccessTokenMapper, OIDCIDTokenMapper, UserInfoTokenMapper { /* * A config which keycloak uses to display a generic dialog to configure the token. @@ -42,12 +42,17 @@ public class AttributeMapper extends AbstractOIDCProtocolMapper implements OIDCA // this token mapper implements to decide which options to add to the config. So if this token // mapper should never be available for some sort of options, e.g. like the id token, just don't // implement the corresponding interface. - OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, AttributeMapper.class); + OIDCAttributeMapperHelper.addIncludeInTokensConfig(configProperties, ExternalAttributeMapper.class); + } + + @Override + public List getConfigProperties() { + return configProperties; } @Override public String getDisplayCategory() { - return "Token mapper"; + return TOKEN_MAPPER_CATEGORY; } @Override @@ -60,11 +65,6 @@ public String getHelpText() { return "Adds information from service c to claim"; } - @Override - public List getConfigProperties() { - return configProperties; - } - @Override public String getId() { return PROVIDER_ID; diff --git a/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl b/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl deleted file mode 100644 index 80930e5..0000000 --- a/keycloak-service-c-protocol-mapper/src/main/module/changeProvider.xsl +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - module:de.synyx.cl.oauth.example.keycloak.keycloak-service-c-protocol-mapper - - classpath:${jboss.home.dir}/providers/* - - - - - - - - - - - - diff --git a/keycloak-service-c-protocol-mapper/src/main/module/module.xml b/keycloak-service-c-protocol-mapper/src/main/module/module.xml deleted file mode 100644 index 8904403..0000000 --- a/keycloak-service-c-protocol-mapper/src/main/module/module.xml +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/keycloak-service-c-protocol-mapper/src/main/resources/META-INF/jboss-deployment-structure.xml b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF/jboss-deployment-structure.xml new file mode 100644 index 0000000..7fdc6c4 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF/jboss-deployment-structure.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper similarity index 73% rename from keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper rename to keycloak-service-c-protocol-mapper/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper index f1ae0fa..1fd6192 100644 --- a/keycloak-service-c-protocol-mapper/src/main/resources/META-INF.services/org.keycloak.protocol.ProtocolMapper +++ b/keycloak-service-c-protocol-mapper/src/main/resources/META-INF/services/org.keycloak.protocol.ProtocolMapper @@ -1,4 +1,4 @@ # the name of this file should not be changed, this is how the Service provider API works. # # List here all protocol mappers which should be loaded by keycloak. -de.synyx.cl.oauth.example.keycloak.AttributeMapper +de.synyx.cl.oauth.example.keycloak.ExternalAttributeMapper diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index bc15e1a..82fdd8b 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -24,6 +24,15 @@ clients: webOrigins: - "*" serviceAccountsEnabled: true + - clientId: ProtocolMapperTestClient + name: ProtocolMapperTestClient + clientAuthenticatorType: client-secret + secret: hWOVZkbeBx8Vl1oFp80UIBcdaEXsRGbA + redirectUris: + - "*" + webOrigins: + - "*" + serviceAccountsEnabled: true clientScopes: - name: roles protocolMappers: From 580204253e41d390c741558442645fb12f94b3aa Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 12:19:13 +0100 Subject: [PATCH 05/14] auto-configured working --- keycloak_config/example-realm.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index 82fdd8b..75a6a28 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -33,6 +33,18 @@ clients: webOrigins: - "*" serviceAccountsEnabled: true + protocolMappers: + - id: test protocol mapper id + name: test protocol mapper + protocol: openid-connect + protocolMapper: oidc-service-c-protocol-mapper + config: + claim.name: test_claim + access.token.claim: 'true' + id.token.claim: 'true' + userinfo.token.claim: 'false' + jsonType.label: String + clientScopes: - name: roles protocolMappers: From 7a7e2f58122739f4b4ae3d8c3d1c91087fe5790d Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 12:19:48 +0100 Subject: [PATCH 06/14] typo --- .../cl/oauth/example/keycloak/ExternalAttributeMapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index 9e6d6c5..1351800 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -74,7 +74,7 @@ public String getId() { protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSessionModel userSession, KeycloakSession keycloakSession, ClientSessionContext clientSessionCtx) { // adds our data to the token. Uses the parameters like the claim name which were set by the user // when this protocol mapper was configured in keycloak. Note that the parameters which can - // be configured in keycloak for this protocol mapper were set in the static intializer of this class. + // be configured in keycloak for this protocol mapper were set in the static initializer of this class. // // Sets a static "Hello world" string, but we could write a dynamic value like a group attribute here too. OIDCAttributeMapperHelper.mapClaim(token, mappingModel, "hello world"); From 3879caaa3b462d556ec7ce236767c29bad1c969a Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 13:27:55 +0100 Subject: [PATCH 07/14] response is coming from service c --- ServiceC/.gitignore | 33 ++ .../.mvn/wrapper/MavenWrapperDownloader.java | 117 +++++++ ServiceC/.mvn/wrapper/maven-wrapper.jar | Bin 0 -> 50710 bytes .../.mvn/wrapper/maven-wrapper.properties | 2 + ServiceC/mvnw | 310 ++++++++++++++++++ ServiceC/mvnw.cmd | 182 ++++++++++ ServiceC/pom.xml | 71 ++++ .../service/b/ServiceBApplication.java | 13 + .../service/b/ServiceBController.java | 33 ++ .../service/b/config/AudienceValidator.java | 29 ++ .../b/config/KeycloakRealmRoleConverter.java | 32 ++ .../service/b/config/SecurityConfig.java | 25 ++ ServiceC/src/main/resources/application.yml | 18 + .../service/b/DemoApplicationTests.java | 13 + .../service/b/ResourceServerLiveTest.java | 123 +++++++ keycloak-service-c-protocol-mapper/pom.xml | 4 +- .../keycloak/ExternalAttributeMapper.java | 79 ++++- 17 files changed, 1077 insertions(+), 7 deletions(-) create mode 100644 ServiceC/.gitignore create mode 100644 ServiceC/.mvn/wrapper/MavenWrapperDownloader.java create mode 100644 ServiceC/.mvn/wrapper/maven-wrapper.jar create mode 100644 ServiceC/.mvn/wrapper/maven-wrapper.properties create mode 100755 ServiceC/mvnw create mode 100644 ServiceC/mvnw.cmd create mode 100644 ServiceC/pom.xml create mode 100644 ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java create mode 100644 ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java create mode 100644 ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java create mode 100644 ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java create mode 100644 ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java create mode 100644 ServiceC/src/main/resources/application.yml create mode 100644 ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/DemoApplicationTests.java create mode 100644 ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/ResourceServerLiveTest.java diff --git a/ServiceC/.gitignore b/ServiceC/.gitignore new file mode 100644 index 0000000..549e00a --- /dev/null +++ b/ServiceC/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### 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/ServiceC/.mvn/wrapper/MavenWrapperDownloader.java b/ServiceC/.mvn/wrapper/MavenWrapperDownloader.java new file mode 100644 index 0000000..e76d1f3 --- /dev/null +++ b/ServiceC/.mvn/wrapper/MavenWrapperDownloader.java @@ -0,0 +1,117 @@ +/* + * 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/ServiceC/.mvn/wrapper/maven-wrapper.jar b/ServiceC/.mvn/wrapper/maven-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..2cc7d4a55c0cd0092912bf49ae38b3a9e3fd0054 GIT binary patch literal 50710 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 EfAhh=DF6Tf literal 0 HcmV?d00001 diff --git a/ServiceC/.mvn/wrapper/maven-wrapper.properties b/ServiceC/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..a9f1ef8 --- /dev/null +++ b/ServiceC/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.3/apache-maven-3.8.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/ServiceC/mvnw b/ServiceC/mvnw new file mode 100755 index 0000000..a16b543 --- /dev/null +++ b/ServiceC/mvnw @@ -0,0 +1,310 @@ +#!/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/ServiceC/mvnw.cmd b/ServiceC/mvnw.cmd new file mode 100644 index 0000000..c8d4337 --- /dev/null +++ b/ServiceC/mvnw.cmd @@ -0,0 +1,182 @@ +@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/ServiceC/pom.xml b/ServiceC/pom.xml new file mode 100644 index 0000000..45e5792 --- /dev/null +++ b/ServiceC/pom.xml @@ -0,0 +1,71 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.6.3 + + + de.synyx.cl.oauth.example + ServiceC + 0.0.1-SNAPSHOT + demo + Demo project for Spring Boot + + 11 + + + + org.springframework.boot + spring-boot-starter-oauth2-client + + + org.springframework.boot + spring-boot-starter-oauth2-resource-server + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-starter-web + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + org.springframework.boot + spring-boot-starter-test + test + + + io.rest-assured + rest-assured + test + + + + org.springframework.security.oauth + spring-security-oauth2 + 2.5.1.RELEASE + test + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java new file mode 100644 index 0000000..4609f94 --- /dev/null +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java @@ -0,0 +1,13 @@ +package de.synyx.cl.oauth.examples.service.b; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class ServiceBApplication { + + public static void main(String[] args) { + SpringApplication.run(ServiceBApplication.class, args); + } + +} diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java new file mode 100644 index 0000000..a0f61d8 --- /dev/null +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java @@ -0,0 +1,33 @@ +package de.synyx.cl.oauth.examples.service.b; + +import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.security.Principal; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +@RestController +public class ServiceBController { + + @GetMapping("/api") + public String api() { + return "{\"answer\": 42}"; + } + + @GetMapping("/info") + public Map getUserInfo(Principal principal) { + + Map map = new HashMap<>(); + if (principal instanceof JwtAuthenticationToken) { + JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) principal; + map.put("clientId", jwtToken.getTokenAttributes().get("clientId").toString()); + } + + map.put("name", principal.getName()); + + return Collections.unmodifiableMap(map); + } +} diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java new file mode 100644 index 0000000..60278a5 --- /dev/null +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java @@ -0,0 +1,29 @@ +package de.synyx.cl.oauth.examples.service.b.config; + +import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.OAuth2ErrorCodes; +import org.springframework.security.oauth2.core.OAuth2TokenValidator; +import org.springframework.security.oauth2.core.OAuth2TokenValidatorResult; +import org.springframework.security.oauth2.jwt.Jwt; +import org.springframework.util.Assert; + +import java.util.List; + +public class AudienceValidator implements OAuth2TokenValidator { + + private final String audience; + + AudienceValidator(String audience) { + Assert.hasText(audience, "audience is null or empty"); + this.audience = audience; + } + + public OAuth2TokenValidatorResult validate(Jwt jwt) { + List audiences = jwt.getAudience(); + if (audiences.contains(this.audience)) { + return OAuth2TokenValidatorResult.success(); + } + OAuth2Error err = new OAuth2Error(OAuth2ErrorCodes.INVALID_TOKEN); + return OAuth2TokenValidatorResult.failure(err); + } +} diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java new file mode 100644 index 0000000..408e1b8 --- /dev/null +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java @@ -0,0 +1,32 @@ +package de.synyx.cl.oauth.examples.service.b.config; + +import org.springframework.core.convert.converter.Converter; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.oauth2.jwt.Jwt; + +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +public class KeycloakRealmRoleConverter implements Converter> { + + @Override + public Collection convert(Jwt jwt) { + final Map realmAccess = (Map) jwt.getClaims().get("realm_access"); + + System.out.println(realmAccess); + + for (String role : (List) realmAccess.get("roles")) { + System.out.println("role: " + role); + } + + + return ((List)realmAccess.get("roles")).stream() + .map(roleName -> "ROLE_" + roleName) // prefix to map to a Spring Security "role" + .map(SimpleGrantedAuthority::new) + .collect(Collectors.toList()); + } + +} diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java new file mode 100644 index 0000000..a6a1f5b --- /dev/null +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java @@ -0,0 +1,25 @@ +package de.synyx.cl.oauth.examples.service.b.config; + +import org.springframework.context.annotation.Configuration; +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; + +@Configuration +@EnableWebSecurity +@EnableGlobalMethodSecurity(prePostEnabled = true) +public class SecurityConfig extends WebSecurityConfigurerAdapter { + + private String audience = "test"; +// private String issuer = "http://localhost:8080/auth/realms/example/protocol/openid-connect/certs"; + private String issuer = "http://localhost:8080/auth/realms/example"; + + String jwkSetUri = "https://localhost:8080/auth/realms/example/protocol/openid-connect/certs"; + + @Override + protected void configure(HttpSecurity http) throws Exception { + http.authorizeRequests().anyRequest().authenticated() + .and().oauth2ResourceServer().jwt(); + } +} diff --git a/ServiceC/src/main/resources/application.yml b/ServiceC/src/main/resources/application.yml new file mode 100644 index 0000000..b734a09 --- /dev/null +++ b/ServiceC/src/main/resources/application.yml @@ -0,0 +1,18 @@ + +server: + port: 8083 + +# https://www.baeldung.com/spring-security-oauth-jwt +spring: + security: + oauth2: + resourceserver: + jwt: + issuer-uri: http://localhost:8080/auth/realms/example + jwk-set-uri: http://localhost:8080/auth/realms/example/protocol/openid-connect/certs + +logging: + level: + org.springframework.security: DEBUG + + diff --git a/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/DemoApplicationTests.java b/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/DemoApplicationTests.java new file mode 100644 index 0000000..c6515b7 --- /dev/null +++ b/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/DemoApplicationTests.java @@ -0,0 +1,13 @@ +package de.synyx.cl.oauth.examples.service.b; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class DemoApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/ResourceServerLiveTest.java b/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/ResourceServerLiveTest.java new file mode 100644 index 0000000..313656a --- /dev/null +++ b/ServiceC/src/test/java/de/synyx/cl/oauth/examples/service/b/ResourceServerLiveTest.java @@ -0,0 +1,123 @@ +package de.synyx.cl.oauth.examples.service.b; + +import io.restassured.RestAssured; +import io.restassured.response.Response; +import org.junit.jupiter.api.Test; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.resource.OAuth2ProtectedResourceDetails; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.client.token.grant.password.ResourceOwnerPasswordResourceDetails; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.hamcrest.Matchers.equalTo; +import static org.junit.jupiter.api.Assertions.assertEquals; + +class ResourceServerLiveTest { + + public static final String baseUrl = "http://localhost:8082"; + private final String redirectUrl = "http://localhost:8084/"; + private final String authorizeUrlPattern = "http://localhost:8080/auth/realms/example/protocol/openid-connect/auth?response_type=code&client_id=service_a&scope=%s&redirect_uri=" + redirectUrl; + private final String tokenUrl = "http://localhost:8080/auth/realms/example/protocol/openid-connect/token"; + + + public OAuth2ProtectedResourceDetails details() { + AuthorizationCodeResourceDetails details = new AuthorizationCodeResourceDetails(); + details.setId("client_id"); + details.setClientId("service_a"); + details.setClientSecret("7f7367d1-f394-4a98-af5b-6c11886ff26f"); + details.setAccessTokenUri(tokenUrl); + details.setUserAuthorizationUri(tokenUrl); + return details; + } + + @Test + void testOAuthRestTemplate() { + ResourceOwnerPasswordResourceDetails resource = new ResourceOwnerPasswordResourceDetails(); + resource.setAccessTokenUri(tokenUrl); + resource.setId("service_a"); + resource.setClientId("service_a"); + resource.setClientSecret("7f7367d1-f394-4a98-af5b-6c11886ff26f"); + + resource.setGrantType("client_credentials"); + + resource.setScope(Arrays.asList("openid")); + + OAuth2RestTemplate template = new OAuth2RestTemplate(resource); + System.out.println(" CALLING: " + baseUrl+"/api"); + + String result = template.getForObject(baseUrl+"/api", String.class); + + System.err.println(result); + assertEquals("Hello, Trusted User marissa", result); + } + + @Test + void testRestAssured() { + + Response response = RestAssured.given() + .redirects() + .follow(false) + .formParams("client_id", "service_a", "client_secret", "7f7367d1-f394-4a98-af5b-6c11886ff26f", "grant_type", "client_credentials") + .post("http://localhost:8080/auth/realms/example/protocol/openid-connect/token"); + + assertThat(HttpStatus.OK.value()).isEqualTo(response.getStatusCode()); + + String[] parts = response.getBody().asString().split(":"); + String accessToken = parts[1].split("\"")[1]; + System.out.println("parts:" + accessToken); + + Response api = RestAssured.given() + .redirects().follow(false) + .header(HttpHeaders.ACCEPT, "application/json") + .header(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken) + .get(baseUrl + "/api"); + + api.then() + .statusCode(200) + .body("answer", equalTo(42)); + } + + + + String obtainAccesToken(String scopes) { + Response response = RestAssured.given() + .redirects() + .follow(false) + .get(String.format(authorizeUrlPattern, scopes)); + String authSessionId = response.getCookie("AUTH_SESSION_ID"); + String kcPostAuthenticationUrl = response.asString() + .split("action=\"")[1].split("\"")[0].replace("&", "&"); + + // obtain authentication code and state + response = RestAssured.given() + .redirects() + .follow(false) + .cookie("AUTH_SESSION_ID", authSessionId) + .formParams("client_id", "service_a", "client_secret", "7f7367d1-f394-4a98-af5b-6c11886ff26f", "grant_type", "client_credentials") + .post(kcPostAuthenticationUrl); + assertThat(HttpStatus.FOUND.value()).isEqualTo(response.getStatusCode()); + + // extract authorization code + String location = response.getHeader(HttpHeaders.LOCATION); + String code = location.split("code=")[1].split("&")[0]; + + // get access token + Map params = new HashMap(); + params.put("grant_type", "authorization_code"); + params.put("code", code); + params.put("client_id", "jwtClient"); + params.put("redirect_uri", redirectUrl); + params.put("client_secret", "jwtClientSecret"); + response = RestAssured.given() + .formParams(params) + .post(tokenUrl); + return response.jsonPath() + .getString("access_token"); + } +} diff --git a/keycloak-service-c-protocol-mapper/pom.xml b/keycloak-service-c-protocol-mapper/pom.xml index 4d4cdc8..ae665fe 100755 --- a/keycloak-service-c-protocol-mapper/pom.xml +++ b/keycloak-service-c-protocol-mapper/pom.xml @@ -26,8 +26,8 @@ org.apache.maven.plugins maven-compiler-plugin - 7 - 7 + 11 + 11 diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index 1351800..72aced0 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -1,17 +1,28 @@ package de.synyx.cl.oauth.example.keycloak; -import org.keycloak.models.ClientSessionContext; -import org.keycloak.models.KeycloakSession; -import org.keycloak.models.ProtocolMapperModel; -import org.keycloak.models.UserSessionModel; +import org.keycloak.crypto.AsymmetricSignatureSignerContext; +import org.keycloak.crypto.KeyUse; +import org.keycloak.crypto.KeyWrapper; +import org.keycloak.jose.jws.JWSBuilder; +import org.keycloak.models.*; import org.keycloak.protocol.oidc.mappers.AbstractOIDCProtocolMapper; import org.keycloak.protocol.oidc.mappers.OIDCAccessTokenMapper; import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper; import org.keycloak.protocol.oidc.mappers.OIDCIDTokenMapper; import org.keycloak.protocol.oidc.mappers.UserInfoTokenMapper; import org.keycloak.provider.ProviderConfigProperty; +import org.keycloak.representations.AccessToken; import org.keycloak.representations.IDToken; +import org.keycloak.services.Urls; +import javax.json.Json; +import javax.json.JsonReader; +import java.io.IOException; +import java.io.StringReader; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; import java.util.ArrayList; import java.util.List; @@ -25,6 +36,8 @@ public class ExternalAttributeMapper extends AbstractOIDCProtocolMapper implemen */ private static final List configProperties = new ArrayList<>(); + private static final HttpClient httpClient = HttpClient.newHttpClient(); + /* * The ID of the token mapper. Is public, because we need this id in our data-setup project to * configure the protocol mapper in keycloak. @@ -77,7 +90,63 @@ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSes // be configured in keycloak for this protocol mapper were set in the static initializer of this class. // // Sets a static "Hello world" string, but we could write a dynamic value like a group attribute here too. - OIDCAttributeMapperHelper.mapClaim(token, mappingModel, "hello world"); + String accessToken = getAccessToken(clientSessionCtx.getClientSession().getClient().getId(), keycloakSession); + + + System.out.println("***************************************"); + System.out.println("1:"); + System.out.println(accessToken); + System.out.println("***************************************"); + + KeyWrapper key = keycloakSession.keys().getActiveKey(keycloakSession.getContext().getRealm(), KeyUse.SIG, "RS256"); + String bearerToken = new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(new AsymmetricSignatureSignerContext(key)); + System.out.println("2:"); + System.out.println(bearerToken); + System.out.println("***************************************"); + + String attribute = getExternalAttribute(bearerToken); + + + OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attribute); + } + + private String getExternalAttribute(String token) { + final String url = "http://192.168.178.37:8083/info"; + HttpRequest request = HttpRequest.newBuilder() + .GET() + .uri(URI.create(url)) + .setHeader("User-Agent", ExternalAttributeMapper.class.getSimpleName()) + .setHeader("Authorization", "Bearer " + token) + .build(); + + try { + HttpResponse response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + if (response.statusCode() != 200) { + throw new RuntimeException("request not successful: " + response.statusCode()); + } + System.out.println("response body: " + response.body()); + JsonReader reader = Json.createReader(new StringReader(response.body())); + return reader.readObject().toString(); + } catch (IOException | InterruptedException e) { + e.printStackTrace(); + return e.getMessage(); + } + } + + // https://stackoverflow.com/questions/63742225/how-to-obtain-an-access-token-within-a-keycloak-spi + public String getAccessToken(String userId, KeycloakSession keycloakSession) { + KeycloakContext keycloakContext = keycloakSession.getContext(); + + AccessToken token = new AccessToken(); + token.subject(userId); + token.issuer(Urls.realmIssuer(keycloakContext.getUri().getBaseUri(), keycloakContext.getRealm().getName())); + token.issuedNow(); + token.type("Bearer"); + token.expiration((int) (token.getIat() + 60L)); //Lifetime of 60 seconds + + KeyWrapper key = keycloakSession.keys().getActiveKey(keycloakContext.getRealm(), KeyUse.SIG, "RS256"); + + return new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(new AsymmetricSignatureSignerContext(key)); } } From ddd9e2d08d50a4b0afcdded9c38e31e4a04a59c3 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 14:33:41 +0100 Subject: [PATCH 08/14] improving response a little bit, but still a string and not json --- .../examples/service/b/ServiceBController.java | 2 +- .../example/keycloak/ExternalAttributeMapper.java | 15 +++++++++------ keycloak_config/example-realm.yml | 2 +- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java index a0f61d8..a999fe7 100644 --- a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java +++ b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java @@ -23,7 +23,7 @@ public Map getUserInfo(Principal principal) { Map map = new HashMap<>(); if (principal instanceof JwtAuthenticationToken) { JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) principal; - map.put("clientId", jwtToken.getTokenAttributes().get("clientId").toString()); + map.put("clientId", jwtToken.getTokenAttributes().get("azp").toString()); } map.put("name", principal.getName()); diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index 72aced0..c365e8d 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -16,6 +16,7 @@ import org.keycloak.services.Urls; import javax.json.Json; +import javax.json.JsonObject; import javax.json.JsonReader; import java.io.IOException; import java.io.StringReader; @@ -104,13 +105,13 @@ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSes System.out.println(bearerToken); System.out.println("***************************************"); - String attribute = getExternalAttribute(bearerToken); + JsonObject attribute = getExternalAttribute(bearerToken); OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attribute); } - private String getExternalAttribute(String token) { + private JsonObject getExternalAttribute(String token) { final String url = "http://192.168.178.37:8083/info"; HttpRequest request = HttpRequest.newBuilder() .GET() @@ -124,12 +125,14 @@ private String getExternalAttribute(String token) { if (response.statusCode() != 200) { throw new RuntimeException("request not successful: " + response.statusCode()); } - System.out.println("response body: " + response.body()); - JsonReader reader = Json.createReader(new StringReader(response.body())); - return reader.readObject().toString(); + String body = response.body(); + + System.out.println("response body: " + body); + JsonReader reader = Json.createReader(new StringReader(body)); + return reader.readObject(); } catch (IOException | InterruptedException e) { e.printStackTrace(); - return e.getMessage(); + return Json.createReader(new StringReader("{\"error\": \"unknown error\"}")).readObject(); } } diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index 75a6a28..c382c70 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -43,7 +43,7 @@ clients: access.token.claim: 'true' id.token.claim: 'true' userinfo.token.claim: 'false' - jsonType.label: String + jsonType.label: JSON clientScopes: - name: roles From 710efeddba8a56bae1c7ea7357f698419226adc1 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 14:48:30 +0100 Subject: [PATCH 09/14] fixed copy past error in service c --- .../cl/oauth/examples/service/b/ServiceBController.java | 2 +- .../ServiceBApplication.java => c/ServiceCApplication.java} | 6 +++--- .../ServiceBController.java => c/ServiceCController.java} | 6 +++--- .../examples/service/{b => c}/config/AudienceValidator.java | 0 .../service/{b => c}/config/KeycloakRealmRoleConverter.java | 0 .../examples/service/{b => c}/config/SecurityConfig.java | 0 6 files changed, 7 insertions(+), 7 deletions(-) rename ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/{b/ServiceBApplication.java => c/ServiceCApplication.java} (58%) rename ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/{b/ServiceBController.java => c/ServiceCController.java} (89%) rename ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/{b => c}/config/AudienceValidator.java (100%) rename ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/{b => c}/config/KeycloakRealmRoleConverter.java (100%) rename ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/{b => c}/config/SecurityConfig.java (100%) diff --git a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java index a999fe7..a0f61d8 100644 --- a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java +++ b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java @@ -23,7 +23,7 @@ public Map getUserInfo(Principal principal) { Map map = new HashMap<>(); if (principal instanceof JwtAuthenticationToken) { JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) principal; - map.put("clientId", jwtToken.getTokenAttributes().get("azp").toString()); + map.put("clientId", jwtToken.getTokenAttributes().get("clientId").toString()); } map.put("name", principal.getName()); diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCApplication.java similarity index 58% rename from ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java rename to ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCApplication.java index 4609f94..7e5cdb5 100644 --- a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBApplication.java +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCApplication.java @@ -1,13 +1,13 @@ -package de.synyx.cl.oauth.examples.service.b; +package de.synyx.cl.oauth.examples.service.c; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication -public class ServiceBApplication { +public class ServiceCApplication { public static void main(String[] args) { - SpringApplication.run(ServiceBApplication.class, args); + SpringApplication.run(ServiceCApplication.class, args); } } diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java similarity index 89% rename from ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java rename to ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java index a0f61d8..09ebae2 100644 --- a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java @@ -1,4 +1,4 @@ -package de.synyx.cl.oauth.examples.service.b; +package de.synyx.cl.oauth.examples.service.c; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; @@ -10,7 +10,7 @@ import java.util.Map; @RestController -public class ServiceBController { +public class ServiceCController { @GetMapping("/api") public String api() { @@ -23,7 +23,7 @@ public Map getUserInfo(Principal principal) { Map map = new HashMap<>(); if (principal instanceof JwtAuthenticationToken) { JwtAuthenticationToken jwtToken = (JwtAuthenticationToken) principal; - map.put("clientId", jwtToken.getTokenAttributes().get("clientId").toString()); + map.put("clientId", jwtToken.getTokenAttributes().get("azp").toString()); } map.put("name", principal.getName()); diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/AudienceValidator.java similarity index 100% rename from ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/AudienceValidator.java rename to ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/AudienceValidator.java diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/KeycloakRealmRoleConverter.java similarity index 100% rename from ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/KeycloakRealmRoleConverter.java rename to ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/KeycloakRealmRoleConverter.java diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/SecurityConfig.java similarity index 100% rename from ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/b/config/SecurityConfig.java rename to ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/config/SecurityConfig.java From 29d05a8ed24d1ed52847211381f10fdaee8b7832 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 16:45:01 +0100 Subject: [PATCH 10/14] rights are passed throw keycloak scope --- .../service/c/ServiceCController.java | 8 +++- keycloak-service-c-protocol-mapper/pom.xml | 42 ++++++++++++++++++- .../keycloak/ExternalAttributeMapper.java | 34 +++++++++++---- .../keycloak/ExternalAttributeMapperTest.java | 33 +++++++++++++++ keycloak_config/example-realm.yml | 4 +- 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 keycloak-service-c-protocol-mapper/src/test/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapperTest.java diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java index 09ebae2..67f3be9 100644 --- a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java @@ -13,8 +13,12 @@ public class ServiceCController { @GetMapping("/api") - public String api() { - return "{\"answer\": 42}"; + public Map api() { + Map map = new HashMap<>(); + map.put("rightA", true); + map.put("rightB", false); + map.put("rightC", true); + return map; } @GetMapping("/info") diff --git a/keycloak-service-c-protocol-mapper/pom.xml b/keycloak-service-c-protocol-mapper/pom.xml index ae665fe..5b58592 100755 --- a/keycloak-service-c-protocol-mapper/pom.xml +++ b/keycloak-service-c-protocol-mapper/pom.xml @@ -15,8 +15,10 @@ - 16.1.0 11 + 16.1.0 + 5.8.1 + 3.6.28 @@ -58,5 +60,43 @@ ${keycloak-version} provided + + org.junit.jupiter + junit-jupiter-api + ${junit-platform.version} + test + + + org.junit.jupiter + junit-jupiter-engine + ${junit-platform.version} + test + + + org.assertj + assertj-core + 3.22.0 + test + + + org.hamcrest + hamcrest-library + 2.2 + test + + + + org.mockito + mockito-core + ${mockito.version} + test + + + org.mockito + mockito-junit-jupiter + ${mockito.version} + test + + diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index c365e8d..33ee4cc 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -15,9 +15,7 @@ import org.keycloak.representations.IDToken; import org.keycloak.services.Urls; -import javax.json.Json; -import javax.json.JsonObject; -import javax.json.JsonReader; +import javax.json.*; import java.io.IOException; import java.io.StringReader; import java.net.URI; @@ -25,7 +23,9 @@ import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.ArrayList; +import java.util.HashMap; import java.util.List; +import java.util.Map; /** * inspired by https://github.com/mschwartau/keycloak-custom-protocol-mapper-example @@ -45,11 +45,21 @@ public class ExternalAttributeMapper extends AbstractOIDCProtocolMapper implemen */ public static final String PROVIDER_ID = "oidc-service-c-protocol-mapper"; + private static final String URL_NAME = PROVIDER_ID + ".url"; + static { // The builtin protocol mapper let the user define under which claim name (key) // the protocol mapper writes its value. To display this option in the generic dialog // in keycloak, execute the following method. OIDCAttributeMapperHelper.addTokenClaimNameConfig(configProperties); + + ProviderConfigProperty propertyUrl = new ProviderConfigProperty(); + propertyUrl.setName(URL_NAME); + propertyUrl.setLabel("URL"); + propertyUrl.setType(ProviderConfigProperty.STRING_TYPE); + propertyUrl.setHelpText("URL of the service to retrive information from"); + configProperties.add(propertyUrl); + // The builtin protocol mapper let the user define for which tokens the protocol mapper // is executed (access token, id token, user info). To add the config options for the different types // to the dialog execute the following method. Note that the following method uses the interfaces @@ -105,14 +115,15 @@ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSes System.out.println(bearerToken); System.out.println("***************************************"); - JsonObject attribute = getExternalAttribute(bearerToken); + String url = mappingModel.getConfig().get(URL_NAME); + JsonObject attributes = getExternalAttribute(url, bearerToken); + final Map list = convert(attributes); - OIDCAttributeMapperHelper.mapClaim(token, mappingModel, attribute); + OIDCAttributeMapperHelper.mapClaim(token, mappingModel, list); } - private JsonObject getExternalAttribute(String token) { - final String url = "http://192.168.178.37:8083/info"; + private JsonObject getExternalAttribute(String url, String token) { HttpRequest request = HttpRequest.newBuilder() .GET() .uri(URI.create(url)) @@ -152,4 +163,13 @@ public String getAccessToken(String userId, KeycloakSession keycloakSession) { return new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(new AsymmetricSignatureSignerContext(key)); } + public static Map convert(JsonObject jsonObject) { + Map result = new HashMap<>(); + for(String key: jsonObject.keySet()) { + final JsonValue jsonValue = jsonObject.get(key); + result.put(key, jsonValue.toString().equals("true") ? true : false); + }; + + return result; + } } diff --git a/keycloak-service-c-protocol-mapper/src/test/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapperTest.java b/keycloak-service-c-protocol-mapper/src/test/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapperTest.java new file mode 100644 index 0000000..8e1dbf7 --- /dev/null +++ b/keycloak-service-c-protocol-mapper/src/test/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapperTest.java @@ -0,0 +1,33 @@ +package de.synyx.cl.oauth.example.keycloak; + +import org.junit.jupiter.api.Test; + +import javax.json.*; +import java.io.StringReader; +import java.util.HashMap; +import java.util.Map; + +import static de.synyx.cl.oauth.example.keycloak.ExternalAttributeMapper.convert; +import static org.assertj.core.api.BDDAssertions.then; + +class ExternalAttributeMapperTest { + + @Test + void convertJsonStructure() { + String body = "{ " + + "\"rightA\" : true," + + "\"rightB\" : false " + + "}"; + JsonReader reader = Json.createReader(new StringReader(body)); + + final JsonObject jsonObject = reader.readObject(); + + Map result = convert(jsonObject); + + Map rights = new HashMap<>(); + rights.put("rightA", true); + rights.put("rightB", false); + + then(result).isEqualTo(rights); + } +} \ No newline at end of file diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index c382c70..1d19cd8 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -39,11 +39,13 @@ clients: protocol: openid-connect protocolMapper: oidc-service-c-protocol-mapper config: + multivalued: 'true' claim.name: test_claim access.token.claim: 'true' id.token.claim: 'true' userinfo.token.claim: 'false' - jsonType.label: JSON + jsonType.label: String + oidc-service-c-protocol-mapper.url: http://192.168.178.37:8083/api clientScopes: - name: roles From e4e938d38fb95e4a186d4afe45e80b558ac6040d Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 17:15:21 +0100 Subject: [PATCH 11/14] now the claim is an object --- keycloak_config/example-realm.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index 1d19cd8..07d7c5d 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -39,12 +39,12 @@ clients: protocol: openid-connect protocolMapper: oidc-service-c-protocol-mapper config: - multivalued: 'true' + multivalued: 'false' claim.name: test_claim access.token.claim: 'true' id.token.claim: 'true' userinfo.token.claim: 'false' - jsonType.label: String + jsonType.label: JSON oidc-service-c-protocol-mapper.url: http://192.168.178.37:8083/api clientScopes: From de781cf62cff2170b419a49a10436d6194244e1e Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 20:41:34 +0100 Subject: [PATCH 12/14] print email and id of user on connect --- .../keycloak/ExternalAttributeMapper.java | 4 ++++ keycloak_config/example-realm.yml | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java index 33ee4cc..e24d17d 100644 --- a/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java +++ b/keycloak-service-c-protocol-mapper/src/main/java/de/synyx/cl/oauth/example/keycloak/ExternalAttributeMapper.java @@ -112,6 +112,10 @@ protected void setClaim(IDToken token, ProtocolMapperModel mappingModel, UserSes KeyWrapper key = keycloakSession.keys().getActiveKey(keycloakSession.getContext().getRealm(), KeyUse.SIG, "RS256"); String bearerToken = new JWSBuilder().kid(key.getKid()).type("JWT").jsonContent(token).sign(new AsymmetricSignatureSignerContext(key)); System.out.println("2:"); + System.out.println("loginUserName: " + userSession.getLoginUsername()); + System.out.println("authMethod: " + userSession.getAuthMethod()); + System.out.println("userId: " + userSession.getUser().getId()); + System.out.println("userEmail: " + userSession.getUser().getEmail()); System.out.println(bearerToken); System.out.println("***************************************"); diff --git a/keycloak_config/example-realm.yml b/keycloak_config/example-realm.yml index 07d7c5d..1117cef 100644 --- a/keycloak_config/example-realm.yml +++ b/keycloak_config/example-realm.yml @@ -24,6 +24,20 @@ clients: webOrigins: - "*" serviceAccountsEnabled: true + protocolMappers: + - id: 0a623cae-8396-11ec-b97f-38f3ab2c9eaf + name: test protocol mapper + protocol: openid-connect + protocolMapper: oidc-service-c-protocol-mapper + config: + multivalued: 'false' + claim.name: test_claim + access.token.claim: 'true' + id.token.claim: 'true' + userinfo.token.claim: 'false' + jsonType.label: JSON + oidc-service-c-protocol-mapper.url: http://192.168.178.37:8083/api + - clientId: ProtocolMapperTestClient name: ProtocolMapperTestClient clientAuthenticatorType: client-secret @@ -34,7 +48,7 @@ clients: - "*" serviceAccountsEnabled: true protocolMappers: - - id: test protocol mapper id + - id: 1184dfc8-8396-11ec-a55f-38f3ab2c9eaf name: test protocol mapper protocol: openid-connect protocolMapper: oidc-service-c-protocol-mapper @@ -74,6 +88,7 @@ clientScopes: jsonType.label: String users: - username: test + id: f12d05a8-f8ba-420b-9c3e-320a2facda64 email: test@test.de enabled: true firstName: first test From 5cdcadec77b4a52e6da07da3752260fbaa046552 Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 23:06:54 +0100 Subject: [PATCH 13/14] print request principals --- .../cl/oauth/examples/service/b/ServiceBController.java | 7 ++++++- .../cl/oauth/examples/service/c/ServiceCController.java | 9 ++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java index a0f61d8..5612651 100644 --- a/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java +++ b/ServiceB/src/main/java/de/synyx/cl/oauth/examples/service/b/ServiceBController.java @@ -1,5 +1,7 @@ package de.synyx.cl.oauth.examples.service.b; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -12,8 +14,11 @@ @RestController public class ServiceBController { + private final Logger log = LoggerFactory.getLogger(ServiceBController.class); + @GetMapping("/api") - public String api() { + public String api(Principal principal) { + log.info(principal.toString()); return "{\"answer\": 42}"; } diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java index 67f3be9..cd5231c 100644 --- a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java @@ -1,5 +1,7 @@ package de.synyx.cl.oauth.examples.service.c; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationToken; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @@ -12,8 +14,13 @@ @RestController public class ServiceCController { + private final Logger log = LoggerFactory.getLogger(ServiceCController.class); + @GetMapping("/api") - public Map api() { + public Map api(Principal principal) { + log.info("principal {}", principal.toString()); + log.info("name {}", principal.getName()); + Map map = new HashMap<>(); map.put("rightA", true); map.put("rightB", false); From c05884b2bc1b895c83691d58f18dadc748ea678e Mon Sep 17 00:00:00 2001 From: lange Date: Tue, 1 Feb 2022 23:19:46 +0100 Subject: [PATCH 14/14] added e-mail output --- .../cl/oauth/examples/service/c/ServiceCController.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java index cd5231c..0dfbf4c 100644 --- a/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java +++ b/ServiceC/src/main/java/de/synyx/cl/oauth/examples/service/c/ServiceCController.java @@ -16,10 +16,13 @@ public class ServiceCController { private final Logger log = LoggerFactory.getLogger(ServiceCController.class); + // http://192.168.178.37:8083/api?userEmail=lange%2B2@synyx.de @GetMapping("/api") - public Map api(Principal principal) { + public Map api(String userEmail, Principal principal) { log.info("principal {}", principal.toString()); log.info("name {}", principal.getName()); + log.info("email {}", userEmail); + Map map = new HashMap<>(); map.put("rightA", true);