From bc5eaade348d74d93da25c7494975b9aa35cded4 Mon Sep 17 00:00:00 2001 From: pujavs <43700552+pujavs@users.noreply.github.com> Date: Wed, 29 Nov 2023 15:16:00 +0530 Subject: [PATCH] feat(idp-plugin): inbound SAML with Keycloak as SP and external SAML IDP (#6793) * feat(keycloak): module for keycloak identify provider Signed-off-by: pujavs * feat(keycloak): sync with main Signed-off-by: pujavs * feat(keycloak): keycloak idp module wip Signed-off-by: pujavs * feat(keycloak): idp module wip Signed-off-by: pujavs * feat(keycloak): rename kc sub module Signed-off-by: pujavs * feat(keycloak): rename kc broker package Signed-off-by: pujavs * feat(keycloak): kc broker classes Signed-off-by: pujavs * feat(keycloak): kc broker classes Signed-off-by: pujavs * feat(keycloak): idp endpoint wip Signed-off-by: pujavs * feat(config-api): identiy provider creation Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp-plugin wip Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(Config-api): idp plugin changes Signed-off-by: pujavs * feat(config-api): idp plugin dependency changes Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(config-api): idp plugin Signed-off-by: pujavs * feat(idp_plugin): dependency and model changes Signed-off-by: pujavs * fix(jans-linux-setup): schema definition should not contain apostrophe * feat(config-api): dependency changes for json Signed-off-by: pujavs * feat(config-api): isp plugin changes Signed-off-by: pujavs * feat(config-api): idp pom changes and assembly changes Signed-off-by: pujavs * feat(config-api): idp changes for logging level and file name Signed-off-by: pujavs * feat(config-api): keycloak idp plugin Signed-off-by: pujavs * feat(config-api): keycloak idp plugin Signed-off-by: pujavs * feat(config-api): keycloak idp plugin Signed-off-by: pujavs * feat(idp-plugin) - code cleanup Signed-off-by: pujavs * feat(idp-plugin) - dependency changes Signed-off-by: pujavs * feat(idp-plugin) - dependency version for mapstruct and profile changes Signed-off-by: pujavs * feat(idp-plugin) - dependency version for mapstruct and profile changes Signed-off-by: pujavs * feat(idp-plugin): refctor Signed-off-by: pujavs * feat(idp-plugin): sp metadata endpoint Signed-off-by: pujavs * feat(idp-plugin): idp plugin spec and config endpoint Signed-off-by: pujavs * feat(idp-plugin): code cleanup Signed-off-by: pujavs * feat(idp-plugin): code cleanup Signed-off-by: pujavs * feat(config-api): sync with main Signed-off-by: pujavs * feat(idp-plugin): idp plugin code Signed-off-by: pujavs * feat(idp-plugin): idp relam endpoint changes Signed-off-by: pujavs * feat(idp-plugin): idp relam endpoint changes Signed-off-by: pujavs * feat(idp-plugin): sync with main Signed-off-by: pujavs * feat(idp-plugin): resolved review comment Signed-off-by: pujavs * feat(idp-plugin): resolved review comment Signed-off-by: pujavs * feat(idp-plugin): resolved review comment Signed-off-by: pujavs * fix(config-api): fixed review comments wrt to logs Signed-off-by: pujavs * feat(jans-linux-setup): jans-idp configuration --------- Signed-off-by: pujavs Co-authored-by: Mustafa Baser --- .../docs/jans-config-api-swagger.yaml | 23 +- .../plugins/docs/fido2-plugin-swagger.yaml | 1 - .../plugins/docs/idp-plugin-swagger.yaml | 991 ++++++++++++++++++ .../docs/jans-admin-ui-plugin-swagger.yaml | 1 - .../docs/jans-link-plugin-swagger.yaml | 1 - .../plugins/docs/saml-plugin-swagger.yaml | 5 +- .../plugins/docs/scim-plugin-swagger.yaml | 1 - .../plugins/docs/user-mgt-plugin-swagger.yaml | 1 - jans-config-api/plugins/idp-plugin/pom.xml | 411 ++++++++ .../idp-plugin/src/main/assembly/assembly.xml | 47 + .../idp/broker/client/IdpClientFactory.java | 93 ++ .../configuration/IdpAppInitializer.java | 86 ++ .../IdpConfigurationFactory.java | 294 ++++++ .../broker/configuration/KeycloakConfig.java | 48 + .../event/IdpMetadataValidationEvent.java | 11 + .../idp/broker/extensions/IdpExtension.java | 6 + .../form/BrokerIdentityProviderForm.java | 65 ++ .../broker/mapper/IdentityProviderMapper.java | 32 + .../idp/broker/mapper/RealmMapper.java | 32 + .../idp/broker/model/IdentityProvider.java | 337 ++++++ .../keycloak/idp/broker/model/Realm.java | 95 ++ .../model/config/IdpAppConfiguration.java | 225 ++++ .../idp/broker/model/config/IdpConf.java | 70 ++ .../broker/model/config/IdpConfigSource.java | 82 ++ .../idp/broker/rest/IdpApiApplication.java | 51 + .../idp/broker/rest/IdpConfigResource.java | 113 ++ .../idp/broker/rest/IdpRealmResource.java | 175 ++++ .../keycloak/idp/broker/rest/IdpResource.java | 315 ++++++ .../service/IdentityProviderService.java | 393 +++++++ .../idp/broker/service/IdpConfigService.java | 166 +++ .../idp/broker/service/IdpService.java | 247 +++++ .../idp/broker/service/KeycloakService.java | 439 ++++++++ .../idp/broker/service/RealmService.java | 223 ++++ .../idp/broker/service/SamlService.java | 144 +++ .../timer/IdpMetadataValidationTimer.java | 366 +++++++ .../keycloak/idp/broker/util/Constants.java | 74 ++ .../src/main/resources/META-INF/beans.xml | 8 + .../javax.enterprise.inject.spi.Extension | 1 + .../services/javax.ws.rs.ext.Providers | 3 + ...lipse.microprofile.config.spi.ConfigSource | 1 + .../src/main/resources/idp.properties | 9 + .../src/main/resources/quartz.properties | 4 + .../io/jans/configapi/KarateTestRunner.java | 18 + .../io/jans/configapi/TestJenkinsRunner.java | 44 + .../feature/saml/config/saml.feature | 21 + .../saml-trust-relationship.feature | 21 + .../test/resources/karate-config-jenkins.js | 59 ++ .../src/test/resources/karate-config.js | 58 + .../src/test/resources/karate.properties | 5 + .../test/resources/karate_jenkins.properties | 2 + .../src/test/resources/logback-test.xml | 24 + .../src/test/resources/test.properties | 8 + .../src/test/resources/testClient.feature | 13 + .../src/test/resources/token.feature | 45 + jans-config-api/plugins/pom.xml | 1 + .../saml/form/TrustRelationshipForm.java | 2 +- jans-config-api/pom.xml | 20 +- .../default/config-api-test.properties | 2 +- .../profiles/jans-ui.jans.io/test.properties | 2 +- .../test.properties | 2 +- .../profiles/local/test.properties | 2 +- .../main/resources/config-api-rs-protect.json | 138 +++ .../example/idp/config/idp-config.json | 23 + .../example/idp/realm/get-all-relam.json | 705 +++++++++++++ .../get-all-saml-identity-provider.json | 108 ++ .../get-saml-identity-provider-by-inum.json | 34 + .../post-saml-identity-provider.json | 16 + .../trust-idp/put-saml-identity-provider.json | 34 + .../core/model/ValidationStatus.java | 56 + .../service/custom/CustomScriptService.java | 8 +- .../jans_setup/schema/jans_schema.json | 243 ++++- .../setup_app/installers/jans_saml.py | 42 +- .../jans_setup/templates/base.ldif | 10 + .../jans-config-api/dynamic-conf.json | 5 + .../jans-keycloak-link/idp-broker-api/pom.xml | 101 ++ .../src/assembly/dependencies.xml | 17 + .../src/main/assembly/deps-zip.xml | 26 + .../config/JansIdpBrokerConfigSource.java | 134 +++ .../exception/JansIdpBrokerException.java | 27 + .../broker/model/JansIdentityProvider.java | 113 ++ .../rest/KcIdentityBrokerApiApplication.java | 40 + .../rest/KcSAMLIdentityBrokerResource.java | 75 ++ .../service/KcIdentityBrokerService.java | 191 ++++ .../io/jans/kc/idp/broker/util/Constants.java | 46 + .../jans/kc/idp/broker/util/JansDataUtil.java | 109 ++ .../io/jans/kc/idp/broker/util/JansUtil.java | 259 +++++ ...lipse.microprofile.config.spi.ConfigSource | 1 + ...eycloak.storage.UserStorageProviderFactory | 1 + .../resources/jans-kc-idp-broker.properties | 8 + .../src/main/resources/log4j2.xml | 36 + .../jans/idp/keycloak/TestJenkinsRunner.java | 29 + .../templates/jans-saml/jans-idp-config.json | 24 + .../jans-saml/jans-idp-configuration.ldif | 6 + .../jans_setup/templates/jans.properties | 1 + 94 files changed, 8649 insertions(+), 56 deletions(-) create mode 100644 jans-config-api/plugins/docs/idp-plugin-swagger.yaml create mode 100644 jans-config-api/plugins/idp-plugin/pom.xml create mode 100644 jans-config-api/plugins/idp-plugin/src/main/assembly/assembly.xml create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/client/IdpClientFactory.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpAppInitializer.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpConfigurationFactory.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/KeycloakConfig.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/event/IdpMetadataValidationEvent.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/extensions/IdpExtension.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/form/BrokerIdentityProviderForm.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/IdentityProviderMapper.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/RealmMapper.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/IdentityProvider.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/Realm.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpAppConfiguration.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConf.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConfigSource.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpApiApplication.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpConfigResource.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpRealmResource.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpResource.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdentityProviderService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpConfigService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/KeycloakService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/RealmService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/SamlService.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/timer/IdpMetadataValidationTimer.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/util/Constants.java create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/beans.xml create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/idp.properties create mode 100644 jans-config-api/plugins/idp-plugin/src/main/resources/quartz.properties create mode 100644 jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/KarateTestRunner.java create mode 100644 jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/TestJenkinsRunner.java create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/config/saml.feature create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/trust-relationship/saml-trust-relationship.feature create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/karate-config-jenkins.js create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/karate-config.js create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/karate.properties create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/karate_jenkins.properties create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/logback-test.xml create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/test.properties create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/testClient.feature create mode 100644 jans-config-api/plugins/idp-plugin/src/test/resources/token.feature create mode 100644 jans-config-api/server/src/main/resources/example/idp/config/idp-config.json create mode 100644 jans-config-api/server/src/main/resources/example/idp/realm/get-all-relam.json create mode 100644 jans-config-api/server/src/main/resources/example/idp/trust-idp/get-all-saml-identity-provider.json create mode 100644 jans-config-api/server/src/main/resources/example/idp/trust-idp/get-saml-identity-provider-by-inum.json create mode 100644 jans-config-api/server/src/main/resources/example/idp/trust-idp/post-saml-identity-provider.json create mode 100644 jans-config-api/server/src/main/resources/example/idp/trust-idp/put-saml-identity-provider.json create mode 100644 jans-config-api/shared/src/main/java/io/jans/configapi/core/model/ValidationStatus.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/pom.xml create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/assembly/dependencies.xml create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/assembly/deps-zip.xml create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/config/JansIdpBrokerConfigSource.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/exception/JansIdpBrokerException.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/model/JansIdentityProvider.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcIdentityBrokerApiApplication.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcSAMLIdentityBrokerResource.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/service/KcIdentityBrokerService.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/Constants.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansDataUtil.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansUtil.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/jans-kc-idp-broker.properties create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/log4j2.xml create mode 100644 jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/test/java/io/jans/idp/keycloak/TestJenkinsRunner.java create mode 100644 jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-config.json create mode 100644 jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-configuration.ldif diff --git a/jans-config-api/docs/jans-config-api-swagger.yaml b/jans-config-api/docs/jans-config-api-swagger.yaml index 2dda350932e..e0864c74e15 100644 --- a/jans-config-api/docs/jans-config-api-swagger.yaml +++ b/jans-config-api/docs/jans-config-api-swagger.yaml @@ -10,7 +10,6 @@ info: servers: - url: https://jans.local.io description: The Jans server - variables: {} tags: - name: Attribute - name: Default Authentication Method @@ -7519,8 +7518,6 @@ components: properties: dn: type: string - selected: - type: boolean inum: type: string sourceAttribute: @@ -7609,20 +7606,22 @@ components: $ref: '#/components/schemas/AttributeValidation' tooltip: type: string - userCanEdit: - type: boolean adminCanView: type: boolean - userCanView: - type: boolean adminCanEdit: type: boolean - userCanAccess: + userCanEdit: + type: boolean + userCanView: type: boolean adminCanAccess: type: boolean + userCanAccess: + type: boolean whitePagesCanView: type: boolean + selected: + type: boolean baseDn: type: string PatchRequest: @@ -8422,6 +8421,8 @@ components: type: boolean skipAuthenticationFilterOptionsMethod: type: boolean + fapi: + type: boolean allResponseTypesSupported: uniqueItems: true type: array @@ -8431,8 +8432,6 @@ components: - code - token - id_token - fapi: - type: boolean AuthenticationFilter: required: - baseDn @@ -9767,8 +9766,6 @@ components: creationDate: type: string format: date-time - persisted: - type: boolean user: $ref: '#/components/schemas/User' ttl: @@ -9776,6 +9773,8 @@ components: format: int32 opbrowserState: type: string + persisted: + type: boolean SessionIdAccessMap: type: object properties: diff --git a/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml b/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml index 608c679f6e6..f3c754c3754 100644 --- a/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/fido2-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: Fido2 - Configuration paths: diff --git a/jans-config-api/plugins/docs/idp-plugin-swagger.yaml b/jans-config-api/plugins/docs/idp-plugin-swagger.yaml new file mode 100644 index 00000000000..7a29b6a66fc --- /dev/null +++ b/jans-config-api/plugins/docs/idp-plugin-swagger.yaml @@ -0,0 +1,991 @@ +openapi: 3.0.1 +info: + title: Jans Config API + contact: + name: Contact + url: https://github.com/JanssenProject/jans/discussions + license: + name: Apache 2.0 + url: https://github.com/JanssenProject/jans/blob/main/LICENSE + version: OAS Version +servers: +- url: https://jans.io/ + description: The Jans server +tags: +- name: Jans - SAML Identity Broker Configuration +- name: Jans - SAML Identity Broker +- name: Jans - SAML Identity Broker Realm +paths: + /idp/idp-config: + get: + tags: + - Jans - SAML Identity Broker Configuration + summary: Gets IDP configuration properties + description: Gets IDP configuration properties + operationId: get-idp-config-properties + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/IdpAppConfiguration' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/config.readonly + put: + tags: + - Jans - SAML Identity Broker Configuration + summary: Update IDP configuration properties + description: Update IDP configuration properties + operationId: put-idp-properties + requestBody: + description: GluuAttribute object + content: + application/json: + schema: + $ref: '#/components/schemas/IdpAppConfiguration' + examples: + Request example: + description: Request example + value: | + { + "applicationName": "idp", + "trustedIdpDn": "ou=trusted-idp,o=jans", + "enabled": true, + "serverUrl": "http://localhost:8180", + "realm": "master", + "clientId": "my-client-1", + "clientSecret": "aqOMI7DhNxCFbW0IieBHSrdA6HMTwxiQ", + "grantType": "PASSWORD", + "username": "admin1", + "password": "admin123", + "idpRootDir": "/opt/jans/idp/", + "idpMetadataRootDir": "/opt/idp/configs/keycloak/idp/metadata", + "idpMetadataTempDir": "/opt/idp/configs/keycloak/idp/temp_metadata", + "idpMetadataFilePattern": "%s-idp-metadata.xml", + "idpMetadataFile": "idp-metadata.xml", + "spMetadataUrl": "/realms/%s/broker/%s/endpoint/descriptor", + "spMetadataRootDir": "/opt/idp/configs/keycloak/sp/metadata", + "spMetadataTempDir": "/opt/idp/configs/keycloak/sp/temp_metadata", + "spMetadataFilePattern": "%s-sp-metadata.xml", + "spMetadataFile": "sp-metadata.xml", + "ignoreValidation": false + } + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/IdpAppConfiguration' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/config.write + patch: + tags: + - Jans - SAML Identity Broker Configuration + summary: Partially modifies IDP configuration properties. + description: Partially modifies IDP Configuration properties. + operationId: patch-idp-properties + requestBody: + description: String representing patch-document. + content: + application/json-patch+json: + schema: + type: array + items: + $ref: '#/components/schemas/JsonPatch' + examples: + Request json example: + description: Request json example + value: "" + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/IdpAppConfiguration' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/config.write + /idp/realm: + get: + tags: + - Jans - SAML Identity Broker Realm + summary: Get all realm + description: Get all realm + operationId: get-realm + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Realm' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.readonly + put: + tags: + - Jans - SAML Identity Broker Realm + summary: Update realm + description: Update realm + operationId: put-realm + requestBody: + description: Realm details + content: + application/json: + schema: + $ref: '#/components/schemas/Realm' + examples: + Request example: + description: Request example + value: "" + responses: + "200": + description: Updated Jans realm object + content: + application/json: + schema: + $ref: '#/components/schemas/Realm' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.write + post: + tags: + - Jans - SAML Identity Broker Realm + summary: Create realm + description: Create realm + operationId: post-realm + requestBody: + description: Realm details + content: + application/json: + schema: + $ref: '#/components/schemas/Realm' + examples: + Request example: + description: Request example + value: "" + responses: + "201": + description: Newly created realm + content: + application/json: + schema: + $ref: '#/components/schemas/Realm' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.write + /idp/realm/{name}: + delete: + tags: + - Jans - SAML Identity Broker Realm + summary: 'Delete realm ' + description: Delete realm + operationId: delete-realm + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.write + /idp/realm/{inum}: + get: + tags: + - Jans - SAML Identity Broker Realm + summary: Get realm by inum + description: Get realm by inum + operationId: get-realm-by-inum + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/Realm' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.readonly + /idp/realm/name/{name}: + get: + tags: + - Jans - SAML Identity Broker Realm + summary: Get realm by name + description: Get realm by name + operationId: get-realm-by-name + parameters: + - name: name + in: path + description: name + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + type: array + items: + $ref: '#/components/schemas/Realm' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/realm.readonly + /idp/kc/saml/upload: + put: + tags: + - Jans - SAML Identity Broker + summary: Update SAML Identity Provider + description: Update SAML Identity Provider + operationId: put-saml-identity-provider + requestBody: + description: String representing patch-document. + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/BrokerIdentityProviderForm' + examples: + Response json example: + description: Response json example + value: | + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Updated Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API Updated", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + } + responses: + "200": + description: Updated Trust IDP + content: + application/json-patch+json: + schema: + $ref: '#/components/schemas/IdentityProvider' + examples: + Response json example: + description: Response json example + value: | + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + } + "400": + description: Bad Request + "401": + description: Unauthorized + "404": + description: Not Found + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.write + post: + tags: + - Jans - SAML Identity Broker + summary: Create SAML Identity Provider + description: Create SAML Identity Provider + operationId: post-saml-identity-provider + requestBody: + description: String representing patch-document. + content: + multipart/form-data: + schema: + $ref: '#/components/schemas/BrokerIdentityProviderForm' + examples: + Response json example: + description: Response json example + value: "{\n\t\"creatorId\": \"jans-admin-2\",\n\t\"name\": \"SAML_IDP_104\"\ + ,\n\t\"displayName\": \"Test SAML IDP 104\",\t\"description\": \"\ + SAML IDP 104 created using Rest API\",\n\t\"realm\": \"keycloak-internal-identity\"\ + ,\n\t\"enabled\": true,\n\t\"providerId\": \"SAML_IDP_104\",\n\t\ + \"trustEmail\": false,\n\t\"storeToken\": false,\n\t\"addReadTokenRoleOnCreate\"\ + : false,\n\t\"authenticateByDefault\": false,\n\t\"linkOnly\": false,\n\ + \t\"firstBrokerLoginFlowAlias\": null,\n\t\"postBrokerLoginFlowAlias\"\ + : null\n\t\n}\n" + responses: + "201": + description: Newly created Trust IDP + content: + application/json-patch+json: + schema: + $ref: '#/components/schemas/IdentityProvider' + examples: + Response json example: + description: Response json example + value: | + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + } + "400": + description: Bad Request + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.write + /idp/kc/saml/{inum}: + get: + tags: + - Jans - SAML Identity Broker + summary: Get SAML Identity Provider by Inum + description: Get SAML Identity Provider by Inum + operationId: get-saml-identity-provider-by-inum + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProvider' + examples: + Response json example: + description: Response json example + value: | + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + } + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.readonly + delete: + tags: + - Jans - SAML Identity Broker + summary: Delete SAML Identity Provider + description: Delete SAML Identity Provider + operationId: delete-saml-identity-provider + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "204": + description: No Content + "401": + description: Unauthorized + "404": + description: Not Found + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.delete + /idp/kc/saml: + get: + tags: + - Jans - SAML Identity Broker + summary: Retrieves SAML Identity Provider + description: Retrieves SAML Identity Provider + operationId: get-saml-identity-provider + parameters: + - name: limit + in: query + description: Search size - max size of the results to return + schema: + type: integer + format: int32 + default: 50 + - name: pattern + in: query + description: Search pattern + schema: + type: string + default: "" + - name: startIndex + in: query + description: The 1-based index of the first query result + schema: + type: integer + format: int32 + default: 0 + - name: sortBy + in: query + description: Attribute whose value will be used to order the returned response + schema: + type: string + default: inum + - name: sortOrder + in: query + description: Order in which the sortBy param is applied. Allowed values are + "ascending" and "descending" + schema: + type: string + default: ascending + - name: fieldValuePair + in: query + description: Field and value pair for seraching + schema: + type: string + default: "" + examples: + Field value example: + description: Field value example + value: "applicationType=web,persistClientAuthorizations=true" + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/IdentityProviderPagedResult' + examples: + Response json example: + description: Response json example + value: | + { + "start": 0, + "totalEntriesCount": 3, + "entriesCount": 3, + "entries": [ + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + }, + { + "dn": "inum=8254fc20-5322-41e0-b398-7c8b2f86f5e5,ou=trusted-idp,o=jans", + "inum": "8254fc20-5322-41e0-b398-7c8b2f86f5e5", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_102", + "displayName": "Test SAML IDP 101", + "description": "SAML IDP 101 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=8254fc20-5322-41e0-b398-7c8b2f86f5e5,ou=trusted-idp,o=jans" + }, + { + "dn": "inum=ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c,ou=trusted-idp,o=jans", + "inum": "ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c,ou=trusted-idp,o=jans" + } + ] + } + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.readonly + /idp/kc/saml/sp-metadata-file/{inum}: + get: + tags: + - Jans - SAML Identity Broker + summary: Get SAML SP Metadata Endpoint URL + description: Get SAML SP Metadata Endpoint URL + operationId: get-saml-sp-metadata-url + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + type: string + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.readonly + /idp/kc/saml/sp-metadata/{inum}: + get: + tags: + - Jans - SAML Identity Broker + summary: Get SAML SP Metadata as Json + description: Get SAML SP Metadata as Json + operationId: get-saml-sp-metadata-json + parameters: + - name: inum + in: path + description: Unique identifier + required: true + schema: + type: string + responses: + "200": + description: Ok + content: + application/json: + schema: + $ref: '#/components/schemas/JsonNode' + "401": + description: Unauthorized + "500": + description: InternalServerError + security: + - oauth2: + - https://jans.io/idp/saml.readonly +components: + schemas: + IdpAppConfiguration: + type: object + properties: + applicationName: + type: string + trustedIdpDn: + type: string + realmDn: + type: string + enabled: + type: boolean + serverUrl: + type: string + realm: + type: string + clientId: + type: string + clientSecret: + type: string + grantType: + type: string + username: + type: string + password: + type: string + idpRootDir: + type: string + idpMetadataRootDir: + type: string + idpMetadataTempDir: + type: string + idpMetadataFilePattern: + type: string + idpMetadataFile: + type: string + spMetadataUrl: + type: string + spMetadataRootDir: + type: string + spMetadataTempDir: + type: string + spMetadataFilePattern: + type: string + spMetadataFile: + type: string + ignoreValidation: + type: boolean + JsonPatch: + type: object + Realm: + required: + - displayName + - name + type: object + properties: + dn: + type: string + inum: + type: string + name: + type: string + displayName: + maxLength: 60 + minLength: 0 + type: string + enabled: + type: boolean + baseDn: + type: string + BrokerIdentityProviderForm: + required: + - identityProvider + type: object + properties: + identityProvider: + $ref: '#/components/schemas/IdentityProvider' + metaDataFile: + type: object + IdentityProvider: + required: + - creatorId + - description + - displayName + - name + - realm + type: object + properties: + dn: + type: string + inum: + type: string + creatorId: + type: string + name: + type: string + displayName: + maxLength: 60 + minLength: 0 + type: string + description: + maxLength: 500 + minLength: 0 + type: string + realm: + type: string + enabled: + type: boolean + providerId: + type: string + trustEmail: + type: boolean + storeToken: + type: boolean + addReadTokenRoleOnCreate: + type: boolean + authenticateByDefault: + type: boolean + linkOnly: + type: boolean + firstBrokerLoginFlowAlias: + type: string + postBrokerLoginFlowAlias: + type: string + spMetaDataURL: + type: string + spMetaDataLocation: + type: string + idpMetaDataURL: + type: string + idpMetaDataLocation: + type: string + status: + type: string + enum: + - active + - inactive + - expired + - register + validationStatus: + type: string + enum: + - In Progress + - Success + - Scheduled + - Failed + validationLog: + type: array + items: + type: string + config: + type: object + additionalProperties: + type: string + baseDn: + type: string + IdentityProviderPagedResult: + type: object + properties: + start: + type: integer + format: int32 + totalEntriesCount: + type: integer + format: int32 + entriesCount: + type: integer + format: int32 + entries: + type: array + items: + $ref: '#/components/schemas/IdentityProvider' + JsonNode: + type: object + securitySchemes: + oauth2: + type: oauth2 + flows: + clientCredentials: + tokenUrl: "https://{op-hostname}/.../token" + scopes: + https://jans.io/idp/config.readonly: View Jans Identity Broker config + related information + https://jans.io/idp/config.write: Manage Jans Identity Broker config related + information + https://jans.io/idp/realm.readonly: View Identity Broker realm related + information + https://jans.io/idp/realm.write: Manage Identity Broker realm related + information + https://jans.io/idp/saml.readonly: View Identity Broker SAML Identity + Broker related information + https://jans.io/idp/saml.write: Manage Identity Broker SAML Identity Broker + related information diff --git a/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml b/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml index 92a49bf547e..0291fe0082b 100644 --- a/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/jans-admin-ui-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: Admin UI - Role - name: Admin UI - Permission diff --git a/jans-config-api/plugins/docs/jans-link-plugin-swagger.yaml b/jans-config-api/plugins/docs/jans-link-plugin-swagger.yaml index 0adee4eed44..3e384a86dfe 100644 --- a/jans-config-api/plugins/docs/jans-link-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/jans-link-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: Jans Link - Configuration paths: diff --git a/jans-config-api/plugins/docs/saml-plugin-swagger.yaml b/jans-config-api/plugins/docs/saml-plugin-swagger.yaml index 0ff985a1e13..07b52073e02 100644 --- a/jans-config-api/plugins/docs/saml-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/saml-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: SAML - Configuration - name: SAML - Trust Relationship @@ -438,15 +437,13 @@ components: type: string TrustRelationshipForm: required: - - metaDataFile - trustRelationship type: object properties: trustRelationship: $ref: '#/components/schemas/TrustRelationship' metaDataFile: - type: string - format: binary + type: object securitySchemes: oauth2: type: oauth2 diff --git a/jans-config-api/plugins/docs/scim-plugin-swagger.yaml b/jans-config-api/plugins/docs/scim-plugin-swagger.yaml index daadeebcccc..53c64e2dba1 100644 --- a/jans-config-api/plugins/docs/scim-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/scim-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: SCIM - Config Management paths: diff --git a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml index 15718369383..babb2ac7bbc 100644 --- a/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml +++ b/jans-config-api/plugins/docs/user-mgt-plugin-swagger.yaml @@ -12,7 +12,6 @@ info: servers: - url: https://jans.io/ description: The Jans server - variables: {} tags: - name: Configuration – User Management paths: diff --git a/jans-config-api/plugins/idp-plugin/pom.xml b/jans-config-api/plugins/idp-plugin/pom.xml new file mode 100644 index 00000000000..1a49cddbaf4 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/pom.xml @@ -0,0 +1,411 @@ + + + + plugins + io.jans.jans-config-api.plugins + 1.0.21-SNAPSHOT + + + 4.0.0 + idp-plugin + + + 4.4.14 + 4.5.13 + ${project.version} + 3.4.6 + 21.1.1 + 22.0.5 + 1.5.3.Final + + + + + + + io.jans + jans-config-api-shared + ${jans.version} + + + + io.jans + jans-config-api-server + + ${jans.version} + + + io.jans + + jans-orm-annotation + ${jans.version} + + + + io.jans + jans-core-document-store + + ${jans.version} + + + io.jans + + jans-core-saml + ${jans.version} + + + + + org.quartz-scheduler + quartz + + + + + + io.smallrye + smallrye-config + 1.5.0 + + + + + + org.keycloak + keycloak-admin-client-jakarta + ${keycloak.version} + + + + org.keycloak + keycloak-core + ${keycloak.version} + + + + org.keycloak + keycloak-common + ${keycloak.version} + + + + + + + + org.opensaml + opensaml-saml-api + ${saml.version} + + + org.opensaml + opensaml-xmlsec-api + ${saml.version} + + + org.opensaml + opensaml-core + 4.0.1 + + + org.opensaml + opensaml-security-api + ${saml.version} + + + net.shibboleth.utilities + java-support + 7.5.2 + + + + + + commons-collections + commons-collections + + + + org.apache.httpcomponents + httpclient + + + + org.apache.httpcomponents + httpcore + + + + org.apache.httpcomponents + httpcore-nio + + ${httpcore.version} + + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + + + + + jakarta.enterprise + jakarta.enterprise.cdi-api + + + + jakarta.inject + jakarta.inject-api + + + + jakarta.validation + jakarta.validation-api + + + + jakarta.ws.rs + jakarta.ws.rs-api + + + + org.jboss.resteasy + resteasy-multipart-provider + + ${resteasy.version} + + + + + + org.apache.james + apache-mime4j-dom + + + + org.apache.james + apache-mime4j-storage + + + + org.apache.james + apache-mime4j-core + + + + + + + io.rest-assured + rest-assured + test + + + + com.intuit.karate + karate-junit5 + test + + + + com.intuit.karate + karate-apache + test + + + + org.junit.jupiter + junit-jupiter-api + test + + + + org.junit.jupiter + junit-jupiter-engine + test + + + + net.masterthought + cucumber-reporting + test + + + + + + io.swagger.core.v3 + swagger-core-jakarta + + + + + + + + + + ../../profiles/${cfg}/config-build.properties + + ../../profiles/${cfg}/config-api-test.properties + + + + + + src/test/resources + true + + + karate.properties + karate_jenkins.properties + + test.properties + *.* + + + + + + + + src/main/resources + true + + **/*.xml + + **/*.properties + **/*.json + + META-INF/services/*.* + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + + + + package + + single + + + + + src/main/assembly/assembly.xml + + + + + + + + + maven-surefire-plugin + + + + integration + + --tags + ~@ignore + + + + + + integration-tests + integration-test + + test + + + + false + !integration + + integration + + + + + + + + org.apache.maven.plugins + maven-resources-plugin + + + + deploy-to-local-folder + package + + + copy-resources + + + + ../target/plugins + + + + ${project.build.directory} + + *-distribution.jar + false + + + + + + + + + + + io.swagger.core.v3 + swagger-maven-plugin-jakarta + + ${swagger-maven-plugin-jakarta} + + + + true + + idp-plugin-swagger + + ${project.artifactId} + true + + + io.jans.configapi.plugin.keycloak.idp.broker.rest + + + + + + + + io.swagger.core.v3 + swagger-models-jakarta + + ${swagger-models-jakarta} + + + + + + + + \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/assembly/assembly.xml b/jans-config-api/plugins/idp-plugin/src/main/assembly/assembly.xml new file mode 100644 index 00000000000..c4333c12580 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/assembly/assembly.xml @@ -0,0 +1,47 @@ + + + distribution + + jar + + false + + + true + / + false + + io.jans:jans-core-saml + org.jboss.resteasy:resteasy-multipart-provider + org.apache.james:apache-mime4j-dom + org.apache.james:apache-mime4j-storage + org.apache.james:apache-mime4j-core + org.opensaml:opensaml-saml-api + org.opensaml:xmltooling + org.opensaml:opensaml-xmlsec-api + org.opensaml:opensaml-core + net.shibboleth.utilities:java-support + keycloak-services:mapstruct + org.keycloak:keycloak-admin-client-jakarta + org.keycloak:keycloak-core + org.keycloak:keycloak-common + + + + runtime + + + + + ${project.build.directory}/classes + / + + **/* + + + + \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/client/IdpClientFactory.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/client/IdpClientFactory.java new file mode 100644 index 00000000000..d81f822880e --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/client/IdpClientFactory.java @@ -0,0 +1,93 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.client; + +import static io.jans.as.model.util.Util.escapeLog; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.JsonNode; + +import io.jans.configapi.core.util.Jackson; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.ws.rs.client.ClientBuilder; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.client.Invocation.Builder; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.microprofile.rest.client.annotation.RegisterProvider; +import org.jboss.resteasy.client.jaxrs.ResteasyWebTarget; +import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient43Engine; +import org.json.JSONObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +public class IdpClientFactory { + + private static Logger log = LoggerFactory.getLogger(IdpClientFactory.class); + private static final String CONTENT_TYPE = "Content-Type"; + + public static Response requestAccessToken(final String idpServerUrl, final String tokenUrl, final String clientId, + final String clientSecret, final String scope) { + log.info("Request for Access Token - idpServerUrl:{}, tokenUrl:{}, clientId:{}, clientSecret:{}, scope:{} ", idpServerUrl, tokenUrl, + clientId, clientSecret, scope); + Response response = null; + try { + Builder request = getClientBuilder(tokenUrl); + request.header("Authorization", "Basic " + clientId+":"+clientSecret); + request.header(CONTENT_TYPE, MediaType.APPLICATION_FORM_URLENCODED); + final MultivaluedHashMap multivaluedHashMap = new MultivaluedHashMap<>(); + multivaluedHashMap.add( "client_id", clientId); + multivaluedHashMap.add( "client_secret", clientSecret); + multivaluedHashMap.add( "grant_type", "client_credentials"); + multivaluedHashMap.add( "redirect_uri", idpServerUrl); + log.debug("Request for Access Token - multivaluedHashMap:{}", multivaluedHashMap); + + response = request.post(Entity.form(multivaluedHashMap)); + log.debug("Response for Access Token - response:{}", response); + if (response.getStatus() == 200) { + String entity = response.readEntity(String.class); + log.trace("Access Token - entity:{}", entity); + return response; + } + } catch(Exception ex){ + log.error("IdpClientFactory Exception requestAccessToken is :{}", ex); + }finally { + + + if (response != null) { + response.close(); + } + } + return response; + } + + public Response getSpMetadata(String metadataEndpoint) { + log.info(" SP Metadata - metadataEndpoint:{}", metadataEndpoint); + Builder metadataClient = getClientBuilder(metadataEndpoint); + metadataClient.header(CONTENT_TYPE, MediaType.APPLICATION_JSON); + Response response = metadataClient.get(); + log.debug("SpMetadata- response:{}", response); + + if (response != null ) { + log.trace("SP metadata response.getStatusInfo():{}, response.getEntity():{}, response.getEntity().getClass():{}", + response.getStatusInfo(), response.getEntity(),response.getEntity().getClass()); + } + + return response; + } + + + private static Builder getClientBuilder(String url) { + return ClientBuilder.newClient().target(url).request(); + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpAppInitializer.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpAppInitializer.java new file mode 100644 index 00000000000..ff5e65fcc7d --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpAppInitializer.java @@ -0,0 +1,86 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.configuration; + +import io.jans.as.common.service.common.ApplicationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.timer.IdpMetadataValidationTimer; +import io.jans.orm.PersistenceEntryManager; +import io.jans.service.timer.QuartzSchedulerManager; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.context.BeforeDestroyed; +import jakarta.enterprise.context.Initialized; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.Produces; +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.inject.Inject; +import jakarta.inject.Named; +import jakarta.servlet.ServletContext; + +import org.slf4j.Logger; + +@ApplicationScoped +@Named("idpAppInitializer") +public class IdpAppInitializer { + + @Inject + Logger log; + + @Inject + @Named(ApplicationFactory.PERSISTENCE_ENTRY_MANAGER_NAME) + Instance persistenceEntryManagerInstance; + + @Inject + BeanManager beanManager; + + @Inject + IdpConfigurationFactory idpConfigurationFactory; + + @Inject + QuartzSchedulerManager quartzSchedulerManager; + + @Inject + IdpMetadataValidationTimer idpMetadataValidationTimer; + + public void onAppStart(@Observes @Initialized(ApplicationScoped.class) Object init) { + log.info("============= Initializing IDP Plugin ========================"); + log.debug("init:{}", init); + + // configuration + this.idpConfigurationFactory.create(); + initSchedulerService(); + idpMetadataValidationTimer.initTimer(); + + log.info("============== IDP Plugin IS UP AND RUNNING ==================="); + } + + protected void initSchedulerService() { + log.debug("Initializing Scheduler Service"); + quartzSchedulerManager.start(); + + String disableScheduler = System.getProperties().getProperty("gluu.disable.scheduler"); + if (Boolean.parseBoolean(disableScheduler)) { + this.log.warn("Suspending Quartz Scheduler Service..."); + quartzSchedulerManager.standby(); + } + } + + public void destroy(@Observes @BeforeDestroyed(ApplicationScoped.class) ServletContext init) { + log.info("================================================================"); + log.info("=========== IDP Plugin STOPPED =========================="); + log.info("init:{}", init); + log.info("================================================================"); + } + + @Produces + @ApplicationScoped + public IdpConfigurationFactory getIdpConfigurationFactory() { + return idpConfigurationFactory; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpConfigurationFactory.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpConfigurationFactory.java new file mode 100644 index 00000000000..8195aeec013 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/IdpConfigurationFactory.java @@ -0,0 +1,294 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.configuration; + +import io.jans.as.common.service.common.ApplicationFactory; +import io.jans.as.model.config.Constants; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpConf; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpAppConfiguration; +import io.jans.as.model.configuration.Configuration; +import io.jans.exception.ConfigurationException; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.exception.BasePersistenceException; +import io.jans.orm.service.PersistanceFactoryService; +import io.jans.orm.util.properties.FileConfiguration; +import io.jans.service.cdi.async.Asynchronous; +import io.jans.service.cdi.event.BaseConfigurationReload; +import io.jans.service.cdi.event.ConfigurationEvent; +import io.jans.service.cdi.event.ConfigurationUpdate; +import io.jans.service.cdi.event.Scheduled; +import io.jans.service.timer.event.TimerEvent; +import io.jans.service.timer.schedule.TimerSchedule; +import org.slf4j.Logger; + +import java.io.File; +import java.util.concurrent.atomic.AtomicBoolean; +import jakarta.annotation.PostConstruct; +import jakarta.annotation.Priority; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; +import jakarta.enterprise.inject.Alternative; +import jakarta.enterprise.inject.Instance; +import jakarta.enterprise.inject.Produces; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +@ApplicationScoped +@Alternative +@Priority(1) +public class IdpConfigurationFactory { + + public static final String IDP_CONFIGURATION_ENTRY_DN = "idp_ConfigurationEntryDN"; + + static { + if (System.getProperty("jans.base") != null) { + BASE_DIR = System.getProperty("jans.base"); + } else if ((System.getProperty("catalina.base") != null) + && (System.getProperty("catalina.base.ignore") == null)) { + BASE_DIR = System.getProperty("catalina.base"); + } else if (System.getProperty("catalina.home") != null) { + BASE_DIR = System.getProperty("catalina.home"); + } else if (System.getProperty("jboss.home.dir") != null) { + BASE_DIR = System.getProperty("jboss.home.dir"); + } else { + BASE_DIR = null; + } + } + + @Inject + private Logger log; + + @Inject + private Event timerEvent; + + @Inject + private Event idpConfigurationUpdateEvent; + + @Inject + private Event event; + + @Inject + @Named(ApplicationFactory.PERSISTENCE_ENTRY_MANAGER_NAME) + private Instance persistenceEntryManagerInstance; + + @Inject + private PersistanceFactoryService persistanceFactoryService; + + @Inject + private Instance configurationInstance; + + // timer events + public static final String PERSISTENCE_CONFIGUARION_RELOAD_EVENT_TYPE = "persistenceConfigurationReloadEvent"; + public static final String IDP_BASE_CONFIGURATION_RELOAD_EVENT_TYPE = "idp_baseConfigurationReloadEvent"; + + private static final int DEFAULT_INTERVAL = 30; // 30 seconds + private AtomicBoolean isActive; + private long baseConfigurationFileLastModifiedTime; + + // base dir + private static final String BASE_DIR; + private static final String DIR = BASE_DIR + File.separator + "conf" + File.separator; + private static final String BASE_PROPERTIES_FILE = DIR + Constants.BASE_PROPERTIES_FILE_NAME; + + // idp config + private IdpAppConfiguration idpAppConfiguration; + private boolean idpConfigLoaded = false; + private long idpLoadedRevision = -1; + private FileConfiguration baseConfiguration; + + public String getIdpConfigurationDn() { + return this.baseConfiguration.getString(IDP_CONFIGURATION_ENTRY_DN); + } + + public FileConfiguration getBaseConfiguration() { + return baseConfiguration; + } + + @PostConstruct + public void init() { + log.info("Initializing IdpConfigurationFactory "); + this.isActive = new AtomicBoolean(true); + try { + + loadBaseConfiguration(); + + } finally { + this.isActive.set(false); + } + } + + @Produces + @ApplicationScoped + public IdpAppConfiguration getIdpAppConfiguration() { + return idpAppConfiguration; + } + + public void create() { + log.info("Loading IDP Configuration"); + + // load IDP config from DB + if (!loadIdpConfigFromDb()) { + log.error("Failed to load IDP configuration from persistence. Please fix it!!!."); + throw new ConfigurationException("Failed to load IDP configuration from persistence."); + } else { + log.debug("IDP Configuration loaded successfully - idpLoadedRevision:{}, idpAppConfiguration:{}", + this.idpLoadedRevision, getIdpAppConfiguration()); + } + + } + + public String getIdpAppConfigurationDn() { + return this.baseConfiguration.getString(IDP_CONFIGURATION_ENTRY_DN); + } + + public String getConfigurationDn(String key) { + return this.baseConfiguration.getString(key); + } + + private void loadBaseConfiguration() { + log.debug("Loading base configuration - BASE_PROPERTIES_FILE:{}", BASE_PROPERTIES_FILE); + + this.baseConfiguration = createFileConfiguration(BASE_PROPERTIES_FILE); + this.baseConfigurationFileLastModifiedTime = new File(BASE_PROPERTIES_FILE).lastModified(); + + log.debug("Loaded base configuration:{}", baseConfiguration.getProperties()); + } + + private FileConfiguration createFileConfiguration(String fileName) { + try { + return new FileConfiguration(fileName); + } catch (Exception ex) { + if (log.isErrorEnabled()) { + log.error("Failed to load configuration from {}", fileName, ex); + } + throw new ConfigurationException("Failed to load configuration from " + fileName, ex); + } + } + + private boolean loadIdpConfigFromDb() { + log.debug("Loading IDP configuration from '{}' DB...", baseConfiguration.getString("persistence.type")); + try { + final IdpConf idpConf = loadConfigurationFromDb(getConfigurationDn(IDP_CONFIGURATION_ENTRY_DN), + new IdpConf()); + log.trace("Conf configuration '{}' DB...", idpConf); + + if (idpConf != null) { + initIdpConf(idpConf); + + // Destroy old configuration + if (this.idpConfigLoaded) { + destroy(IdpAppConfiguration.class); + } + + this.idpConfigLoaded = true; + idpConfigurationUpdateEvent.select(ConfigurationUpdate.Literal.INSTANCE).fire(idpAppConfiguration); + + return true; + } + } catch (Exception ex) { + log.error("Unable to find api configuration in DB..." + ex.getMessage(), ex); + } + return false; + } + + private void initIdpConf(IdpConf idpConf) { + log.debug("Initializing IDP Configuration From DB.... idpConf:{}", idpConf); + + if (idpConf == null) { + throw new ConfigurationException("Failed to load IDP Configuration From DB " + idpConf); + } + + log.info("idpAppConfigurationFromDb:{}", idpConf); + if (idpConf.getDynamicConf() != null) { + this.idpAppConfiguration = idpConf.getDynamicConf(); + } + + this.idpLoadedRevision = idpConf.getRevision(); + + log.debug("*** idpAppConfiguration:{}, idpLoadedRevision:{} ", this.idpAppConfiguration, idpLoadedRevision); + + } + + private T loadConfigurationFromDb(String dn, T obj, String... returnAttributes) { + log.debug("Load IDP configuration from DB - dn:{}, clazz:{}, returnAttributes:{}", dn, obj, returnAttributes); + final PersistenceEntryManager persistenceEntryManager = persistenceEntryManagerInstance.get(); + try { + return (T) persistenceEntryManager.find(dn, obj.getClass(), returnAttributes); + } catch (BasePersistenceException ex) { + log.error(ex.getMessage()); + return null; + } + } + + private boolean isIdpRevisionIncreased() { + final IdpConf idpConf = loadConfigurationFromDb(getConfigurationDn(IDP_CONFIGURATION_ENTRY_DN), new IdpConf(), + "jansRevision"); + if (idpConf == null) { + return false; + } + + log.debug("IDP Config - DB revision: {}, server revision: {}", idpConf.getRevision(), idpLoadedRevision); + return idpConf.getRevision() > this.idpLoadedRevision; + } + + public boolean reloadIdpConfFromLdap() { + log.debug("Reload api configuration TimerEvent"); + if (!isIdpRevisionIncreased()) { + return false; + } + return this.loadIdpConfigFromDb(); + } + + public void destroy(Class clazz) { + Instance confInstance = configurationInstance.select(clazz); + configurationInstance.destroy(confInstance.get()); + } + + public void initTimer() { + log.debug("Initializing Configuration Timer"); + + final int delay = 30; + + timerEvent.fire(new TimerEvent(new TimerSchedule(delay, DEFAULT_INTERVAL), new ConfigurationEvent(), + Scheduled.Literal.INSTANCE)); + } + + @Asynchronous + public void reloadConfigurationTimerEvent(@Observes @Scheduled ConfigurationEvent configurationEvent) { + log.debug("Config reload configuration TimerEvent - baseConfigurationFileLastModifiedTime:{}", + baseConfigurationFileLastModifiedTime); + + // Reload Base configuration if needed + File baseConf = new File(BASE_PROPERTIES_FILE); + if (baseConf.exists()) { + final long lastModified = baseConf.lastModified(); + if (lastModified > baseConfigurationFileLastModifiedTime) { + // Reload configuration only if it was modified + loadBaseConfiguration(); + event.select(BaseConfigurationReload.Literal.INSTANCE).fire(IDP_BASE_CONFIGURATION_RELOAD_EVENT_TYPE); + } + } + + if (this.isActive.get()) { + return; + } + + if (!this.isActive.compareAndSet(false, true)) { + return; + } + + try { + reloadIdpConfFromLdap(); + } catch (Exception ex) { + log.error("Exception happened while reloading application configuration", ex); + } finally { + this.isActive.set(false); + } + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/KeycloakConfig.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/KeycloakConfig.java new file mode 100644 index 00000000000..a9289ea56c0 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/configuration/KeycloakConfig.java @@ -0,0 +1,48 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.configuration; + +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpAppConfiguration; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpConfigService; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.keycloak.admin.client.Keycloak; + +@ApplicationScoped +public class KeycloakConfig { + + private static Logger log = LoggerFactory.getLogger(KeycloakConfig.class); + + @Inject + IdpConfigService idpConfigService; + + public IdpAppConfiguration getIdpAppConfiguration() { + return idpConfigService.getIdpConf().getDynamicConf(); + } + + public Keycloak getInstance() { + log.info("Keycloak instance entry - idpConfigService:{}, getIdpAppConfiguration()", idpConfigService, + getIdpAppConfiguration()); + IdpAppConfiguration idpAppConfiguration = this.getIdpAppConfiguration(); + + log.trace("Keycloak instance entry - idpAppConfiguration:{}", idpAppConfiguration); + + return getInstance(idpAppConfiguration.getServerUrl(), idpAppConfiguration.getRealm(), + idpAppConfiguration.getUsername(), idpAppConfiguration.getPassword(), idpAppConfiguration.getClientId(), + idpAppConfiguration.getClientSecret()); + } + + public Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, + String clientSecret) { + log.info( + "Keycloak instance param serverUrl:{}, realm:{}, username:{}, password:{}, clientId:{}, clientSecret:{} ", + serverUrl, realm, username, password, clientId, clientSecret); + Keycloak keycloak = Keycloak.getInstance(serverUrl, realm, username, password, clientId, clientSecret); + log.info("keycloak:{} ", keycloak); + return keycloak; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/event/IdpMetadataValidationEvent.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/event/IdpMetadataValidationEvent.java new file mode 100644 index 00000000000..ee9e52099b6 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/event/IdpMetadataValidationEvent.java @@ -0,0 +1,11 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ +package io.jans.configapi.plugin.keycloak.idp.broker.event; + + +public class IdpMetadataValidationEvent { + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/extensions/IdpExtension.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/extensions/IdpExtension.java new file mode 100644 index 00000000000..a587d374c9a --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/extensions/IdpExtension.java @@ -0,0 +1,6 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.extensions; + +import jakarta.enterprise.inject.spi.Extension; + +public class IdpExtension implements Extension { +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/form/BrokerIdentityProviderForm.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/form/BrokerIdentityProviderForm.java new file mode 100644 index 00000000000..355b92ccb1d --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/form/BrokerIdentityProviderForm.java @@ -0,0 +1,65 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.form; + +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; + +import java.io.Serializable; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.Valid; + + +import java.io.InputStream; + +import org.jboss.resteasy.annotations.providers.multipart.PartType; +import jakarta.ws.rs.FormParam; +import jakarta.ws.rs.core.MediaType; + +public class BrokerIdentityProviderForm implements Serializable { + + private static final long serialVersionUID = 1L; + + @NotNull + @Valid + @FormParam("identityProvider") + @PartType(MediaType.APPLICATION_JSON) + private IdentityProvider identityProvider; + + @NotNull + @FormParam("metaDataFile") + @PartType(MediaType.APPLICATION_OCTET_STREAM) + private transient InputStream metaDataFile; + + public IdentityProvider getIdentityProvider() { + return identityProvider; + } + + public void setIdentityProvider(IdentityProvider identityProvider) { + this.identityProvider = identityProvider; + } + + public InputStream getMetaDataFile() { + return metaDataFile; + } + + public void setMetaDataFile(InputStream metaDataFile) { + this.metaDataFile = metaDataFile; + } + + public static long getSerialversionuid() { + return serialVersionUID; + } + + @Override + public String toString() { + return "BrokerIdentityProviderForm [identityProvider=" + identityProvider + "]"; + } + + + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/IdentityProviderMapper.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/IdentityProviderMapper.java new file mode 100644 index 00000000000..95bc79f53f8 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/IdentityProviderMapper.java @@ -0,0 +1,32 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.mapper; + + +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; +import jakarta.enterprise.context.ApplicationScoped; + +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + + +@Mapper +@ApplicationScoped +public interface IdentityProviderMapper { + + IdentityProviderMapper INSTANCE = Mappers.getMapper(IdentityProviderMapper.class); + + @Mapping(target = "inum", source = "kcIdentityProviderRepresentation.internalId") + @Mapping(target = "name", source = "kcIdentityProviderRepresentation.alias") + IdentityProvider kcIdentityProviderToIdentityProvider(IdentityProviderRepresentation kcIdentityProviderRepresentation); + + @Mapping(target = "internalId", source = "identityProvider.inum") + @Mapping(target = "alias", source = "identityProvider.name") + IdentityProviderRepresentation identityProviderToKCIdentityProvider(IdentityProvider identityProvider); +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/RealmMapper.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/RealmMapper.java new file mode 100644 index 00000000000..8502172be7d --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/mapper/RealmMapper.java @@ -0,0 +1,32 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.mapper; + + +import io.jans.configapi.plugin.keycloak.idp.broker.model.Realm; +import jakarta.enterprise.context.ApplicationScoped; + +import org.keycloak.representations.idm.RealmRepresentation; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.factory.Mappers; + + +@Mapper +@ApplicationScoped +public interface RealmMapper { + + IdentityProviderMapper INSTANCE = Mappers.getMapper(IdentityProviderMapper.class); + + @Mapping(target = "inum", source = "kcRealmRepresentation.id") + @Mapping(target = "name", source = "kcRealmRepresentation.realm") + Realm kcRealmRepresentationToRealm(RealmRepresentation kcRealmRepresentation); + + @Mapping(target = "id", source = "realm.inum") + @Mapping(target = "realm", source = "realm.name") + RealmRepresentation realmToKCRealmRepresentation(Realm realm); +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/IdentityProvider.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/IdentityProvider.java new file mode 100644 index 00000000000..981dd32263d --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/IdentityProvider.java @@ -0,0 +1,337 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.model; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.jans.model.GluuStatus; +import io.jans.orm.annotation.AttributeName; +import io.jans.orm.annotation.DataEntry; +import io.jans.orm.annotation.JsonObject; +import io.jans.orm.annotation.ObjectClass; +import io.jans.orm.model.base.Entry; +import io.jans.configapi.core.model.ValidationStatus; +import io.swagger.v3.oas.annotations.Hidden; + + +import java.util.Collections; +import java.util.List; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.io.Serializable; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +@DataEntry(sortBy = { "displayName" }) +@ObjectClass(value = "jansTrustedIdp") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class IdentityProvider extends Entry implements Serializable { + + @AttributeName(ignoreDuringUpdate = true) + private String inum; + + @NotNull + @AttributeName + private String creatorId; + + @NotNull + @AttributeName(name = "name") + private String name; + + @NotNull + @Size(min = 0, max = 60, message = "Length of the Display Name should not exceed 60") + @AttributeName + private String displayName; + + @NotNull + @Size(min = 0, max = 500, message = "Length of the Description should not exceed 500") + @AttributeName + private String description; + + @NotNull + @AttributeName(name = "realm") + private String realm; + + @AttributeName(name = "jansEnabled") + private boolean enabled; + + @AttributeName + private String providerId; + + @AttributeName + protected boolean trustEmail; + + @AttributeName + protected boolean storeToken; + + @AttributeName + protected boolean addReadTokenRoleOnCreate; + + @AttributeName + protected boolean authenticateByDefault; + + @AttributeName + protected boolean linkOnly; + + @AttributeName + protected String firstBrokerLoginFlowAlias; + + @AttributeName + protected String postBrokerLoginFlowAlias; + + @AttributeName(name = "jansSAMLspMetaDataFN") + @Hidden + private String spMetaDataFN; + + @AttributeName(name = "jansSAMLspMetaDataURL") + private String spMetaDataURL; + + @AttributeName(name = "jansSAMLspMetaLocation") + private String spMetaDataLocation; + + @AttributeName(name = "jansSAMLidpMetaDataFN") + @Hidden + private String idpMetaDataFN; + + @AttributeName(name = "jansSAMLidpMetaDataURL") + private String idpMetaDataURL; + + @AttributeName(name = "jansSAMLidpMetaLocation") + private String idpMetaDataLocation; + + @AttributeName(name = "jansStatus") + private GluuStatus status; + + @AttributeName(name = "jansValidationStatus") + private ValidationStatus validationStatus; + + @AttributeName(name = "jansValidationLog") + private List validationLog; + + @JsonObject + @AttributeName(name = "jansSAMLidpConfDyn") + Map config = new HashMap<>(); + + public String getInum() { + return inum; + } + + public void setInum(String inum) { + this.inum = inum; + } + + public String getCreatorId() { + return creatorId; + } + + public void setCreatorId(String creatorId) { + this.creatorId = creatorId; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getRealm() { + return realm; + } + + public void setRealm(String realm) { + this.realm = realm; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getProviderId() { + return providerId; + } + + public void setProviderId(String providerId) { + this.providerId = providerId; + } + + public boolean isTrustEmail() { + return trustEmail; + } + + public void setTrustEmail(boolean trustEmail) { + this.trustEmail = trustEmail; + } + + public boolean isStoreToken() { + return storeToken; + } + + public void setStoreToken(boolean storeToken) { + this.storeToken = storeToken; + } + + public boolean isAddReadTokenRoleOnCreate() { + return addReadTokenRoleOnCreate; + } + + public void setAddReadTokenRoleOnCreate(boolean addReadTokenRoleOnCreate) { + this.addReadTokenRoleOnCreate = addReadTokenRoleOnCreate; + } + + public boolean isAuthenticateByDefault() { + return authenticateByDefault; + } + + public void setAuthenticateByDefault(boolean authenticateByDefault) { + this.authenticateByDefault = authenticateByDefault; + } + + public boolean isLinkOnly() { + return linkOnly; + } + + public void setLinkOnly(boolean linkOnly) { + this.linkOnly = linkOnly; + } + + public String getFirstBrokerLoginFlowAlias() { + return firstBrokerLoginFlowAlias; + } + + public void setFirstBrokerLoginFlowAlias(String firstBrokerLoginFlowAlias) { + this.firstBrokerLoginFlowAlias = firstBrokerLoginFlowAlias; + } + + public String getPostBrokerLoginFlowAlias() { + return postBrokerLoginFlowAlias; + } + + public void setPostBrokerLoginFlowAlias(String postBrokerLoginFlowAlias) { + this.postBrokerLoginFlowAlias = postBrokerLoginFlowAlias; + } + + public String getSpMetaDataFN() { + return spMetaDataFN; + } + + public void setSpMetaDataFN(String spMetaDataFN) { + this.spMetaDataFN = spMetaDataFN; + } + + public String getSpMetaDataURL() { + return spMetaDataURL; + } + + public void setSpMetaDataURL(String spMetaDataURL) { + this.spMetaDataURL = spMetaDataURL; + } + + public String getSpMetaDataLocation() { + return spMetaDataLocation; + } + + public void setSpMetaDataLocation(String spMetaDataLocation) { + this.spMetaDataLocation = spMetaDataLocation; + } + + public String getIdpMetaDataFN() { + return idpMetaDataFN; + } + + public void setIdpMetaDataFN(String idpMetaDataFN) { + this.idpMetaDataFN = idpMetaDataFN; + } + + public String getIdpMetaDataURL() { + return idpMetaDataURL; + } + + public void setIdpMetaDataURL(String idpMetaDataURL) { + this.idpMetaDataURL = idpMetaDataURL; + } + + public String getIdpMetaDataLocation() { + return idpMetaDataLocation; + } + + public void setIdpMetaDataLocation(String idpMetaDataLocation) { + this.idpMetaDataLocation = idpMetaDataLocation; + } + + public GluuStatus getStatus() { + return status; + } + + public void setStatus(GluuStatus status) { + this.status = status; + } + + public ValidationStatus getValidationStatus() { + return validationStatus; + } + + public void setValidationStatus(ValidationStatus validationStatus) { + this.validationStatus = validationStatus; + } + + public List getValidationLog() { + return validationLog; + } + + public void setValidationLog(List validationLog) { + this.validationLog = validationLog; + } + + public Map getConfig() { + return config; + } + + public void setConfig(Map config) { + this.config = config; + } + + @Override + public String toString() { + return "IdentityProvider [inum=" + inum + ", creatorId=" + creatorId + ", name=" + name + ", displayName=" + + displayName + ", description=" + description + ", realm=" + realm + ", enabled=" + enabled + + ", providerId=" + providerId + ", trustEmail=" + trustEmail + ", storeToken=" + storeToken + + ", addReadTokenRoleOnCreate=" + addReadTokenRoleOnCreate + ", authenticateByDefault=" + + authenticateByDefault + ", linkOnly=" + linkOnly + ", firstBrokerLoginFlowAlias=" + + firstBrokerLoginFlowAlias + ", postBrokerLoginFlowAlias=" + postBrokerLoginFlowAlias + + ", spMetaDataFN=" + spMetaDataFN + ", spMetaDataURL=" + spMetaDataURL + ", spMetaDataLocation=" + + spMetaDataLocation + ", idpMetaDataFN=" + idpMetaDataFN + ", idpMetaDataURL=" + idpMetaDataURL + + ", idpMetaDataLocation=" + idpMetaDataLocation + ", status=" + status + ", validationStatus=" + + validationStatus + ", validationLog=" + validationLog + ", config=" + config + "]"; + } + + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/Realm.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/Realm.java new file mode 100644 index 00000000000..9121b778a2a --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/Realm.java @@ -0,0 +1,95 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.model; + +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.jans.model.GluuStatus; +import io.jans.orm.annotation.AttributeName; +import io.jans.orm.annotation.DataEntry; +import io.jans.orm.annotation.JsonObject; +import io.jans.orm.annotation.ObjectClass; +import io.jans.orm.model.base.Entry; +import io.jans.configapi.core.model.ValidationStatus; +import io.swagger.v3.oas.annotations.Hidden; + + +import java.util.Collections; +import java.util.List; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.io.Serializable; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + +@DataEntry(sortBy = { "displayName" }) +@ObjectClass(value = "jansRealm") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class Realm extends Entry implements Serializable { + + @AttributeName(ignoreDuringUpdate = true) + private String inum; + + @NotNull + @AttributeName(name = "name") + private String name; + + @NotNull + @Size(min = 0, max = 60, message = "Length of the Display Name should not exceed 60") + @AttributeName + private String displayName; + + + @AttributeName(name = "jansEnabled") + private boolean enabled; + + + public String getInum() { + return inum; + } + + + public void setInum(String inum) { + this.inum = inum; + } + + + public String getName() { + return name; + } + + + public void setName(String name) { + this.name = name; + } + + + public String getDisplayName() { + return displayName; + } + + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + + public boolean isEnabled() { + return enabled; + } + + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpAppConfiguration.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpAppConfiguration.java new file mode 100644 index 00000000000..cb9b2df16b9 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpAppConfiguration.java @@ -0,0 +1,225 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.model.config; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import io.jans.as.model.configuration.Configuration; + +@JsonIgnoreProperties(ignoreUnknown = true) +public class IdpAppConfiguration implements Configuration { + + private String applicationName; + private String trustedIdpDn; + private String realmDn; + private boolean enabled; + + private String serverUrl; + private String realm; + private String clientId; + private String clientSecret; + private String grantType; + private String username; + private String password; + + private String idpRootDir; + private String idpMetadataRootDir; + private String idpMetadataTempDir; + private String idpMetadataFilePattern; + private String idpMetadataFile; + + private String spMetadataUrl; + private String spMetadataRootDir; + private String spMetadataTempDir; + private String spMetadataFilePattern; + private String spMetadataFile; + + private boolean ignoreValidation; + + public String getApplicationName() { + return applicationName; + } + + public void setApplicationName(String applicationName) { + this.applicationName = applicationName; + } + + public String getTrustedIdpDn() { + return trustedIdpDn; + } + + public void setTrustedIdpDn(String trustedIdpDn) { + this.trustedIdpDn = trustedIdpDn; + } + + public String getRealmDn() { + return realmDn; + } + + public void setRealmDn(String realmDn) { + this.realmDn = realmDn; + } + + public boolean isEnabled() { + return enabled; + } + + public void setEnabled(boolean enabled) { + this.enabled = enabled; + } + + public String getServerUrl() { + return serverUrl; + } + + public void setServerUrl(String serverUrl) { + this.serverUrl = serverUrl; + } + + public String getRealm() { + return realm; + } + + public void setRealm(String realm) { + this.realm = realm; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getGrantType() { + return grantType; + } + + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getIdpRootDir() { + return idpRootDir; + } + + public void setIdpRootDir(String idpRootDir) { + this.idpRootDir = idpRootDir; + } + + public String getIdpMetadataRootDir() { + return idpMetadataRootDir; + } + + public void setIdpMetadataRootDir(String idpMetadataRootDir) { + this.idpMetadataRootDir = idpMetadataRootDir; + } + + public String getIdpMetadataTempDir() { + return idpMetadataTempDir; + } + + public void setIdpMetadataTempDir(String idpMetadataTempDir) { + this.idpMetadataTempDir = idpMetadataTempDir; + } + + public String getIdpMetadataFilePattern() { + return idpMetadataFilePattern; + } + + public void setIdpMetadataFilePattern(String idpMetadataFilePattern) { + this.idpMetadataFilePattern = idpMetadataFilePattern; + } + + public String getIdpMetadataFile() { + return idpMetadataFile; + } + + public void setIdpMetadataFile(String idpMetadataFile) { + this.idpMetadataFile = idpMetadataFile; + } + + public String getSpMetadataUrl() { + return spMetadataUrl; + } + + public void setSpMetadataUrl(String spMetadataUrl) { + this.spMetadataUrl = spMetadataUrl; + } + + public String getSpMetadataRootDir() { + return spMetadataRootDir; + } + + public void setSpMetadataRootDir(String spMetadataRootDir) { + this.spMetadataRootDir = spMetadataRootDir; + } + + public String getSpMetadataTempDir() { + return spMetadataTempDir; + } + + public void setSpMetadataTempDir(String spMetadataTempDir) { + this.spMetadataTempDir = spMetadataTempDir; + } + + public String getSpMetadataFilePattern() { + return spMetadataFilePattern; + } + + public void setSpMetadataFilePattern(String spMetadataFilePattern) { + this.spMetadataFilePattern = spMetadataFilePattern; + } + + public String getSpMetadataFile() { + return spMetadataFile; + } + + public void setSpMetadataFile(String spMetadataFile) { + this.spMetadataFile = spMetadataFile; + } + + public boolean isIgnoreValidation() { + return ignoreValidation; + } + + public void setIgnoreValidation(boolean ignoreValidation) { + this.ignoreValidation = ignoreValidation; + } + + @Override + public String toString() { + return "IdpAppConfiguration [applicationName=" + applicationName + ", trustedIdpDn=" + trustedIdpDn + + ", realmDn=" + realmDn + ", enabled=" + enabled + ", serverUrl=" + serverUrl + ", realm=" + realm + + ", clientId=" + clientId + ", clientSecret=" + clientSecret + ", grantType=" + grantType + + ", username=" + username + ", password=" + password + ", idpRootDir=" + idpRootDir + + ", idpMetadataRootDir=" + idpMetadataRootDir + ", idpMetadataTempDir=" + idpMetadataTempDir + + ", idpMetadataFilePattern=" + idpMetadataFilePattern + ", idpMetadataFile=" + idpMetadataFile + + ", spMetadataUrl=" + spMetadataUrl + ", spMetadataRootDir=" + spMetadataRootDir + + ", spMetadataTempDir=" + spMetadataTempDir + ", spMetadataFilePattern=" + spMetadataFilePattern + + ", spMetadataFile=" + spMetadataFile + ", ignoreValidation=" + ignoreValidation + "]"; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConf.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConf.java new file mode 100644 index 00000000000..2f227d4a513 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConf.java @@ -0,0 +1,70 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.model.config; + +import io.jans.as.model.config.StaticConfiguration; +import io.jans.orm.annotation.AttributeName; +import io.jans.orm.annotation.DN; +import io.jans.orm.annotation.DataEntry; +import io.jans.orm.annotation.JsonObject; +import io.jans.orm.annotation.ObjectClass; + +@DataEntry +@ObjectClass(value = "jansAppConf") +public class IdpConf { + @DN + private String dn; + + @JsonObject + @AttributeName(name = "jansConfDyn") + private IdpAppConfiguration dynamicConf; + + @JsonObject + @AttributeName(name = "jansConfStatic") + private StaticConfiguration staticsConf; + + @AttributeName(name = "jansRevision") + private long revision; + + public String getDn() { + return dn; + } + + public void setDn(String dn) { + this.dn = dn; + } + + public IdpAppConfiguration getDynamicConf() { + return dynamicConf; + } + + public void setDynamicConf(IdpAppConfiguration dynamicConf) { + this.dynamicConf = dynamicConf; + } + + public StaticConfiguration getStaticsConf() { + return staticsConf; + } + + public void setStaticsConf(StaticConfiguration staticsConf) { + this.staticsConf = staticsConf; + } + + public long getRevision() { + return revision; + } + + public void setRevision(long revision) { + this.revision = revision; + } + + @Override + public String toString() { + return "IdpConf [dn=" + dn + ", dynamicConf=" + dynamicConf + ", staticsConf=" + staticsConf + ", revision=" + + revision + "]"; + } +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConfigSource.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConfigSource.java new file mode 100644 index 00000000000..51bcdd72ffd --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/model/config/IdpConfigSource.java @@ -0,0 +1,82 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.model.config; + +import io.jans.exception.ConfigurationException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; +import jakarta.enterprise.context.ApplicationScoped; + +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@ApplicationScoped +public class IdpConfigSource implements ConfigSource { + + private static Logger log = LoggerFactory.getLogger(IdpConfigSource.class); + private static final String FILE_CONFIG = "idp.properties"; + private Properties properties = null; + Map propertiesMap = new HashMap<>(); + + public IdpConfigSource() { + this.loadProperties(); + } + + @Override + public Map getProperties() { + log.debug("Getting properties"); + return propertiesMap; + } + + @Override + public Set getPropertyNames() { + log.debug("Getting Property Names"); + try { + return properties.stringPropertyNames(); + + } catch (Exception e) { + log.error("Unable to read properties from file: " + FILE_CONFIG, e); + } + return Collections.emptySet(); + } + + @Override + public int getOrdinal() { + return 800; + } + + @Override + public String getValue(String name) { + log.debug("IdpConfigSource()::getValue() - name:{}", name); + try { + return properties.getProperty(name); + } catch (Exception e) { + log.error("Unable to read properties from file: " + FILE_CONFIG, e); + } + + return null; + } + + @Override + public String getName() { + return FILE_CONFIG; + } + + private Properties loadProperties() { + // Load the properties file + ClassLoader loader = Thread.currentThread().getContextClassLoader(); + try (InputStream inputStream = loader.getResourceAsStream(FILE_CONFIG)) { + properties = new Properties(); + properties.load(inputStream); + properties.stringPropertyNames().stream() + .forEach(key -> propertiesMap.put(key, properties.getProperty(key))); + return properties; + } catch (Exception e) { + throw new ConfigurationException("Failed to load configuration from " + FILE_CONFIG, e); + } + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpApiApplication.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpApiApplication.java new file mode 100644 index 00000000000..8c2640bbf9a --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpApiApplication.java @@ -0,0 +1,51 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.rest; + +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.*; +import io.swagger.v3.oas.annotations.tags.*; +import io.swagger.v3.oas.annotations.security.*; +import io.swagger.v3.oas.annotations.servers.*; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +@ApplicationPath(Constants.IDENTITY_PROVIDER) +@OpenAPIDefinition(info = @Info(title = "Jans Config API", version="OAS Version", contact = +@Contact(name = "Contact", url = "https://github.com/JanssenProject/jans/discussions"), + +license = @License(name = "Apache 2.0", url = "https://github.com/JanssenProject/jans/blob/main/LICENSE")), + +tags = { + @Tag(name = "Jans - SAML Identity Broker Configuration"), + @Tag(name = "Jans - SAML Identity Broker"), + @Tag(name = "Jans - SAML Identity Broker Realm") }, + +servers = { @Server(url = "https://jans.io/", description = "The Jans server") }) + +@SecurityScheme(name = "oauth2", type = SecuritySchemeType.OAUTH2, flows = @OAuthFlows(clientCredentials = @OAuthFlow(tokenUrl = "https://{op-hostname}/.../token", scopes = { +@OAuthScope(name = Constants.JANS_IDP_CONFIG_READ_ACCESS, description = "View Jans Identity Broker config related information"), +@OAuthScope(name = Constants.JANS_IDP_CONFIG_WRITE_ACCESS, description = "Manage Jans Identity Broker config related information"), +@OAuthScope(name = Constants.JANS_IDP_REALM_READ_ACCESS, description = "View Identity Broker realm related information"), +@OAuthScope(name = Constants.JANS_IDP_REALM_WRITE_ACCESS, description = "Manage Identity Broker realm related information"), +@OAuthScope(name = Constants.JANS_IDP_SAML_READ_ACCESS, description = "View Identity Broker SAML Identity Broker related information"), +@OAuthScope(name = Constants.JANS_IDP_SAML_WRITE_ACCESS, description = "Manage Identity Broker SAML Identity Broker related information") +} +))) +public class IdpApiApplication extends Application { + + @Override + public Set> getClasses() { + HashSet> classes = new HashSet<>(); + + classes.add(IdpConfigResource.class); + classes.add(IdpResource.class); + classes.add(IdpRealmResource.class); + + return classes; + } +} + diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpConfigResource.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpConfigResource.java new file mode 100644 index 00000000000..46953931cc2 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpConfigResource.java @@ -0,0 +1,113 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.rest; + +import io.jans.configapi.core.rest.BaseResource; +import io.jans.configapi.core.rest.ProtectedApi; +import io.jans.configapi.core.util.Jackson; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpAppConfiguration; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpConf; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpConfigService; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.jans.configapi.util.ApiAccessConstants; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.*; + +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import java.io.IOException; + +import org.slf4j.Logger; + +import com.github.fge.jsonpatch.JsonPatch; +import com.github.fge.jsonpatch.JsonPatchException; + +@Path(Constants.IDP_CONFIG_PATH) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class IdpConfigResource extends BaseResource { + + @Inject + Logger logger; + + @Inject + IdpConfigService idpConfigService; + + @Operation(summary = "Gets IDP configuration properties", description = "Gets IDP configuration properties", operationId = "get-idp-config-properties", tags = { + "Jans - SAML Identity Broker Configuration" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_CONFIG_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdpAppConfiguration.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { Constants.JANS_IDP_CONFIG_READ_ACCESS }, groupScopes = { + Constants.JANS_IDP_CONFIG_WRITE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_READ_ACCESS, + ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) + public Response getIdpConfiguration() { + IdpAppConfiguration idpAppConfiguration = idpConfigService.getIdpAppConfiguration(); + logger.info("IDP idpAppConfiguration():{}", idpAppConfiguration); + return Response.ok(idpAppConfiguration).build(); + } + + @Operation(summary = "Update IDP configuration properties", description = "Update IDP configuration properties", operationId = "put-idp-properties", tags = { + "Jans - SAML Identity Broker Configuration" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_CONFIG_WRITE_ACCESS })) + @RequestBody(description = "GluuAttribute object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdpAppConfiguration.class), examples = @ExampleObject(name = "Request example", value = "example/idp/config/idp-config.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdpAppConfiguration.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PUT @ProtectedApi(scopes = { Constants.JANS_IDP_CONFIG_WRITE_ACCESS }, groupScopes = {}, superScopes = { + ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) + public Response updateIdpConfiguration(@Valid IdpAppConfiguration idpAppConfiguration) { + logger.info("Update IDP details idpAppConfiguration():{}", idpAppConfiguration); + IdpConf conf = idpConfigService.findIdpConf(); + conf.setDynamicConf(idpAppConfiguration); + idpConfigService.mergeIdpConfig(conf); + idpAppConfiguration = idpConfigService.getIdpAppConfiguration(); + logger.info("IDP post update - idpAppConfiguration:{}", idpAppConfiguration); + return Response.ok(idpAppConfiguration).build(); + + } + + @Operation(summary = "Partially modifies IDP configuration properties.", description = "Partially modifies IDP Configuration properties.", operationId = "patch-idp-properties", tags = { + "Jans - SAML Identity Broker Configuration" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_CONFIG_WRITE_ACCESS })) + @RequestBody(description = "String representing patch-document.", content = @Content(mediaType = MediaType.APPLICATION_JSON_PATCH_JSON, array = @ArraySchema(schema = @Schema(implementation = JsonPatch.class)), examples = @ExampleObject(name = "Request json example", value = "example/dp/config/idp-config-patch.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdpAppConfiguration.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @PATCH + @Consumes(MediaType.APPLICATION_JSON_PATCH_JSON) + @ProtectedApi(scopes = { Constants.JANS_IDP_CONFIG_WRITE_ACCESS }, groupScopes = {}, superScopes = { + ApiAccessConstants.SUPER_ADMIN_WRITE_ACCESS }) + public Response patchIdpConfiguration(@NotNull String jsonPatchString) throws JsonPatchException, IOException { + logger.info("IDP Config patch - jsonPatchString:{} ", jsonPatchString); + IdpConf conf = idpConfigService.findIdpConf(); + IdpAppConfiguration idpAppConfiguration = Jackson.applyPatch(jsonPatchString, conf.getDynamicConf()); + conf.setDynamicConf(idpAppConfiguration); + idpConfigService.mergeIdpConfig(conf); + idpAppConfiguration = idpConfigService.getIdpAppConfiguration(); + logger.info("IDP config post patch - idpAppConfiguration:{}", idpAppConfiguration); + return Response.ok(idpAppConfiguration).build(); + } +} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpRealmResource.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpRealmResource.java new file mode 100644 index 00000000000..b3a8d89008b --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpRealmResource.java @@ -0,0 +1,175 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.rest; + +import static io.jans.as.model.util.Util.escapeLog; + +import io.jans.configapi.core.rest.BaseResource; +import io.jans.configapi.core.rest.ProtectedApi; +import io.jans.configapi.util.ApiConstants; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.jans.configapi.plugin.keycloak.idp.broker.service.RealmService; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.*; + +import jakarta.inject.Inject; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import java.io.InputStream; +import java.io.IOException; +import java.util.*; + +import org.slf4j.Logger; + +import io.jans.configapi.plugin.keycloak.idp.broker.model.Realm; + +@Path(Constants.REALM_PATH) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class IdpRealmResource extends BaseResource { + + private static final String JANS_REALM_DETAILS = "Jans Realm Details"; + + @Inject + Logger logger; + + @Inject + RealmService realmService; + + @Operation(summary = "Get all realm", description = "Get all realm", operationId = "get-realm", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = Realm.class)))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_READ_ACCESS }) + public Response getAllRealms() { + List realms = realmService.getAllRealmDetails(); + logger.info("All realms:{}", realms); + return Response.ok(realms).build(); + } + + @Operation(summary = "Get realm by name", description = "Get realm by name", operationId = "get-realm-by-name", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, array = @ArraySchema(schema = @Schema(implementation = Realm.class)))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_READ_ACCESS }) + @Path(Constants.NAME_PATH + Constants.NAME_PATH_PARAM) + public Response getRealmByName(@Parameter(description = "name") @PathParam(Constants.NAME) @NotNull String name) { + if (logger.isInfoEnabled()) { + logger.info("Searching Realm by name: {}", escapeLog(name)); + } + + List realmList = realmService.getRealmByName(name); + + logger.info("Realms found - realmList:{}", realmList); + + return Response.ok(realmList).build(); + } + + @Operation(summary = "Get realm by inum", description = "Get realm by inum", operationId = "get-realm-by-inum", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Realm.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_READ_ACCESS }) + @Path(Constants.INUM_PATH_PARAM) + public Response getRealmByInum( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (logger.isInfoEnabled()) { + logger.info("Searching Realm by inum: {}", escapeLog(inum)); + } + + Realm realm = realmService.getRealmByInum(inum); + + logger.info("Realm found by inum is realm:{}", realm); + + return Response.ok(realm).build(); + } + + @Operation(summary = "Create realm", description = "Create realm", operationId = "post-realm", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_WRITE_ACCESS })) + @RequestBody(description = "Realm details", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Realm.class), examples = @ExampleObject(name = "Request example", value = "example/idp/realm/post-realm.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Newly created realm", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Realm.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_WRITE_ACCESS }, groupScopes = {}, superScopes = { + Constants.JANS_IDP_REALM_WRITE_ACCESS }) + @POST + public Response createNewKCRealm(@NotNull Realm realm) throws IOException { + if (logger.isInfoEnabled()) { + logger.info(" Create new realm:{} ", realm); + } + checkResourceNotNull(realm, JANS_REALM_DETAILS); + realm = this.realmService.createNewRealm(realm); + logger.info("Created new - realm:{}", realm); + return Response.status(Response.Status.CREATED).entity(realm).build(); + } + + @Operation(summary = "Update realm", description = "Update realm", operationId = "put-realm", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_WRITE_ACCESS })) + @RequestBody(description = "Realm details", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Realm.class), examples = @ExampleObject(name = "Request example", value = "example/idp/realm/put-realm.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Updated Jans realm object", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = Realm.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_WRITE_ACCESS }, groupScopes = {}, superScopes = { + Constants.JANS_IDP_REALM_WRITE_ACCESS }) + @PUT + public Response updateRealm(@NotNull Realm realm) throws IOException { + if (logger.isInfoEnabled()) { + logger.info(" Update realm:{} ", realm); + } + + checkResourceNotNull(realm, JANS_REALM_DETAILS); + realm = this.realmService.updateRealm(realm); + logger.info("Updated realm:{}", realm); + return Response.status(Response.Status.OK).entity(realm).build(); + } + + @Operation(summary = "Delete realm ", description = "Delete realm", operationId = "delete-realm", tags = { + "Jans - SAML Identity Broker Realm" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_REALM_WRITE_ACCESS })) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @Path(Constants.NAME_PATH_PARAM) + @ProtectedApi(scopes = { Constants.JANS_IDP_REALM_WRITE_ACCESS }) + @DELETE + public Response deleteRealm( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (logger.isInfoEnabled()) { + logger.info("Delete realm by inum:{}", escapeLog(inum)); + } + + Realm realm = realmService.getRealmByInum(inum); + checkResourceNotNull(realm, "Realm identified by" + inum + "'"); + + realmService.deleteRealm(realm); + + return Response.noContent().build(); + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpResource.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpResource.java new file mode 100644 index 00000000000..03c2fe3ab51 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/rest/IdpResource.java @@ -0,0 +1,315 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.rest; + +import com.fasterxml.jackson.databind.JsonNode; +import com.github.fge.jsonpatch.JsonPatch; +import com.github.fge.jsonpatch.JsonPatchException; + +import io.jans.as.common.model.registration.Client; +import io.jans.configapi.core.rest.BaseResource; +import io.jans.configapi.core.rest.ProtectedApi; +import io.jans.configapi.core.util.Jackson; +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpService; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.jans.configapi.plugin.keycloak.idp.broker.form.BrokerIdentityProviderForm; +import io.jans.configapi.util.ApiAccessConstants; +import io.jans.configapi.util.ApiConstants; +import io.jans.configapi.util.AttributeNames; +import io.jans.model.SearchRequest; +import io.jans.orm.model.PagedResult; +import io.jans.util.security.StringEncrypter.EncryptionException; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.*; + +import jakarta.inject.Inject; +import jakarta.validation.Valid; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import static io.jans.as.model.util.Util.escapeLog; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.util.List; +import java.util.stream.Collectors; + +import org.slf4j.Logger; +import org.jboss.resteasy.annotations.providers.multipart.MultipartForm; + +@Path(Constants.KEYCLOAK + Constants.SAML_PATH) +@Consumes(MediaType.APPLICATION_JSON) +@Produces(MediaType.APPLICATION_JSON) +public class IdpResource extends BaseResource { + + private static final String SAML_IDP_DATA = "SAML IDP Data"; + private static final String SAML_IDP_DATA_FORM = "SAML IDP Data From"; + private static final String SAML_IDP_CHECK_STR = "IdentityProvider identified by '"; + + private class IdentityProviderPagedResult extends PagedResult { + }; + + @Inject + Logger log; + + @Inject + IdpService idpService; + + @Operation(summary = "Retrieves SAML Identity Provider", description = "Retrieves SAML Identity Provider", operationId = "get-saml-identity-provider", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdentityProviderPagedResult.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/get-all-saml-identity-provider.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_READ_ACCESS }) + public Response getAllSamlIdentityProvider( + @Parameter(description = "Search size - max size of the results to return") @DefaultValue(ApiConstants.DEFAULT_LIST_SIZE) @QueryParam(value = ApiConstants.LIMIT) int limit, + @Parameter(description = "Search pattern") @DefaultValue("") @QueryParam(value = ApiConstants.PATTERN) String pattern, + @Parameter(description = "The 1-based index of the first query result") @DefaultValue(ApiConstants.DEFAULT_LIST_START_INDEX) @QueryParam(value = ApiConstants.START_INDEX) int startIndex, + @Parameter(description = "Attribute whose value will be used to order the returned response") @DefaultValue(ApiConstants.INUM) @QueryParam(value = ApiConstants.SORT_BY) String sortBy, + @Parameter(description = "Order in which the sortBy param is applied. Allowed values are \"ascending\" and \"descending\"") @DefaultValue(ApiConstants.ASCENDING) @QueryParam(value = ApiConstants.SORT_ORDER) String sortOrder, + @Parameter(description = "Field and value pair for seraching", examples = @ExampleObject(name = "Field value example", value = "applicationType=web,persistClientAuthorizations=true")) @DefaultValue("") @QueryParam(value = ApiConstants.FIELD_VALUE_PAIR) String fieldValuePair) + throws IllegalAccessException, InvocationTargetException { + if (log.isDebugEnabled()) { + log.debug( + "Client serach param - limit:{}, pattern:{}, startIndex:{}, sortBy:{}, sortOrder:{}, fieldValuePair:{}", + escapeLog(limit), escapeLog(pattern), escapeLog(startIndex), escapeLog(sortBy), + escapeLog(sortOrder), escapeLog(fieldValuePair)); + } + + SearchRequest searchReq = createSearchRequest(idpService.getIdentityProviderDn(), pattern, sortBy, sortOrder, + startIndex, limit, null, null, ApiConstants.DEFAULT_MAX_COUNT, fieldValuePair, IdentityProvider.class); + + return Response.ok(this.doSearch(searchReq)).build(); + } + + @Operation(summary = "Get SAML Identity Provider by Inum", description = "Get SAML Identity Provider by Inum", operationId = "get-saml-identity-provider-by-inum", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdentityProvider.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/get-saml-identity-provider-by-inum.json"))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @Path(Constants.INUM_PATH_PARAM) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_READ_ACCESS }) + public Response getSamlIdentityProviderByInum( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (log.isInfoEnabled()) { + log.info("Fetch SAML IDP by inum:{}", escapeLog(inum)); + } + IdentityProvider idp = idpService.getIdentityProviderByInum(inum); + log.debug("SAML IDP fetched idp:{}", idp); + return Response.ok(idp).build(); + } + + @Operation(summary = "Get SAML SP Metadata as Json", description = "Get SAML SP Metadata as Json", operationId = "get-saml-sp-metadata-json", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = JsonNode.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @Path(Constants.SP_METADATA_PATH + Constants.INUM_PATH_PARAM) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_READ_ACCESS }) + public Response getSamlSPMetadataJson( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (log.isInfoEnabled()) { + log.info("Fetch SAML SP Metadata for IDP by inum:{}", escapeLog(inum)); + } + IdentityProvider identityProvider = idpService.getIdentityProviderByInum(inum); + log.debug(" identityProvider:{} ", identityProvider); + checkResourceNotNull(identityProvider, SAML_IDP_CHECK_STR + inum + "'"); + Response response = idpService.getSpMetadata(identityProvider); + log.info(" response:{} ", response); + + return Response.ok(response.getEntity()).build(); + } + + @Operation(summary = "Get SAML SP Metadata Endpoint URL", description = "Get SAML SP Metadata Endpoint URL", operationId = "get-saml-sp-metadata-url", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @Path(Constants.SP_METADATA_FILE_PATH + Constants.INUM_PATH_PARAM) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_READ_ACCESS }) + @Produces(MediaType.APPLICATION_OCTET_STREAM) + public Response getSamlSPMetadataFile( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (log.isInfoEnabled()) { + log.info("Fetch SAML SP Metadata URL IDP by inum:{}", escapeLog(inum)); + } + IdentityProvider identityProvider = idpService.getIdentityProviderByInum(inum); + log.debug(" identityProvider:{} ", identityProvider); + checkResourceNotNull(identityProvider, SAML_IDP_CHECK_STR + inum + "'"); + String spMetadataUrl = idpService.getSpMetadataUrl(identityProvider.getRealm(), identityProvider.getName()); + log.info(" spMetadataUrl:{} ", spMetadataUrl); + return Response.ok(spMetadataUrl).build(); + } + + @Operation(summary = "Create SAML Identity Provider", description = "Create SAML Identity Provider", operationId = "post-saml-identity-provider", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_WRITE_ACCESS })) + @RequestBody(description = "String representing patch-document.", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = BrokerIdentityProviderForm.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/post-saml-identity-provider.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "201", description = "Newly created Trust IDP", content = @Content(mediaType = MediaType.APPLICATION_JSON_PATCH_JSON, schema = @Schema(implementation = IdentityProvider.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/get-saml-identity-provider-by-inum.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @POST + @Path(Constants.UPLOAD_PATH) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_WRITE_ACCESS }) + public Response createSamlIdentityProvider(@MultipartForm BrokerIdentityProviderForm brokerIdentityProviderForm) + throws IOException { + if (log.isInfoEnabled()) { + log.info("Create brokerIdentityProviderForm:{}", brokerIdentityProviderForm); + } + + // validation + checkResourceNotNull(brokerIdentityProviderForm, SAML_IDP_DATA_FORM); + IdentityProvider idp = brokerIdentityProviderForm.getIdentityProvider(); + log.debug(" Create idp:{} ", idp); + checkResourceNotNull(idp, SAML_IDP_DATA); + checkNotNull(idp.getName(), "NAME"); + checkNotNull(idp.getDisplayName(), AttributeNames.DISPLAY_NAME); + checkNotNull(idp.getRealm(), Constants.REALM); + // check if IDP with same name already exists + List existingIdentityProviders = idpService.getIdentityProviderByName(idp.getName()); + log.debug(" existingIdentityProviders:{} ", existingIdentityProviders); + if (existingIdentityProviders != null && !existingIdentityProviders.isEmpty()) { + throwBadRequestException("SAML IDP with same name '" + idp.getName() + "' already exists!"); + } + + InputStream metaDataFile = brokerIdentityProviderForm.getMetaDataFile(); + log.debug(" Create metaDataFile:{} ", metaDataFile); + if (metaDataFile != null) { + log.error(" IDP metaDataFile.available():{}", metaDataFile.available()); + } + + // create SAML IDP + idp = idpService.createSamlIdentityProvider(idp, metaDataFile); + + log.info("Create IdentityProvider - idp:{}", idp); + return Response.status(Response.Status.CREATED).entity(idp).build(); + } + + @Operation(summary = "Update SAML Identity Provider", description = "Update SAML Identity Provider", operationId = "put-saml-identity-provider", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_WRITE_ACCESS })) + @RequestBody(description = "String representing patch-document.", content = @Content(mediaType = MediaType.MULTIPART_FORM_DATA, schema = @Schema(implementation = BrokerIdentityProviderForm.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/put-saml-identity-provider.json"))) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Updated Trust IDP", content = @Content(mediaType = MediaType.APPLICATION_JSON_PATCH_JSON, schema = @Schema(implementation = IdentityProvider.class), examples = @ExampleObject(name = "Response json example", value = "example/idp/trust-idp/get-saml-identity-provider-by-inum.json"))), + @ApiResponse(responseCode = "400", description = "Bad Request"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @Consumes(MediaType.MULTIPART_FORM_DATA) + @PUT + @Path(Constants.UPLOAD_PATH) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_WRITE_ACCESS }) + public Response updateSamlIdentityProvider(@MultipartForm BrokerIdentityProviderForm brokerIdentityProviderForm) + throws IOException { + if (log.isInfoEnabled()) { + log.info("Update brokerIdentityProviderForm:{}", brokerIdentityProviderForm); + } + + // validation + checkResourceNotNull(brokerIdentityProviderForm, SAML_IDP_DATA_FORM); + IdentityProvider idp = brokerIdentityProviderForm.getIdentityProvider(); + log.debug(" Update idp:{} ", idp); + + checkResourceNotNull(idp, SAML_IDP_DATA); + checkNotNull(idp.getName(), AttributeNames.NAME); + checkNotNull(idp.getDisplayName(), AttributeNames.DISPLAY_NAME); + checkNotNull(idp.getInum(), AttributeNames.INUM); + checkNotNull(idp.getRealm(), Constants.REALM); + IdentityProvider existingIdentityProvider = idpService.getIdentityProviderByInum(idp.getInum()); + + log.debug(" existingIdentityProvider:{} ", existingIdentityProvider); + checkResourceNotNull(existingIdentityProvider, SAML_IDP_CHECK_STR + idp.getInum() + "'"); + InputStream metaDataFile = brokerIdentityProviderForm.getMetaDataFile(); + log.debug(" Update metaDataFile:{} ", metaDataFile); + if (metaDataFile != null) { + log.error(" IDP metaDataFile.available():{}", metaDataFile.available()); + } + + // create SAML IDP + idp = idpService.updateSamlIdentityProvider(idp, metaDataFile); + + log.info("Updated IdentityProvider idp:{}", idp); + return Response.ok(idp).build(); + } + + @Operation(summary = "Delete SAML Identity Provider", description = "Delete SAML Identity Provider", operationId = "delete-saml-identity-provider", tags = { + "Jans - SAML Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.JANS_IDP_SAML_DELETE_ACCESS })) + @ApiResponses(value = { @ApiResponse(responseCode = "204", description = "No Content"), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "404", description = "Not Found"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @DELETE + @Path(ApiConstants.INUM_PATH) + @ProtectedApi(scopes = { Constants.JANS_IDP_SAML_DELETE_ACCESS }, groupScopes = { + ApiAccessConstants.OPENID_DELETE_ACCESS }, superScopes = { ApiAccessConstants.SUPER_ADMIN_DELETE_ACCESS }) + public Response deleteIdentityProvider( + @Parameter(description = "Unique identifier") @PathParam(ApiConstants.INUM) @NotNull String inum) { + if (log.isDebugEnabled()) { + log.debug("IdentityProvider to be deleted - inum:{} ", escapeLog(inum)); + } + IdentityProvider existingIdentityProvider = idpService.getIdentityProviderByInum(inum); + log.debug(" existingIdentityProvider:{} ", existingIdentityProvider); + checkResourceNotNull(existingIdentityProvider, SAML_IDP_CHECK_STR + inum + "'"); + + idpService.deleteIdentityProvider(existingIdentityProvider); + return Response.noContent().build(); + } + + private IdentityProviderPagedResult doSearch(SearchRequest searchReq) + throws IllegalAccessException, InvocationTargetException { + if (log.isInfoEnabled()) { + log.info("IdentityProvider search params - searchReq:{}, realm:{} ", escapeLog(searchReq)); + } + + PagedResult pagedResult = idpService.getIdentityProviders(searchReq); + if (log.isTraceEnabled()) { + log.debug("IdentityProvider PagedResult - pagedResult:{}", pagedResult); + } + + IdentityProviderPagedResult pagedIdentityProvider = new IdentityProviderPagedResult(); + if (pagedResult != null) { + log.debug("IdentityProviders fetched - pagedResult.getEntries():{}", pagedResult.getEntries()); + List identityProviderList = pagedResult.getEntries(); + pagedIdentityProvider.setStart(pagedResult.getStart()); + pagedIdentityProvider.setEntriesCount(pagedResult.getEntriesCount()); + pagedIdentityProvider.setTotalEntriesCount(pagedResult.getTotalEntriesCount()); + pagedIdentityProvider.setEntries(identityProviderList); + } + + log.info("pagedIdentityProvider:{}", pagedIdentityProvider); + return pagedIdentityProvider; + } + +} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdentityProviderService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdentityProviderService.java new file mode 100644 index 00000000000..58e99b4c3f5 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdentityProviderService.java @@ -0,0 +1,393 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.service.common.InumService; +import io.jans.as.common.service.OrganizationService; +import io.jans.as.common.util.AttributeConstants; +import io.jans.configapi.configuration.ConfigurationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.timer.IdpMetadataValidationTimer; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; + +import io.jans.model.GluuStatus; +import io.jans.model.SearchRequest; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.model.PagedResult; +import io.jans.orm.model.SortOrder; +import io.jans.orm.search.filter.Filter; +import io.jans.util.StringHelper; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; + +@ApplicationScoped +public class IdentityProviderService { + + @Inject + Logger log; + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + ConfigurationFactory configurationFactory; + + @Inject + OrganizationService organizationService; + + @Inject + private InumService inumService; + + @Inject + IdpConfigService idpConfigService; + + @Inject + SamlService samlService; + + @Inject + IdpMetadataValidationTimer idpMetadataValidationTimer; + + public String getIdentityProviderDn() { + return idpConfigService.getTrustedIdpDn(); + } + + public String getIdpMetadataFilePattern() { + return idpConfigService.getIdpMetadataFilePattern(); + } + + public boolean containsIdentityProvider(String dn) { + return persistenceEntryManager.contains(dn, IdentityProvider.class); + } + + public IdentityProvider getIdentityProviderByDn(String dn) { + if (StringHelper.isNotEmpty(dn)) { + try { + return persistenceEntryManager.find(IdentityProvider.class, dn); + } catch (Exception e) { + log.error(e.getMessage()); + } + + } + return null; + } + + public List getAllIdentityProviders() { + return persistenceEntryManager.findEntries(getDnForIdentityProvider(null), IdentityProvider.class, null); + } + + public List getAllIdentityProviders(int sizeLimit) { + return persistenceEntryManager.findEntries(getDnForIdentityProvider(null), IdentityProvider.class, null, + sizeLimit); + } + + public IdentityProvider getIdentityProviderByUnpunctuatedInum(String unpunctuated) { + for (IdentityProvider idp : getAllIdentityProviders()) { + if (StringHelper.removePunctuation(idp.getInum()).equals(unpunctuated)) { + return idp; + } + } + return null; + } + + public List getAllActiveIdentityProviders() { + IdentityProvider identityProvider = new IdentityProvider(); + identityProvider.setBaseDn(getDnForIdentityProvider(null)); + identityProvider.setStatus(GluuStatus.ACTIVE); + + return persistenceEntryManager.findEntries(identityProvider); + } + + public IdentityProvider getIdentityProviderByInum(String inum) { + IdentityProvider result = null; + try { + result = persistenceEntryManager.find(IdentityProvider.class, getDnForIdentityProvider(inum)); + } catch (Exception ex) { + log.error("Failed to load IdentityProvider entry", ex); + } + return result; + } + + public List getIdentityProviderByName(String name) { + log.info("Search IdentityProvider with name:{}", name); + + Filter nameFilter = Filter.createEqualityFilter("NAME", name); + log.debug("Search IdentityProvider with displayNameFilter:{}", nameFilter); + return persistenceEntryManager.findEntries(getDnForIdentityProvider(null), IdentityProvider.class, nameFilter); + } + + public IdentityProvider getIdentityProvider(IdentityProvider identityProvider) { + return getIdentityProviderByDn(identityProvider.getDn()); + } + + public IdentityProvider getIdentityProvider(String dn) { + return getIdentityProviderByDn(dn); + } + + public List searchIdentityProvider(String pattern, int sizeLimit) { + + log.info("Search IdentityProvider with pattern:{}, sizeLimit:{}", pattern, sizeLimit); + + String[] targetArray = new String[] { pattern }; + Filter nameFilter = Filter.createSubstringFilter("NAME", null, targetArray, null); + Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, targetArray, + null); + Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, targetArray, + null); + Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); + Filter searchFilter = Filter.createORFilter(nameFilter, displayNameFilter, descriptionFilter, inumFilter); + + log.trace("Search IdentityProvider with searchFilter:{}", searchFilter); + return persistenceEntryManager.findEntries(getDnForIdentityProvider(null), IdentityProvider.class, searchFilter, + sizeLimit); + } + + public List getAllIdentityProvider(int sizeLimit) { + return persistenceEntryManager.findEntries(getDnForIdentityProvider(null), IdentityProvider.class, null, + sizeLimit); + } + + public PagedResult getIdentityProvider(SearchRequest searchRequest) { + log.info("Search IdentityProvider with searchRequest:{}", searchRequest); + + Filter searchFilter = null; + List filters = new ArrayList<>(); + if (searchRequest.getFilterAssertionValue() != null && !searchRequest.getFilterAssertionValue().isEmpty()) { + + for (String assertionValue : searchRequest.getFilterAssertionValue()) { + String[] targetArray = new String[] { assertionValue }; + Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, + targetArray, null); + Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, + targetArray, null); + Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); + filters.add(Filter.createORFilter(displayNameFilter, descriptionFilter, inumFilter)); + } + searchFilter = Filter.createORFilter(filters); + } + + log.debug("IdentityProvider pattern searchFilter:{}", searchFilter); + List fieldValueFilters = new ArrayList<>(); + if (searchRequest.getFieldValueMap() != null && !searchRequest.getFieldValueMap().isEmpty()) { + for (Map.Entry entry : searchRequest.getFieldValueMap().entrySet()) { + Filter dataFilter = Filter.createEqualityFilter(entry.getKey(), entry.getValue()); + log.debug("IdentityProvider dataFilter:{}", dataFilter); + fieldValueFilters.add(Filter.createANDFilter(dataFilter)); + } + searchFilter = Filter.createANDFilter(Filter.createORFilter(filters), + Filter.createANDFilter(fieldValueFilters)); + } + + log.debug("IdentityProvider searchFilter:{}", searchFilter); + + return persistenceEntryManager.findPagedEntries(getDnForIdentityProvider(null), IdentityProvider.class, + searchFilter, null, searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), + searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); + + } + + public IdentityProvider addSamlIdentityProvider(IdentityProvider identityProvider, InputStream file) + throws IOException { + log.info("Add new identityProvider:{}, file:{}", identityProvider, file); + + String inum = generateInumForIdentityProvider(); + identityProvider.setInum(inum); + identityProvider.setDn(getDnForIdentityProvider(inum)); + + if (file != null) { + log.info("Save IDP metadatfile on server"); + saveIdpMetaDataFileSourceTypeFile(identityProvider, file); + } + + // Set default Value for SAML IDP + setSamlIdentityProviderDefaultValue(identityProvider, false); + persistenceEntryManager.persist(identityProvider); + + return getIdentityProviderByInum(identityProvider.getInum()); + } + + public IdentityProvider updateIdentityProvider(IdentityProvider identityProvider) throws IOException { + return updateIdentityProvider(identityProvider, null); + } + + public IdentityProvider updateIdentityProvider(IdentityProvider identityProvider, InputStream file) + throws IOException { + + if (identityProvider == null) { + return identityProvider; + } + if (file != null && file.available() > 0) { + saveIdpMetaDataFileSourceTypeFile(identityProvider, file); + } + + // Set default Value for SAML IDP + setSamlIdentityProviderDefaultValue(identityProvider, true); + persistenceEntryManager.merge(identityProvider); + + return getIdentityProviderByInum(identityProvider.getInum()); + + } + + public void removeIdentityProvider(IdentityProvider identityProvider) { + persistenceEntryManager.removeRecursively(identityProvider.getDn(), IdentityProvider.class); + + } + + public String getDnForIdentityProvider(String inum) { + String orgDn = organizationService.getDnForOrganization(); + if (StringHelper.isEmpty(inum)) { + return String.format("ou=trusted-idp,%s", orgDn); + } + return String.format("inum=%s,ou=trusted-idp,%s", inum, orgDn); + } + + public String generateInumForIdentityProvider() { + String newInum = null; + String newDn = null; + do { + newInum = UUID.randomUUID().toString(); + newDn = getDnForIdentityProvider(newInum); + } while (this.containsIdentityProvider(newDn)); + + return newInum; + } + + public String generateInumForNewIdentityProvider() { + String newInum = null; + String newDn = null; + int trycount = 0; + do { + if (trycount < InumService.MAX_IDGEN_TRY_COUNT) { + newInum = inumService.generateId("idp"); + trycount++; + } else { + newInum = inumService.generateDefaultId(); + } + newDn = getDnForIdentityProvider(newInum); + } while (persistenceEntryManager.contains(newDn, Client.class)); + return newInum; + } + + private IdentityProvider setSamlIdentityProviderDefaultValue(IdentityProvider identityProvider, boolean update) { + log.info("setting default value for identityProvider:{}, update:{}", identityProvider, update); + if (identityProvider == null) { + return identityProvider; + } + if (!update) { + identityProvider.setProviderId(Constants.SAML); + } + return identityProvider; + } + + private boolean saveIdpMetaDataFileSourceTypeFile(IdentityProvider identityProvider, InputStream file) { + log.info("Saving file identityProvider:{}, file:{}", identityProvider, file); + + String idpMetaDataFN = identityProvider.getIdpMetaDataFN(); + log.debug("idpMetaDataFN:{}", idpMetaDataFN); + + boolean emptyidpMetaDataFN = StringHelper.isEmpty(idpMetaDataFN); + log.debug("emptyidpMetaDataFN:{}", emptyidpMetaDataFN); + if ((file == null)) { + log.debug("File is null"); + if (emptyidpMetaDataFN) { + log.error("The trust relationship {} has an empty Metadata filename", identityProvider.getInum()); + return false; + } + String filePath = idpConfigService.getIdpMetadataTempDirFilePath(idpMetaDataFN); + log.debug("filePath:{}", filePath); + + if (filePath == null) { + log.error("The trust relationship {} has an invalid Metadata file storage path", + identityProvider.getInum()); + return false; + } + + if (samlService.isLocalDocumentStoreType()) { + + File newFile = new File(filePath); + log.trace("newFile:{}", newFile); + + if (!newFile.exists()) { + log.error( + "The trust relationship {} metadata used local storage but the IDP metadata file `{}` was not found", + identityProvider.getInum(), filePath); + return false; + } + } + return true; + } + if (emptyidpMetaDataFN) { + log.info("File name is blank emptyidpMetaDataFN:{}", emptyidpMetaDataFN); + idpMetaDataFN = getIdpNewMetadataFileName(identityProvider); + log.debug("Final idpMetaDataFN:{}", idpMetaDataFN); + identityProvider.setIdpMetaDataFN(idpMetaDataFN); + + } + InputStream targetStream = file; + log.debug("targetStream:{}, idpMetaDataFN:{}", targetStream, idpMetaDataFN); + + String result = samlService.saveMetadataFile(Constants.IDP_MODULE, idpConfigService.getIdpMetadataTempDir(), + idpMetaDataFN, targetStream); + log.debug("targetStream:{}, idpMetaDataFN:{}", targetStream, idpMetaDataFN); + if (StringHelper.isNotEmpty(result)) { + idpMetadataValidationTimer.queue(result); + // process files in temp that were not processed earlier + processUnprocessedIdpMetadataFiles(); + } else { + log.error("Failed to save IDP meta-data file. Please check if you provide correct file"); + } + + return false; + + } + + private String getIdpNewMetadataFileName(IdentityProvider identityProvider) { + String idpMetaDataFN = null; + if (identityProvider == null) { + return idpMetaDataFN; + } + log.info("idpConfigService.getIdpMetadataFileName(identityProvider.getInum()):{}", + idpConfigService.getIdpMetadataFileName(identityProvider.getInum())); + return idpConfigService.getIdpMetadataFileName(identityProvider.getInum()); + } + + public void processUnprocessedIdpMetadataFiles() { + log.info("processing unprocessed IDP metadata files "); + String directory = idpConfigService.getIdpMetadataTempDir(); + log.trace("directory:{}, Files.exists(Paths.get(directory):{}", directory, Files.exists(Paths.get(directory))); + + if (Files.exists(Paths.get(directory))) { + log.trace("directory:{} does exists)", directory); + File folder = new File(directory); + File[] files = folder.listFiles(); + log.trace("files:{}", files); + if (files != null && files.length > 0) { + + for (File file : files) { + log.trace("file:{}, file.getName():{}", file, file.getName()); + idpMetadataValidationTimer.queue(file.getName()); + } + } + + } + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpConfigService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpConfigService.java new file mode 100644 index 00000000000..29b34576e55 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpConfigService.java @@ -0,0 +1,166 @@ +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.as.common.service.common.ApplicationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.configuration.IdpConfigurationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpAppConfiguration; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpConf; +import io.jans.orm.PersistenceEntryManager; +import io.jans.util.StringHelper; +import io.jans.util.exception.InvalidConfigurationException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import java.io.File; + +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; + +@ApplicationScoped +public class IdpConfigService { + + @Inject + Logger logger; + + @Inject + @Named(ApplicationFactory.PERSISTENCE_ENTRY_MANAGER_NAME) + PersistenceEntryManager persistenceManager; + + @Inject + IdpConfigurationFactory idpConfigurationFactory; + + public IdpConf findIdpConf() { + final String dn = idpConfigurationFactory.getIdpConfigurationDn(); + if (StringUtils.isBlank(dn)) { + throw new InvalidConfigurationException("IDP Configuration DN is undefined!"); + } + + logger.info(" dn:{}", dn); + IdpConf idpConf = persistenceManager.find(dn, IdpConf.class, null); + logger.info(" idpConf:{}", idpConf); + + return idpConf; + } + + public IdpConf getIdpConf() { + final IdpConf idpConf = findIdpConf(); + if (idpConf == null) { + throw new InvalidConfigurationException("IdpConf is undefined!"); + } + logger.debug(" idpConf:{}, IdpConf.getDynamicConf():{}", idpConf, idpConf.getDynamicConf()); + return idpConf; + } + + public void mergeIdpConfig(IdpConf idpConf) { + idpConf.setRevision(idpConf.getRevision() + 1); + persistenceManager.merge(idpConf); + } + + public IdpAppConfiguration getIdpAppConfiguration() { + return getIdpConf().getDynamicConf(); + } + + public boolean isIdpEnabled() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + boolean idpEnabled = false; + if (idpAppConfiguration != null) { + idpEnabled = idpAppConfiguration.isEnabled(); + } + return idpEnabled; + } + + public String getTrustedIdpDn() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String trustRelationshipDn = null; + if (idpAppConfiguration != null) { + trustRelationshipDn = idpAppConfiguration.getTrustedIdpDn(); + } + return trustRelationshipDn; + } + + public String getRealmDn() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String realmDn = null; + if (idpAppConfiguration != null) { + realmDn = idpAppConfiguration.getRealmDn(); + } + return realmDn; + } + + public String getIdpRootDir() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String idpRootDir = null; + if (idpAppConfiguration != null) { + idpRootDir = idpAppConfiguration.getIdpRootDir(); + } + return idpRootDir; + } + + public String getIdpMetadataRootDir() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String idpMetadataRootDir = null; + if (idpAppConfiguration != null) { + idpMetadataRootDir = idpAppConfiguration.getIdpMetadataRootDir(); + } + return idpMetadataRootDir; + } + + public String getIdpMetadataTempDir() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String idpMetadataTempDir = null; + if (idpAppConfiguration != null) { + idpMetadataTempDir = idpAppConfiguration.getIdpMetadataTempDir(); + } + return idpMetadataTempDir; + } + + public String getIdpMetadataFilePattern() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String idpMetadataFilePattern = null; + if (idpAppConfiguration != null) { + idpMetadataFilePattern = idpAppConfiguration.getIdpMetadataFilePattern(); + } + return idpMetadataFilePattern; + } + + public String getIdpMetadataFile() { + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String idpMetadataFile = null; + if (idpAppConfiguration != null) { + idpMetadataFile = idpAppConfiguration.getIdpMetadataFile(); + } + return idpMetadataFile; + } + + public String getIdpMetadataTempDirFilePath(String idpMetaDataFN) { + logger.debug("idpMetaDataFN:{}, getIdpMetadataTempDirFilePath():{}", idpMetaDataFN, getIdpMetadataTempDirFilePath()); + if (StringUtils.isBlank(getIdpMetadataTempDirFilePath())) { + throw new InvalidConfigurationException("Failed to return IDP metadata file path as undefined!"); + } + + return getIdpMetadataTempDirFilePath() + idpMetaDataFN; + } + + public String getIdpMetadataTempDirFilePath() { + return getIdpMetadataTempDir() + File.separator; + } + + public String getIdpMetadataFileName(String inum) { + String id = StringHelper.removePunctuation(inum); + return String.format(getIdpMetadataFilePattern(), id); + } + + public String getSpMetadataUrl(String realm, String name) { + logger.debug("Get SP Metadata Url - realm:{}, name:{}", realm, name); + IdpAppConfiguration idpAppConfiguration = getIdpAppConfiguration(); + String spMetadataUrl = null; + if (idpAppConfiguration != null) { + StringBuilder sb = new StringBuilder(); + sb.append(idpAppConfiguration.getServerUrl()).append(idpAppConfiguration.getSpMetadataUrl()); + spMetadataUrl = String.format(sb.toString(), realm, name); + } + logger.debug("SP Metadata Url - spMetadataUrl:{}", spMetadataUrl); + return spMetadataUrl; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpService.java new file mode 100644 index 00000000000..3a20b07f126 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/IdpService.java @@ -0,0 +1,247 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.as.common.model.registration.Client; +import io.jans.as.common.service.common.InumService; +import io.jans.as.common.service.OrganizationService; +import io.jans.as.common.util.AttributeConstants; +import io.jans.configapi.configuration.ConfigurationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.client.IdpClientFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.mapper.IdentityProviderMapper; +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpConfigService; +import io.jans.configapi.plugin.keycloak.idp.broker.service.SamlService; +import io.jans.configapi.plugin.keycloak.idp.broker.timer.IdpMetadataValidationTimer; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; + +import io.jans.model.GluuStatus; +import io.jans.model.SearchRequest; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.model.PagedResult; +import io.jans.orm.model.SortOrder; +import io.jans.orm.search.filter.Filter; +import io.jans.util.StringHelper; +import io.jans.util.exception.InvalidAttributeException; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +import jakarta.ws.rs.core.Response; + +import org.apache.commons.lang.StringUtils; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.filefilter.AgeFileFilter; +import org.apache.commons.io.filefilter.TrueFileFilter; +import org.slf4j.Logger; + +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +@ApplicationScoped +public class IdpService { + + @Inject + Logger log; + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + IdpConfigService idpConfigService; + + @Inject + OrganizationService organizationService; + + @Inject + IdentityProviderService identityProviderService; + + @Inject + KeycloakService keycloakService; + + @Inject + IdentityProviderMapper identityProviderMapper; + + @Inject + IdpClientFactory idpClientFactory; + + public String getIdentityProviderDn() { + return idpConfigService.getTrustedIdpDn(); + } + + public String getSpMetadataUrl(String realm, String name) { + return idpConfigService.getSpMetadataUrl(realm, name); + } + + public List getAllIdentityProviders() { + return this.identityProviderService.getAllIdentityProvider(0); + } + + public IdentityProvider getIdentityProviderByInum(String inum) { + return identityProviderService.getIdentityProviderByInum(inum); + } + + public List getIdentityProviderByName(String name) { + return identityProviderService.getIdentityProviderByName(name); + } + + public PagedResult getIdentityProviders(SearchRequest searchRequest) { + return identityProviderService.getIdentityProvider(searchRequest); + } + + public IdentityProvider createSamlIdentityProvider(IdentityProvider identityProvider, InputStream idpMetadataStream) + throws IOException { + log.info( + "Create IdentityProvider with IDP metadata file in identityProvider:{}, idpMetadataStream:{}, idpConfigService.isIdpEnabled():{}", + identityProvider, idpMetadataStream, idpConfigService.isIdpEnabled()); + + // validate + if (identityProvider == null) { + throw new InvalidAttributeException("IdentityProvider object is null!!!"); + } + + if (idpMetadataStream == null) { + throw new InvalidAttributeException("Idp Metedata file is null!!!"); + } + + // validate metadata and set in config + Map config = validateSamlMetadata(identityProvider.getRealm(), idpMetadataStream); + log.debug("Validated metadata to create IDP - config:{}", config); + identityProvider.setConfig(config); + + // Create IDP in Jans DB + log.debug("Create IdentityProvider identityProvider:{})", identityProvider); + identityProviderService.addSamlIdentityProvider(identityProvider, idpMetadataStream); + log.debug("Created IdentityProvider in Jans DB - identityProvider:{})", identityProvider); + + if (idpConfigService.isIdpEnabled()) { + // Create IDP in KC + IdentityProviderRepresentation kcIdp = this.convertToIdentityProviderRepresentation(identityProvider); + log.debug("converted kcIdp:{}", kcIdp); + + log.debug("IDP Service idpMetadataStream:{}, idpMetadataStream.available():{}", idpMetadataStream, + idpMetadataStream.available()); + kcIdp = keycloakService.createIdentityProvider(identityProvider.getRealm(), kcIdp); + log.debug("Newly created kcIdp:{}", kcIdp); + identityProvider = this.convertToIdentityProvider(kcIdp); + log.debug("Final created identityProvider:{}", identityProvider); + + } + return identityProvider; + } + + public IdentityProvider updateSamlIdentityProvider(IdentityProvider identityProvider, InputStream idpMetadataStream) + throws IOException { + log.debug( + "Update IdentityProvider with IDP metadata file in - identityProvider:{}, idpMetadataStream:{}, idpConfigService.isIdpEnabled():{}", + identityProvider, idpMetadataStream, idpConfigService.isIdpEnabled()); + + // validate + if (identityProvider == null) { + throw new InvalidAttributeException("IdentityProvider object for update is null!!!"); + } + + if (idpMetadataStream == null) { + throw new InvalidAttributeException("Idp Metedata file for update is null!!!"); + } + + // validate metadata and set in config + Map config = validateSamlMetadata(identityProvider.getRealm(), idpMetadataStream); + log.debug("Validated metadata to update config:{}", config); + identityProvider.setConfig(config); + + // Update IDP in Jans DB + updateIdentityProvider(identityProvider); + log.debug("Updated IdentityProvider dentityProvider:{})", identityProvider); + + if (idpConfigService.isIdpEnabled()) { + // Update IDP in KC + IdentityProviderRepresentation kcIdp = this.convertToIdentityProviderRepresentation(identityProvider); + log.debug("converted kcIdp:{}", kcIdp); + + kcIdp = keycloakService.updateIdentityProvider(identityProvider.getRealm(), kcIdp); + log.debug("Updated kcIdp:{}", kcIdp); + identityProvider = this.convertToIdentityProvider(kcIdp); + } + return identityProvider; + } + + public void deleteIdentityProvider(IdentityProvider identityProvider) { + + if (idpConfigService.isIdpEnabled()) { + // Delete IDP in KC + keycloakService.deleteIdentityProvider(identityProvider.getRealm(), identityProvider.getName()); + } + // Delete in Jans DB + identityProviderService.removeIdentityProvider(identityProvider); + } + + public Response getSpMetadata(IdentityProvider identityProvider) { + Response response = null; + if (identityProvider == null) { + return response; + } + return idpClientFactory + .getSpMetadata(getSpMetadataUrl(identityProvider.getRealm(), identityProvider.getName())); + + } + + private IdentityProvider updateIdentityProvider(IdentityProvider identityProvider) throws IOException { + log.info("Update IdentityProvider with IDP metadata file in identityProvider:{}", identityProvider); + + // Update IDP in Jans DB + identityProviderService.updateIdentityProvider(identityProvider); + log.debug("Updated IdentityProvider in Jans DB - identityProvider:{})", identityProvider); + + return identityProvider; + } + + private Map validateSamlMetadata(String realmName, InputStream idpMetadataStream) { + return keycloakService.validateSamlMetadata(realmName, idpMetadataStream); + } + + private IdentityProvider convertToIdentityProvider(IdentityProviderRepresentation kcIdp) { + log.debug("kcIdp:{}", kcIdp); + IdentityProvider idp = null; + if (kcIdp == null) { + return idp; + } + idp = identityProviderMapper.kcIdentityProviderToIdentityProvider(kcIdp); + log.info("convertToIdentityProvider - idp:{}", idp); + + return idp; + } + + private IdentityProviderRepresentation convertToIdentityProviderRepresentation(IdentityProvider idp) { + log.info("idp:{}", idp); + IdentityProviderRepresentation kcIdp = null; + if (idp == null) { + return kcIdp; + } + kcIdp = identityProviderMapper.identityProviderToKCIdentityProvider(idp); + log.debug("convert IdentityProviderRepresentation - kcIdp:{}", kcIdp); + + log.trace( + "convert IDP data kcIdp.getAlias():{}, kcIdp.getInternalId():{}, kcIdp.getProviderId():{}, kcIdp.getConfig():{}, kcIdp.isEnabled():{}, kcIdp.isLinkOnly():{}, kcIdp.isStoreToken():{},kcIdp.getFirstBrokerLoginFlowAlias():{}, kcIdp.getPostBrokerLoginFlowAlias():{},kcIdp.isTrustEmail():{}", + kcIdp.getAlias(), kcIdp.getInternalId(), kcIdp.getProviderId(), kcIdp.getConfig(), kcIdp.isEnabled(), + kcIdp.isLinkOnly(), kcIdp.isStoreToken(), kcIdp.getFirstBrokerLoginFlowAlias(), + kcIdp.getPostBrokerLoginFlowAlias(), kcIdp.isTrustEmail()); + + return kcIdp; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/KeycloakService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/KeycloakService.java new file mode 100644 index 00000000000..4f0cc2b5444 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/KeycloakService.java @@ -0,0 +1,439 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.configapi.plugin.keycloak.idp.broker.configuration.KeycloakConfig; +import io.jans.configapi.plugin.keycloak.idp.broker.util.Constants; +import io.jans.util.exception.ConfigurationException; +import io.jans.util.exception.InvalidAttributeException; + +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.util.*; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.MultivaluedHashMap; +import jakarta.ws.rs.core.Response; +import jakarta.ws.rs.core.Response.Status; +import jakarta.ws.rs.core.Response.StatusType; +import jakarta.ws.rs.WebApplicationException; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataOutput; +import org.keycloak.admin.client.resource.IdentityProviderResource; +import org.keycloak.admin.client.resource.IdentityProvidersResource; +import org.keycloak.admin.client.resource.RealmResource; +import org.keycloak.admin.client.resource.RealmsResource; +import org.keycloak.representations.idm.IdentityProviderMapperRepresentation; +import org.keycloak.representations.idm.IdentityProviderMapperTypeRepresentation; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; + +import org.slf4j.Logger; + +@ApplicationScoped +public class KeycloakService { + + @Inject + Logger logger; + + @Inject + KeycloakConfig keycloakConfig; + + private RealmResource getRealmResource(String realm) { + logger.info("Get RealmResource for realm:{}", realm); + if (StringUtils.isBlank(realm)) { + realm = Constants.REALM_MASTER; + } + RealmResource realmResource = keycloakConfig.getInstance().realm(realm); + logger.debug("realmResource:{}", realmResource); + return realmResource; + } + + public List getAllRealms() { + logger.info("Get All KC Realms"); + List realmRepresentationList = keycloakConfig.getInstance().realms().findAll(); + + logger.debug("realmRepresentationList:{}", realmRepresentationList); + return realmRepresentationList; + } + + public RealmRepresentation getRealmByName(String realmName) { + logger.info("Get RealmResource for realmName:{}", realmName); + + List realms = getAllRealms(); + RealmRepresentation realmRepresentation = null; + if (realms == null || realms.isEmpty()) { + return realmRepresentation; + } + for (RealmRepresentation realm : realms) { + if (realmName.equals(realm.getRealm())) { + realmRepresentation = realm; + break; + } + } + logger.debug("Realm fetched by name is - realmRepresentation:{}", realmRepresentation); + return realmRepresentation; + } + + public RealmRepresentation createNewRealm(RealmRepresentation realmRepresentation) { + logger.info("Create realmRepresentation:{}", realmRepresentation); + if (realmRepresentation == null) { + throw new InvalidAttributeException("RealmRepresentation is null"); + } + keycloakConfig.getInstance().realms().create(realmRepresentation); + + realmRepresentation = getRealmByName(realmRepresentation.getRealm()); + logger.info("Realm created is - realmRepresentation:{}", realmRepresentation); + return realmRepresentation; + } + + public RealmRepresentation updateRealm(RealmRepresentation realmRepresentation) { + logger.info("Updade realmRepresentation:{}", realmRepresentation); + if (realmRepresentation == null) { + throw new InvalidAttributeException("RealmRepresentation is null"); + } + RealmResource realmResource = this.getRealmResource(realmRepresentation.getRealm()); + logger.debug("realmResource:{}", realmResource); + realmResource.update(realmRepresentation); + realmRepresentation = realmResource.toRepresentation(); + logger.info("Updated realmRepresentation:{}", realmRepresentation); + return realmRepresentation; + } + + public void deleteRealm(String realmName) { + logger.info("Delete Realm by name realmName:{}", realmName); + + if (StringUtils.isBlank(realmName)) { + throw new InvalidAttributeException("Realm name is null!!!"); + } + keycloakConfig.getInstance().realm(realmName).remove(); + return; + } + + public IdentityProvidersResource getIdentityProvidersResource(String realmName) { + if (StringUtils.isBlank(realmName)) { + throw new InvalidAttributeException("Realm name is null!!!"); + } + IdentityProvidersResource identityProvidersResource = getRealmResource(realmName).identityProviders(); + logger.debug("identityProvidersResource:{}", identityProvidersResource); + return identityProvidersResource; + } + + public List findAllIdentityProviders(String realmName) { + logger.info("Fetch all IdentityProvider for realmName:{}", realmName); + + IdentityProvidersResource identityProvidersResource = this.getIdentityProvidersResource(realmName); + List identityProviders = identityProvidersResource.findAll(); + + logger.info("identityProviders:{}", identityProviders); + + return identityProviders; + } + + public IdentityProviderRepresentation getIdentityProviderById(String realmName, String internalId) { + logger.info("Fetch IdentityProvider by id realmName:{}, internalId:{}", realmName, internalId); + + if (StringUtils.isBlank(realmName) || StringUtils.isBlank(internalId)) { + throw new InvalidAttributeException("Realm name or IdentityProvider internalId is null!!!"); + } + + List identityProviders = findAllIdentityProviders(realmName); + logger.debug("identityProviders:{}", identityProviders); + IdentityProviderRepresentation identityProvider = null; + if (identityProviders == null || identityProviders.isEmpty()) { + return identityProvider; + } + + for (IdentityProviderRepresentation data : identityProviders) { + if (internalId.equals(data.getInternalId())) { + identityProvider = data; + break; + } + } + logger.info("IdentityProvider fetched by id realmName:{}, internalId:{}, identityProvider:{}", realmName, + internalId, identityProvider); + return identityProvider; + } + + public IdentityProviderRepresentation getIdentityProviderByName(String realmName, String alias) { + logger.info("Get IdentityProvider by name realmName:{}, alias:{}", realmName, alias); + + if (StringUtils.isBlank(realmName) || StringUtils.isBlank(alias)) { + throw new InvalidAttributeException("Realm name or IdentityProvider alias is null!!!"); + } + + List identityProviders = findAllIdentityProviders(realmName); + logger.debug("identityProviders:{}", identityProviders); + IdentityProviderRepresentation identityProvider = null; + if (identityProviders == null || identityProviders.isEmpty()) { + return identityProvider; + } + + for (IdentityProviderRepresentation data : identityProviders) { + if (alias.equals(data.getAlias())) { + identityProvider = data; + break; + } + } + + logger.debug("IdentityProvider fetched by name realmName:{}, alias:{}, identityProvider:{}", realmName, alias, + identityProvider); + return identityProvider; + } + + public Map validateSamlMetadata(String realmName, InputStream idpMetadataStream) { + Map config = null; + try { + logger.info("Verify Saml Idp Metadata realmName:{}, idpMetadataStream:{}", realmName, idpMetadataStream); + + if (idpMetadataStream == null) { + throw new InvalidAttributeException("Idp Metedata file is null!!!"); + } + + MultipartFormDataOutput form = new MultipartFormDataOutput(); + form.addFormData("providerId", "saml", MediaType.TEXT_PLAIN_TYPE); + logger.debug("SAML idpMetadataStream.available():{}", idpMetadataStream.available()); + + byte[] content = idpMetadataStream.readAllBytes(); + logger.debug("content:{}", content); + String body = new String(content, Charset.forName("utf-8")); + form.addFormData("file", body, MediaType.APPLICATION_XML_TYPE, "saml-idp-metadata.xml"); + + IdentityProvidersResource identityProvidersResource = this.getIdentityProvidersResource(realmName); + if (identityProvidersResource == null) { + return config; + } + config = identityProvidersResource.importFrom(form); + logger.debug("IDP metadata importConfig config:{})", config); + boolean valid = verifySamlIdpConfig(config); + logger.debug("Is IDP metadata config valid:{})", valid); + if (!valid) { + throw new InvalidAttributeException("Idp Metedata file is not valid !!!"); + } + + } catch (Exception ex) { + throw new ConfigurationException("Error while validating SAML IDP Metadata", ex); + } + + return config; + } + + public IdentityProviderRepresentation createIdentityProvider(String realmName, + IdentityProviderRepresentation identityProviderRepresentation) { + + try { + logger.info("Create new IdentityProvider under realmName:{}, identityProviderRepresentation:{})", + realmName, identityProviderRepresentation); + + if (StringUtils.isBlank(realmName)) { + throw new InvalidAttributeException("Realm name is null!!!"); + } + + if (identityProviderRepresentation == null) { + throw new InvalidAttributeException("IdentityProviderRepresentation is null!!!"); + } + + // validate IDP metadata + logger.debug("IDP metadata config identityProviderRepresentation.getConfig():{})", + identityProviderRepresentation.getConfig()); + if (identityProviderRepresentation.getConfig() == null + || identityProviderRepresentation.getConfig().isEmpty()) { + throw new InvalidAttributeException("Idp Metedata config is null!!!"); + } + + boolean valid = verifySamlIdpConfig(identityProviderRepresentation.getConfig()); + logger.debug("Is IDP metadata config valid:{})", valid); + if (!valid) { + throw new InvalidAttributeException("Idp Metedata file is not valid !!!"); + } + + // create Identity Provider + IdentityProvidersResource identityProvidersResource = this.getIdentityProvidersResource(realmName); + if (identityProvidersResource == null) { + throw new ConfigurationException( + "identityProvidersResource are null, could not create Identity Provider!!!"); + } + logger.trace( + "IDP data identityProviderRepresentation.getAlias():{}, identityProviderRepresentation.getInternalId():{}, identityProviderRepresentation.getProviderId():{}, identityProviderRepresentation.getConfig():{}, identityProviderRepresentation.isEnabled():{}, identityProviderRepresentation.isLinkOnly():{}, identityProviderRepresentation.isStoreToken():{},identityProviderRepresentation.getFirstBrokerLoginFlowAlias():{}, identityProviderRepresentation.getPostBrokerLoginFlowAlias():{},identityProviderRepresentation.isTrustEmail():{}", + identityProviderRepresentation.getAlias(), identityProviderRepresentation.getInternalId(), + identityProviderRepresentation.getProviderId(), identityProviderRepresentation.getConfig(), + identityProviderRepresentation.isEnabled(), identityProviderRepresentation.isLinkOnly(), + identityProviderRepresentation.isStoreToken(), + identityProviderRepresentation.getFirstBrokerLoginFlowAlias(), + identityProviderRepresentation.getPostBrokerLoginFlowAlias(), + identityProviderRepresentation.isTrustEmail()); + Response response = identityProvidersResource.create(identityProviderRepresentation); + + logger.debug("IdentityProvider creation response:{}", response); + if (response != null) { + logger.debug("IdentityProvider creation response.getStatusInfo():{}, response.getEntity():{}", + response.getStatusInfo(), response.getEntity()); + + String id = getCreatedId(response); + logger.debug("IdentityProvider creation id():{}", id); + + List identityProvider = findAllIdentityProviders(realmName); + if (identityProvider != null && !identityProvider.isEmpty()) { + identityProvider.stream() + .forEach(e -> System.out.println(e.getInternalId() + "::" + e.getDisplayName())); + } + + identityProviderRepresentation = getIdentityProviderByName(realmName, + identityProviderRepresentation.getAlias()); + logger.debug("Final identityProviderRepresentation:{}", identityProviderRepresentation); + + response.close(); + + } + } catch (Exception ex) { + throw new ConfigurationException("Error while creating SAML IDP ", ex); + } + + return identityProviderRepresentation; + } + + public IdentityProviderRepresentation updateIdentityProvider(String realmName, + IdentityProviderRepresentation identityProviderRepresentation) { + logger.info("Update IdentityProvider under realmName:{}, identityProviderRepresentation:{}", realmName, + identityProviderRepresentation); + + // validations + if (StringUtils.isBlank(realmName)) { + throw new InvalidAttributeException("Realm name is null!!!"); + } + + if (identityProviderRepresentation == null) { + throw new InvalidAttributeException("IdentityProviderRepresentation for updation is null!!!"); + } + + // validate IDP metadata + logger.debug("IDP metadata config while update identityProviderRepresentation.getConfig():{}", + identityProviderRepresentation.getConfig()); + if (identityProviderRepresentation.getConfig() == null + || identityProviderRepresentation.getConfig().isEmpty()) { + throw new InvalidAttributeException("Idp Metedata config is null!!!"); + } + + boolean valid = verifySamlIdpConfig(identityProviderRepresentation.getConfig()); + logger.debug("Is IDP metadata config valid?:{})", valid); + if (!valid) { + throw new InvalidAttributeException("Idp Metedata file is not valid !!!"); + } + + // validate IDP to update + IdentityProvidersResource identityProvidersResource = this.getIdentityProvidersResource(realmName); + if (identityProvidersResource == null) { + throw new ConfigurationException( + "identityProvidersResource is null, could not update Identity Provider!!!"); + } + + IdentityProviderResource identityProviderResource = identityProvidersResource + .get(identityProviderRepresentation.getAlias()); + logger.debug( + "Is IDP resource present for update identityProviderRepresentation.getAlias():{}, identityProviderResource:{}", + identityProviderRepresentation.getAlias(), identityProviderResource); + if (identityProviderResource == null) { + throw new InvalidAttributeException("IdentityProvider not found to update!!!"); + } + + // update + identityProviderResource.update(identityProviderRepresentation); + identityProviderRepresentation = identityProviderResource.toRepresentation(); + + logger.info( + "Updated IdentityProvider identityProviderRepresentation.getAlias():{} under realmName:{} is identityProviderRepresentation:{}", + identityProviderRepresentation.getAlias(), realmName, identityProviderRepresentation); + + return identityProviderRepresentation; + } + + public void deleteIdentityProvider(String realmName, String alias) { + logger.info("IdentityProvider to delete realmName:{}, alias:{}", realmName, alias); + if (StringUtils.isBlank(realmName) || StringUtils.isBlank(alias)) { + throw new InvalidAttributeException("Realm name or IdentityProvider alias is null!!!"); + } + + IdentityProvidersResource identityProvidersResource = this.getIdentityProvidersResource(realmName); + if (identityProvidersResource == null) { + throw new ConfigurationException( + "IdentityProvidersResource is null, could not delete Identity Provider!!!"); + } + + logger.debug( + "IdentityProviderResource fetched for delete realmName:{}, alias:{}, identityProvidersResource:{} ", + realmName, alias, identityProvidersResource); + + IdentityProviderResource identityProviderResource = identityProvidersResource.get(alias); + + if (identityProviderResource == null) { + throw new InvalidAttributeException("IdentityProvidersResource not found to delete!!!"); + } + identityProviderResource.remove(); + logger.debug("Deleted IdentityProvider under realmName:{}, alias:{}", realmName, alias); + + IdentityProviderRepresentation identityProviderRepresentation = getIdentityProviderByName(realmName, alias); + logger.debug("Checking identityProvider is deleted - identityProviderRepresentation:{}", + identityProviderRepresentation); + + if (identityProviderResource != null) { + throw new InvalidAttributeException("IdentityProviders could not be deleted!!!"); + } + + return; + } + + public void getSAMLServiceProviderMetadata(String realmName, String alias) { + // To-do + + } + + private static String getCreatedId(Response response) { + URI location = response.getLocation(); + if (!response.getStatusInfo().equals(Status.CREATED)) { + StatusType statusInfo = response.getStatusInfo(); + throw new WebApplicationException("Create method returned status " + statusInfo.getReasonPhrase() + + " (Code: " + statusInfo.getStatusCode() + "); expected status: Created (201)", response); + } + if (location == null) { + return null; + } + String path = location.getPath(); + return path.substring(path.lastIndexOf('/') + 1); + } + + private boolean verifySamlIdpConfig(Map config) { + // import endpoint simply converts IDPSSODescriptor into key value pairs. + // check that saml-idp-metadata.xml was properly converted into key value pairs + logger.debug("verifySamlConfig - config:{}", config); + if (config == null || config.isEmpty()) { + return false; + } + logger.debug("config.keySet().containsAll(Constants.SAML_IDP_CONFIG):{}", + config.keySet().containsAll(Constants.SAML_IDP_CONFIG)); + + return config.keySet().containsAll(Constants.SAML_IDP_CONFIG); + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/RealmService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/RealmService.java new file mode 100644 index 00000000000..0190db994a4 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/RealmService.java @@ -0,0 +1,223 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.as.common.service.OrganizationService; +import io.jans.as.common.util.AttributeConstants; +import io.jans.configapi.configuration.ConfigurationFactory; +import io.jans.configapi.plugin.keycloak.idp.broker.model.Realm; +import io.jans.configapi.plugin.keycloak.idp.broker.mapper.RealmMapper; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpConfigService; +import io.jans.orm.PersistenceEntryManager; +import io.jans.orm.search.filter.Filter; +import io.jans.util.StringHelper; +import io.jans.util.StringHelper; +import io.jans.util.exception.InvalidAttributeException; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import java.util.*; + +import org.apache.commons.lang.StringUtils; +import org.keycloak.representations.idm.RealmRepresentation; +import org.slf4j.Logger; + +@ApplicationScoped +public class RealmService { + + @Inject + Logger logger; + + @Inject + PersistenceEntryManager persistenceEntryManager; + + @Inject + ConfigurationFactory configurationFactory; + + @Inject + IdpConfigService idpConfigService; + + @Inject + KeycloakService keycloakService; + + @Inject + OrganizationService organizationService; + + @Inject + RealmMapper realmMapper; + + public String getRealmDn() { + return idpConfigService.getRealmDn(); + } + + public Realm getRealmByDn(String dn) { + if (StringHelper.isNotEmpty(dn)) { + try { + return persistenceEntryManager.find(Realm.class, dn); + } catch (Exception e) { + logger.error(e.getMessage()); + } + + } + return null; + } + + public List getAllRealmDetails() { + return getAllRealms(0); + } + + public List getAllRealms(int sizeLimit) { + return persistenceEntryManager.findEntries(getDnForRealm(null), Realm.class, null, sizeLimit); + } + + public Realm getRealmByInum(String inum) { + Realm result = null; + try { + result = persistenceEntryManager.find(Realm.class, getDnForRealm(inum)); + } catch (Exception ex) { + logger.error("Failed to load Realm entry", ex); + } + return result; + } + + public List getRealmByName(String name) { + logger.info("Get RealmResource for name:{})", name); + if (StringUtils.isBlank(name)) { + throw new InvalidAttributeException("Realm name is null"); + } + + Filter nameFilter = Filter.createEqualityFilter("NAME", name); + logger.debug("Search Realm with displayNameFilter:{}", nameFilter); + return persistenceEntryManager.findEntries(getDnForRealm(null), Realm.class, nameFilter); + } + + public List searchRealm(String pattern, int sizeLimit) { + + logger.info("Search Realm with pattern:{}, sizeLimit:{}", pattern, sizeLimit); + + String[] targetArray = new String[] { pattern }; + Filter nameFilter = Filter.createSubstringFilter("NAME", null, targetArray, null); + Filter displayNameFilter = Filter.createSubstringFilter(AttributeConstants.DISPLAY_NAME, null, targetArray, + null); + Filter descriptionFilter = Filter.createSubstringFilter(AttributeConstants.DESCRIPTION, null, targetArray, + null); + Filter inumFilter = Filter.createSubstringFilter(AttributeConstants.INUM, null, targetArray, null); + Filter searchFilter = Filter.createORFilter(nameFilter, displayNameFilter, descriptionFilter, inumFilter); + + logger.debug("Search Realm with searchFilter:{}", searchFilter); + return persistenceEntryManager.findEntries(getDnForRealm(null), Realm.class, searchFilter, sizeLimit); + } + + public Realm createNewRealm(Realm realm) { + logger.info("Create new realm - realm:{})", realm); + if (realm == null) { + throw new InvalidAttributeException("Realm object is null"); + } + String inum = generateInumForRealm(); + realm.setInum(inum); + realm.setDn(getDnForRealm(inum)); + + // Create Realm in DB + persistenceEntryManager.persist(realm); + realm = getRealmByInum(realm.getInum()); + + // Add in KC + if (idpConfigService.isIdpEnabled()) { + RealmRepresentation realmRepresentation = convertToRealmRepresentation(realm); + realmRepresentation = keycloakService.createNewRealm(realmRepresentation); + realm = this.convertToRealm(realmRepresentation); + } + logger.info("Create new realm - realm:{})", realm); + return realm; + } + + public Realm updateRealm(Realm realm) { + logger.info("Update a realm - realm:{})", realm); + if (realm == null) { + throw new InvalidAttributeException("Realm object is null"); + } + + // Update Realm in DB + persistenceEntryManager.merge(realm); + + // Update in KC + if (idpConfigService.isIdpEnabled()) { + RealmRepresentation realmRepresentation = convertToRealmRepresentation(realm); + keycloakService.updateRealm(realmRepresentation); + realm = this.convertToRealm(realmRepresentation); + } + + logger.info("Update a realm - realm:{})", realm); + return realm; + } + + public void deleteRealm(Realm realm) { + logger.info("Delete realm:{})", realm); + + if (realm == null) { + throw new InvalidAttributeException("Realm object is null!!!"); + } + + // Delete from KC + keycloakService.deleteRealm(realm.getName()); + + // Delete from Jans DB + persistenceEntryManager.removeRecursively(realm.getDn(), Realm.class); + } + + public boolean containsRealm(String dn) { + return persistenceEntryManager.contains(dn, Realm.class); + } + + public String generateInumForRealm() { + String newInum = null; + String newDn = null; + do { + newInum = UUID.randomUUID().toString(); + newDn = getDnForRealm(newInum); + } while (this.containsRealm(newDn)); + + return newInum; + } + + public String getDnForRealm(String inum) { + String orgDn = organizationService.getDnForOrganization(); + if (StringHelper.isEmpty(inum)) { + return String.format("ou=realm,o=jans,%s", orgDn); + } + return String.format("inum=%s,ou=realm,o=jans,%s", inum, orgDn); + } + + private Realm convertToRealm(RealmRepresentation realmRepresentation) { + logger.debug("realmRepresentation:{}", realmRepresentation); + Realm realm = null; + if (realmRepresentation == null) { + return realm; + } + realm = realmMapper.kcRealmRepresentationToRealm(realmRepresentation); + logger.debug("converted - realm:{}", realm); + + return realm; + } + + private RealmRepresentation convertToRealmRepresentation(Realm realm) { + logger.debug("realm:{}", realm); + RealmRepresentation realmRepresentation = null; + if (realm == null) { + return realmRepresentation; + } + realmRepresentation = realmMapper.realmToKCRealmRepresentation(realm); + logger.debug("converted realmRepresentation:{}", realmRepresentation); + + logger.trace( + "convert Realm data realmRepresentation.getId():{}, realmRepresentation.getRealm():{}, realmRepresentation.getDisplayName():{},realmRepresentation.isEnabled():{}", + realmRepresentation.getId(), realmRepresentation.getRealm(), realmRepresentation.getDisplayName(), + realmRepresentation.isEnabled()); + + return realmRepresentation; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/SamlService.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/SamlService.java new file mode 100644 index 00000000000..c293278ea18 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/service/SamlService.java @@ -0,0 +1,144 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.service; + +import io.jans.service.document.store.service.DocumentStoreService; +import io.jans.service.document.store.conf.DocumentStoreType; +import io.jans.service.document.store.service.LocalDocumentStoreService; +import io.jans.util.exception.InvalidConfigurationException; +import io.jans.util.INumGenerator; +import io.jans.xml.GluuErrorHandler; +import io.jans.xml.XMLValidator; + +import javax.xml.parsers.ParserConfigurationException; +import org.xml.sax.SAXException; +import org.opensaml.saml.common.xml.SAMLSchemaBuilder; +import org.opensaml.saml.common.xml.SAMLSchemaBuilder.SAML1Version; +import org.opensaml.xml.parse.XMLParserException; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.inject.Inject; +import javax.xml.validation.Schema; + +import java.io.File; +import java.io.InputStream; +import java.io.IOException; +import java.util.*; + +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang.StringUtils; +import org.slf4j.Logger; + +@ApplicationScoped +public class SamlService { + + @Inject + Logger logger; + + @Inject + private DocumentStoreService documentStoreService; + + @Inject + private LocalDocumentStoreService localDocumentStoreService; + + private Schema samlSchema; + + @PostConstruct + public void create() { + SAMLSchemaBuilder samlSchemaBuilder = new SAMLSchemaBuilder(SAML1Version.SAML_11); + try { + //this.samlSchema = samlSchemaBuilder.getSAMLSchema(); + logger.info("samlSchema:{}", samlSchema); + } catch (Exception ex) { + logger.error("Failed to load SAMLSchema - ", ex); + } + } + + public boolean isLocalDocumentStoreType() { + return documentStoreService.getProviderType() == DocumentStoreType.LOCAL; + } + + private String getTempMetadataFilename(String metadataFolder, String fileName) { + logger.info("documentStoreService:{}, localDocumentStoreService:{}, metadataFolder:{}, fileName:{}", + documentStoreService, localDocumentStoreService, metadataFolder, fileName); + synchronized (SamlService.class) { + String possibleTemp; + do { + possibleTemp = fileName + INumGenerator.generate(2); + logger.debug("possibleTemp:{}", possibleTemp); + } while (documentStoreService.hasDocument(metadataFolder + possibleTemp)); + return possibleTemp; + } + } + + public String saveMetadataFile(String module, String metadataTempFolder, String metadataFileName, + InputStream stream) { + logger.info("module:{}, metadataTempFolder:{}, metadataFileName:{}, stream:{}", module, metadataTempFolder, + metadataFileName, stream); + + if (StringUtils.isBlank(metadataFileName)) { + throw new InvalidConfigurationException("Cannot save metadata file as metadataFileName is null!"); + } + + if (stream == null) { + throw new InvalidConfigurationException("Cannot save metadata file as file stream is null!"); + } + + String tempFileName = getTempMetadataFilename(metadataTempFolder, metadataFileName); + logger.debug("metadataTempFolder:{}, tempFileName:{}", metadataTempFolder, tempFileName); + + String spMetadataFile = metadataTempFolder + tempFileName; + logger.debug("documentStoreService:{}, spMetadataFile:{}, localDocumentStoreService:{} ", documentStoreService, + spMetadataFile, localDocumentStoreService); + try { + boolean result = documentStoreService.saveDocumentStream(spMetadataFile, stream, + List.of("jans-server", module)); + logger.debug("SP File saving result:{}", result); + + InputStream newFile = documentStoreService.readDocumentAsStream(spMetadataFile); + logger.debug("SP File read newFile:{}", newFile); + + if (result) { + return tempFileName; + } + } catch (Exception ex) { + logger.error("Failed to write SP metadata file '{}'", spMetadataFile, ex); + } finally { + IOUtils.closeQuietly(stream); + } + + return null; + } + + public GluuErrorHandler validateMetadata(String metadataPath) + throws ParserConfigurationException, SAXException, IOException, XMLParserException { + if (samlSchema == null) { + final List validationLog = new ArrayList(); + validationLog.add(GluuErrorHandler.SCHEMA_CREATING_ERROR_MESSAGE); + validationLog.add("Failed to load SAML schema"); + return new GluuErrorHandler(false, true, validationLog); + } + + try (InputStream stream = documentStoreService.readDocumentAsStream(metadataPath)) { + return XMLValidator.validateMetadata(stream, samlSchema); + } + } + + public boolean renameMetadata(String metadataPath, String destinationMetadataPath) { + logger.debug("Rename metadata file documentStoreService:{},metadataPath:{}, destinationMetadataPath:{}", + documentStoreService, metadataPath, destinationMetadataPath); + try { + return documentStoreService.renameDocument(metadataPath, destinationMetadataPath); + } catch (Exception ex) { + logger.error("Failed to rename metadata '{}' to '{}'", metadataPath, destinationMetadataPath, ex); + } + + return false; + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/timer/IdpMetadataValidationTimer.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/timer/IdpMetadataValidationTimer.java new file mode 100644 index 00000000000..53455314ff4 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/timer/IdpMetadataValidationTimer.java @@ -0,0 +1,366 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.timer; + +import io.jans.configapi.core.model.ValidationStatus; +import io.jans.configapi.plugin.keycloak.idp.broker.event.IdpMetadataValidationEvent; +import io.jans.configapi.plugin.keycloak.idp.broker.model.IdentityProvider; +import io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpAppConfiguration; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdpConfigService; +import io.jans.configapi.plugin.keycloak.idp.broker.service.IdentityProviderService; +import io.jans.configapi.plugin.keycloak.idp.broker.service.SamlService; + +import io.jans.model.GluuStatus; +import io.jans.saml.metadata.SAMLMetadataParser; +import io.jans.service.cdi.async.Asynchronous; +import io.jans.service.cdi.event.Scheduled; +import io.jans.service.timer.event.TimerEvent; +import io.jans.service.timer.schedule.TimerSchedule; + +import io.jans.util.StringHelper; +import io.jans.xml.GluuErrorHandler; + +import jakarta.annotation.PostConstruct; +import jakarta.enterprise.context.ApplicationScoped; +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; +import jakarta.inject.Inject; +import jakarta.inject.Named; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.TreeSet; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.atomic.AtomicBoolean; + +import org.slf4j.Logger; + +@ApplicationScoped +@Named +public class IdpMetadataValidationTimer { + + private final static int DEFAULT_INTERVAL = 60; // 60 seconds + + @Inject + private Logger log; + + @Inject + private Event timerEvent; + + @Inject + private SAMLMetadataParser samlMetadataParser; + + @Inject + SamlService samlService; + + @Inject + private IdpConfigService idpConfigService; + + @Inject + private IdentityProviderService identityProviderService; + + private AtomicBoolean isActive; + + private LinkedBlockingQueue metadataUpdates; + + + + @PostConstruct + public void init() { + this.isActive = new AtomicBoolean(true); + try { + this.metadataUpdates = new LinkedBlockingQueue(); + } finally { + this.isActive.set(false); + } + } + + public void initTimer() { + log.debug("Initializing Metadata Validation Timer"); + + final int delay = 30; + final int interval = DEFAULT_INTERVAL; + + timerEvent.fire(new TimerEvent(new TimerSchedule(delay, interval), new IdpMetadataValidationEvent(), + Scheduled.Literal.INSTANCE)); + } + + @Asynchronous + public void processMetadataValidationTimerEvent( + @Observes @Scheduled IdpMetadataValidationEvent IdpMetadataValidationEvent) { + if (this.isActive.get()) { + return; + } + + if (!this.isActive.compareAndSet(false, true)) { + return; + } + + try { + procesMetadataValidation(); + } catch (Throwable ex) { + log.error("Exception happened while reloading application configuration", ex); + } finally { + this.isActive.set(false); + } + } + + private void procesMetadataValidation() throws Exception { + log.debug("Starting metadata validation"); + boolean result = validateMetadata(getIdpAppConfiguration().getIdpMetadataTempDir(), getIdpAppConfiguration().getSpMetadataRootDir()); + log.debug("Metadata validation finished with result: '{}'", result); + + } + + public void queue(String fileName) { + synchronized (metadataUpdates) { + log.debug("fileNamem:{}, metadataUpdates.contains(fileName):{}", fileName, + metadataUpdates.contains(fileName)); + if (!metadataUpdates.contains(fileName)) { + metadataUpdates.add(fileName); + } + } + } + + public boolean isQueued(String samlspMetaDataFN) { + synchronized (metadataUpdates) { + for (String filename : metadataUpdates) { + if (filename.contains(samlspMetaDataFN)) { + return true; + } + } + return false; + } + } + + public String getValidationStatus(String spMetaDataFN, IdentityProvider trustedIdp) { + if (trustedIdp.getValidationStatus() == null) { + return ValidationStatus.SUCCESS.getDisplayName(); + } + if (trustedIdp.getValidationStatus() == null) { + return ValidationStatus.PENDING.getDisplayName(); + } + synchronized (metadataUpdates) { + boolean result = false; + for (String filename : metadataUpdates) { + if (filename.contains(spMetaDataFN)) { + result = true; + break; + } + } + if (result) { + return ValidationStatus.SCHEDULED.getDisplayName(); + } else { + return trustedIdp.getValidationStatus().getDisplayName(); + } + } + } + + /** + * @param tempmetadataFolder + * @param metadataFolder + */ + private boolean validateMetadata(String tempmetadataFolder, String metadataFolder) throws Exception { + boolean result = false; + log.debug("Starting metadata validation process."); + + String metadataFN = null; + synchronized (metadataUpdates) { + if (!metadataUpdates.isEmpty()) { + metadataFN = metadataUpdates.poll(); + + } + } + log.debug("metadataFN:{}", metadataFN); + + synchronized (this) { + if (metadataFN!=null && StringHelper.isNotEmpty(metadataFN)) { + String metadataPath = tempmetadataFolder + metadataFN; + String destinationMetadataName = metadataFN.replaceAll(".{4}\\..{4}$", ""); + String destinationMetadataPath = metadataFolder + destinationMetadataName; + log.debug("metadataFN:{}, metadataPath:{}, destinationMetadataName:{}, destinationMetadataPath:{}", + metadataFN, metadataPath, destinationMetadataName, destinationMetadataPath); + + IdentityProvider idp = identityProviderService.getIdentityProviderByUnpunctuatedInum(metadataFN.split("-" + identityProviderService.getIdpMetadataFilePattern())[0]); + + log.debug("IdentityProvider found with name:{} is:{}",metadataFN, idp); + if (idp == null) { + log.debug("No IdentityProvider found with name:{}",metadataFN); + metadataUpdates.add(metadataFN); + return false; + } + idp.setValidationStatus(ValidationStatus.PENDING); + + identityProviderService.updateIdentityProvider(idp); + + log.debug("metadataFN:{}, metadataPath:{}, destinationMetadataName:{}, destinationMetadataPath:{}", + metadataFN, metadataPath, destinationMetadataName, destinationMetadataPath); + + GluuErrorHandler errorHandler = null; + List validationLog = null; + try { + errorHandler = samlService.validateMetadata(metadataPath); + log.debug("validateMetadata result errorHandler:{}", errorHandler); + } catch (Exception e) { + idp.setValidationStatus(ValidationStatus.FAILED); + idp.setStatus(GluuStatus.INACTIVE); + validationLog = this.getValidationLog(validationLog); + validationLog.add(e.getMessage()); + log.debug("Validation of " + idp.getInum() + " failed: " + e.getMessage()); + idp.setValidationLog(validationLog); + identityProviderService.updateIdentityProvider(idp); + return false; + } + + if (errorHandler == null) { + return false; + } + + log.debug( + "validateMetadata result errorHandler.isValid():{}, errorHandler.getLog():{}, errorHandler.toString():{}", + errorHandler.isValid(), errorHandler.getLog(), errorHandler.toString()); + log.debug("samlAppConfiguration.isIgnoreValidation():{} errorHandler.isInternalError():{}", + getIdpAppConfiguration().isIgnoreValidation(), errorHandler.isInternalError()); + + + if (errorHandler.isValid()) { + log.debug("validate Metadata file processing"); + idp.setValidationLog(errorHandler.getLog()); + idp.setValidationStatus(ValidationStatus.SUCCESS); + + log.debug("Move metadata file:{} to location:{}", metadataPath, destinationMetadataPath); + boolean renamed = samlService.renameMetadata(metadataPath, destinationMetadataPath); + + log.debug("Staus of moving file:{} to location:{} is :{}", metadataPath, destinationMetadataPath, + renamed); + + if (renamed) { + log.debug("Failed to move metadata file:{} to location:{}", metadataPath, + destinationMetadataPath); + idp.setStatus(GluuStatus.INACTIVE); + } else { + idp.setSpMetaDataFN(destinationMetadataName); + } + + String metadataFile = getIdpAppConfiguration().getSpMetadataRootDir() + idp.getSpMetaDataFN(); + log.debug("After successfully moving metadataFile :{}", metadataFile); + List entityIdList = samlMetadataParser.getEntityIdFromMetadataFile(metadataFile); + log.debug("Success entityIdList :{}", entityIdList); + Set entityIdSet = new TreeSet(); + Set duplicatesSet = new TreeSet(); + if (entityIdList != null && !entityIdList.isEmpty()) { + + for (String entityId : entityIdList) { + if (!entityIdSet.add(entityId)) { + duplicatesSet.add(entityId); + } + } + } + log.debug("Success duplicatesSet :{}", duplicatesSet); + if (!duplicatesSet.isEmpty()) { + validationLog = idp.getValidationLog(); + if (validationLog != null) { + validationLog = new LinkedList(validationLog); + } else { + validationLog = new LinkedList(); + } + validationLog.add("This metadata contains multiple instances of entityId: " + + Arrays.toString(duplicatesSet.toArray())); + } + idp.setValidationLog(validationLog); + idp.setStatus(GluuStatus.ACTIVE); + + identityProviderService.updateIdentityProvider(idp); + result = true; + } else if (getIdpAppConfiguration().isIgnoreValidation() || errorHandler.isInternalError()) { + idp.setValidationLog(new ArrayList(new HashSet(errorHandler.getLog()))); + idp.setValidationStatus(ValidationStatus.FAILED); + boolean fileRenamed = samlService.renameMetadata(metadataPath, destinationMetadataPath); + log.debug("Status of IdentityProvider updated to Failed, File copy from:{} to:{}, status:()", + metadataPath, destinationMetadataPath, fileRenamed); + + if (!fileRenamed) { + log.debug( + "Updating IdentityProvider status to Inactive as metadata:{} could not be copied to:{}", + metadataPath, destinationMetadataPath); + idp.setStatus(GluuStatus.INACTIVE); + } else { + idp.setSpMetaDataFN(destinationMetadataName); + log.debug("Validation error for metadata file ignored as isIgnoreValidation:{}" + + getIdpAppConfiguration().isIgnoreValidation()); + } + + String metadataFile = getIdpAppConfiguration().getSpMetadataRootDir() + idp.getSpMetaDataFN(); + log.debug("metadataFile:{}", metadataFile); + + List entityIdList = samlMetadataParser.getEntityIdFromMetadataFile(metadataFile); + log.debug("entityIdList:{}", entityIdList); + Set duplicatesSet = new TreeSet(); + Set entityIdSet = new TreeSet(); + + if (entityIdList != null && !entityIdList.isEmpty()) { + for (String entityId : entityIdList) { + if (!entityIdSet.add(entityId)) { + duplicatesSet.add(entityId); + } + } + } + + idp.setStatus(GluuStatus.ACTIVE); + validationLog = idp.getValidationLog(); + log.debug("duplicatesSet:{}", duplicatesSet); + if (!duplicatesSet.isEmpty()) { + validationLog = this.getValidationLog(validationLog); + validationLog.add("This metadata contains multiple instances of entityId: " + + Arrays.toString(duplicatesSet.toArray())); + } + log.debug("errorHandler.isInternalError():{}", errorHandler.isInternalError()); + if (errorHandler.isInternalError()) { + validationLog = idp.getValidationLog(); + validationLog = this.getValidationLog(validationLog); + validationLog.add( + "Warning: cannot validate metadata. Check internet connetion ans www.w3.org availability."); + + log.debug("errorHandler.getLog():{}", errorHandler.getLog()); + // update log with warning + for (String warningLogMessage : errorHandler.getLog()) { + validationLog.add("Warning: " + warningLogMessage); + } + } + log.debug("Updating IdentityProvider:{} , validationLog :{}", idp, validationLog); + + identityProviderService.updateIdentityProvider(idp); + result = true; + } else { + log.debug("Unhandled metadataFN:{}", metadataFN); + idp.setValidationLog(new ArrayList(new HashSet(errorHandler.getLog()))); + idp.setValidationStatus(ValidationStatus.FAILED); + idp.setStatus(GluuStatus.INACTIVE); + identityProviderService.updateIdentityProvider(idp); + } + } + } + + return result; + } + + private List getValidationLog(List validationLog) { + log.debug("validationLog:{}", validationLog); + if (validationLog == null) { + validationLog = new LinkedList(); + } + return validationLog; + } + + private IdpAppConfiguration getIdpAppConfiguration() { + return this.idpConfigService.getIdpAppConfiguration(); + } +} diff --git a/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/util/Constants.java b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/util/Constants.java new file mode 100644 index 00000000000..7983a10e6cc --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/java/io/jans/configapi/plugin/keycloak/idp/broker/util/Constants.java @@ -0,0 +1,74 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.plugin.keycloak.idp.broker.util; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + + +public class Constants { + + private Constants() {} + + public static final String IDP_MODULE = "idp-module"; + public static final String JANS_IDP_CONFIG_PROP_PATH = "jans.idp.prop.path"; + public static final String KEYCLOAK_USER = "/keycloak-user"; + public static final String BASE_URL = "https://localhost"; + public static final String REALM_MASTER = "master"; + + + public static final String IDENTITY_PROVIDER = "/idp"; + public static final String KEYCLOAK= "/kc"; + public static final String SAML_PATH = "/saml"; + public static final String IDP_CONFIG_PATH = "/idp-config"; + public static final String REALM_PATH = "/realm"; + public static final String NAME_PATH = "/name"; + public static final String UPLOAD_PATH = "/upload"; + public static final String SP_METADATA_PATH = "/sp-metadata"; + public static final String SP_METADATA_FILE_PATH = "/sp-metadata-file"; + + public static final String ID_PATH_PARAM = "/{id}"; + public static final String INUM_PATH_PARAM = "/{inum}"; + public static final String NAME_PATH_PARAM = "/{name}"; + + public static final String IDP = "idp"; + public static final String SAML = "saml"; + public static final String OIDC = "oidc"; + public static final String ID = "id"; + public static final String NAME = "name"; + public static final String REALM = "realm"; + + + public static final List SAML_IDP_CONFIG = new ArrayList<>(Arrays.asList( "validateSignature", + "singleLogoutServiceUrl", + "postBindingLogout", + "postBindingResponse", + "postBindingAuthnRequest", + "singleSignOnServiceUrl", + "wantAuthnRequestsSigned", + "signingCertificate", + "addExtensionsElementWithKeyInfo")); + + //properties + public static final String IDP_SERVER_URL = "idp.server.url"; + public static final String AUTH_TOKEN_ENDPOINT = "auth.token.endpoint"; + public static final String IDP_CLIENT_ID = "idp.client.id"; + public static final String IDP_CLIENT_PASSWORD = "idp.client.password"; + + + //Scope + public static final String JANS_IDP_CONFIG_READ_ACCESS = "https://jans.io/idp/config.readonly"; + public static final String JANS_IDP_CONFIG_WRITE_ACCESS = "https://jans.io/idp/config.write"; + public static final String JANS_IDP_REALM_READ_ACCESS = "https://jans.io/idp/realm.readonly"; + public static final String JANS_IDP_REALM_WRITE_ACCESS = "https://jans.io/idp/realm.write"; + public static final String JANS_IDP_REALM_DELETE_ACCESS = "https://jans.io/idp/realm.delete"; + public static final String JANS_IDP_SAML_READ_ACCESS = "https://jans.io/idp/saml.readonly"; + public static final String JANS_IDP_SAML_WRITE_ACCESS = "https://jans.io/idp/saml.write"; + public static final String JANS_IDP_SAML_DELETE_ACCESS = "https://jans.io/idp/saml.delete"; + +} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/beans.xml b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/beans.xml new file mode 100644 index 00000000000..bf2ab180c1c --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/beans.xml @@ -0,0 +1,8 @@ + + + + + diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000000..5bd16fb48a7 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +io.jans.configapi.plugin.keycloak.idp.broker.extensions.IdpExtension \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers new file mode 100644 index 00000000000..b2c9664d366 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/javax.ws.rs.ext.Providers @@ -0,0 +1,3 @@ +io.jans.configapi.filters.AuthorizationFilter +io.jans.configapi.filters.LoggingFilter + diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource new file mode 100644 index 00000000000..a1d5a3c545b --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource @@ -0,0 +1 @@ +io.jans.configapi.plugin.keycloak.idp.broker.model.config.IdpConfigSource \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/idp.properties b/jans-config-api/plugins/idp-plugin/src/main/resources/idp.properties new file mode 100644 index 00000000000..8ff1363bf75 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/idp.properties @@ -0,0 +1,9 @@ +# Sample properties +keycloak.server.url=http://localhost:8180 +keycloak.realm.name= master +keycloak.client.id= https://samltest.id/saml/sp +keycloak.grant.type=PASSWORD +keycloak.admin.username=admin1 +keycloak.admin.password=admin123 +keycloak.client.id=jans-client-1 +keycloak.client.secret=yxsKi8ah9pU7ANyjH7HBwJh4XTLYN4x3 \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/main/resources/quartz.properties b/jans-config-api/plugins/idp-plugin/src/main/resources/quartz.properties new file mode 100644 index 00000000000..12cef917f4a --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/main/resources/quartz.properties @@ -0,0 +1,4 @@ +org.quartz.scheduler.instanceName=JansConfigScheduler +org.quartz.threadPool.threadCount=5 +org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore +org.quartz.scheduler.skipUpdateCheck=true diff --git a/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/KarateTestRunner.java b/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/KarateTestRunner.java new file mode 100644 index 00000000000..34da4586ef9 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/KarateTestRunner.java @@ -0,0 +1,18 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi; + +import com.intuit.karate.junit5.Karate; + +public class KarateTestRunner { + + @Karate.Test + Karate testFullPath() throws Exception { + return Karate.run("src/test/resources/feature"); + } + +} diff --git a/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/TestJenkinsRunner.java b/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/TestJenkinsRunner.java new file mode 100644 index 00000000000..a7f7d2d80c2 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/java/io/jans/configapi/TestJenkinsRunner.java @@ -0,0 +1,44 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi; + +import com.intuit.karate.Results; +import com.intuit.karate.Runner; + +import io.jans.as.common.model.registration.Client; +import net.masterthought.cucumber.Configuration; +import net.masterthought.cucumber.ReportBuilder; +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class TestJenkinsRunner { + + @Test + void testParallel() { + System.setProperty("karate.env", "jenkins"); + Results results = Runner.path("src/test/resources/feature").tags("~@ignore").parallel(1); + generateReport(results.getReportDir()); + Assertions.assertEquals(0, results.getFailCount(), results.getErrorMessages()); + } + + public static void generateReport(String karateOutputPath) { + Collection jsonFiles = FileUtils.listFiles(new File(karateOutputPath), new String[] { "json" }, true); + List jsonPaths = new ArrayList(jsonFiles.size()); + jsonFiles.forEach(file -> jsonPaths.add(file.getAbsolutePath())); + Configuration config = new Configuration(new File("target"), "karateTesting"); + ReportBuilder reportBuilder = new ReportBuilder(jsonPaths, config); + reportBuilder.generateReports(); + } +} diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/config/saml.feature b/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/config/saml.feature new file mode 100644 index 00000000000..9e6a9a52da2 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/config/saml.feature @@ -0,0 +1,21 @@ + +Feature: Verify SAML configuration endpoint + + Background:samlUrl + * def mainUrl = samlConfigUrl + + @get-saml-config-no-token + Scenario: Fetch SAML config without bearer token + Given url mainUrl + When method GET + Then status 401 + + @get-saml-config + Scenario: Retrieve SAML configuration + Given url mainUrl + And header Authorization = 'Bearer ' + accessToken + When method GET + Then status 200 + And print response + And assert response.length != null + diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/trust-relationship/saml-trust-relationship.feature b/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/trust-relationship/saml-trust-relationship.feature new file mode 100644 index 00000000000..515ced47946 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/feature/saml/trust-relationship/saml-trust-relationship.feature @@ -0,0 +1,21 @@ + +Feature: Verify SAML Trust Relationship endpoint + + Background:samlUrl + * def mainUrl = samlTrustRelationshipUrl + + @get-trust-relationship-no-token + Scenario: Fetch SAML Trust Relationships without bearer token + Given url mainUrl + When method GET + Then status 401 + + @get-trust-relationship + Scenario: Retrieve SAML Trust Relationship + Given url mainUrl + And header Authorization = 'Bearer ' + accessToken + When method GET + Then status 200 + And print response + And assert response.length != null + diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config-jenkins.js b/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config-jenkins.js new file mode 100644 index 00000000000..d32b307b93f --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config-jenkins.js @@ -0,0 +1,59 @@ +function() { + + var stream = read('classpath:karate_jenkins.properties'); + var props = new java.util.Properties(); + props.load(stream); + + var env = props.get('karate.env'); // get java system property 'karate.env' + karate.configure("ssl", true); + + if (!env) { + env = 'dev'; //env can be anything: dev, qa, staging, etc. + } + + var url = props.get('karate.test.url'); + var port = props.get('karate.test.port'); + var baseUrl = url + (port ? ':' + port : ''); + + karate.log('karate_jenkins env :', env); + karate.log('karate_jenkins url :', url); + karate.log('karate_jenkins port :', port); + karate.log('karate_jenkins baseUrl :', baseUrl); + + var testStream = read('classpath:test.properties'); + var testProps = new java.util.Properties(); + testProps.load(testStream); + karate.log(' testProps = '+testProps); + var testClientId = testProps.get('test.client.id'); + var testClientSecret = testProps.get('test.client.secret'); + var tokenEndpoint = testProps.get('token.endpoint'); + var testScopes = testProps.get('test.scopes'); + var issuer = testProps.get('test.issuer'); + karate.log(' testClientId = '+testClientId); + karate.log(' testClientSecret = '+testClientSecret); + karate.log(' tokenEndpoint = '+tokenEndpoint); + karate.log(' testScopes = '+testScopes); + karate.log(' issuer = '+issuer); + + + var config = { + env: env, + baseUrl: baseUrl, + testProps: testProps, + issuer: issuer, + accessToken: '123', + + samlTrustRelationshipUrl: baseUrl + '/jans-config-api/saml/trust-relationship', + samlConfigUrl: baseUrl + '/jans-config-api/saml/samlConfig', + + }; + + karate.configure('connectTimeout', 30000); + karate.configure('readTimeout', 60000); + + var result = karate.callSingle('classpath:token.feature', config); + print(' result.response = '+result.response); + config.accessToken = result.response.access_token; + + return config; +} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config.js b/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config.js new file mode 100644 index 00000000000..a8086c01326 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/karate-config.js @@ -0,0 +1,58 @@ +function() { + + var stream = read('classpath:karate.properties'); + var props = new java.util.Properties(); + props.load(stream); + + var env = props.get('karate.env'); // get java system property 'karate.env' + karate.configure("ssl", true); + + if (!env) { + env = 'dev'; //env can be anything: dev, qa, staging, etc. + } + + var url = props.get('karate.test.url'); + var port = props.get('karate.test.port'); + var baseUrl = url + (port ? ':' + port : ''); + + karate.log('karate env :', env); + karate.log('karate url :', url); + karate.log('karate port :', port); + karate.log('karate baseUrl :', baseUrl); + + var testStream = read('classpath:test.properties'); + var testProps = new java.util.Properties(); + testProps.load(testStream); + karate.log(' testProps = '+testProps); + var testClientId = testProps.get('test.client.id'); + var testClientSecret = testProps.get('test.client.secret'); + var tokenEndpoint = testProps.get('token.endpoint'); + var testScopes = testProps.get('test.scopes'); + var issuer = testProps.get('test.issuer'); + karate.log(' testClientId = '+testClientId); + karate.log(' testClientSecret = '+testClientSecret); + karate.log(' tokenEndpoint = '+tokenEndpoint); + karate.log(' testScopes = '+testScopes); + karate.log(' issuer = '+issuer); + + + var config = { + env: env, + baseUrl: baseUrl, + testProps: testProps, + issuer: issuer, + accessToken: '123', + + samlTrustRelationshipUrl: baseUrl + '/jans-config-api/saml/trust-relationship', + samlConfigUrl: baseUrl + '/jans-config-api/saml/samlConfig', + }; + + karate.configure('connectTimeout', 30000); + karate.configure('readTimeout', 60000); + + var result = karate.callSingle('classpath:token.feature', config); + print(' result.response = '+result.response); + config.accessToken = result.response.access_token; + + return config; +} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/karate.properties b/jans-config-api/plugins/idp-plugin/src/test/resources/karate.properties new file mode 100644 index 00000000000..41c0d369aff --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/karate.properties @@ -0,0 +1,5 @@ +#karate.test.url=http://localhost +#karate.test.port=8080 +#karate.test.url=https://jenkins-config-api.gluu.org/jans-config-api +#karate.test.port=443 +karate.test.url=${test.server} diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/karate_jenkins.properties b/jans-config-api/plugins/idp-plugin/src/test/resources/karate_jenkins.properties new file mode 100644 index 00000000000..0b44a8d7b13 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/karate_jenkins.properties @@ -0,0 +1,2 @@ +karate.test.url=${test.server} +#karate.test.port=443 diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/logback-test.xml b/jans-config-api/plugins/idp-plugin/src/test/resources/logback-test.xml new file mode 100644 index 00000000000..fea195eb039 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/logback-test.xml @@ -0,0 +1,24 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + target/karate.log + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + + + + \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/test.properties b/jans-config-api/plugins/idp-plugin/src/test/resources/test.properties new file mode 100644 index 00000000000..4257f297907 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/test.properties @@ -0,0 +1,8 @@ +test.scopes=${test.scopes} + +# Test env Setting +token.endpoint=${token.endpoint} +token.grant.type=${token.grant.type} +test.client.id=${test.client.id} +test.client.secret=${test.client.secret} +test.issuer=${test.issuer} \ No newline at end of file diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/testClient.feature b/jans-config-api/plugins/idp-plugin/src/test/resources/testClient.feature new file mode 100644 index 00000000000..34cfdffc438 --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/testClient.feature @@ -0,0 +1,13 @@ +@ignore +Feature: This Feature is to get token to test the test cases + +Background: +* def mainUrl = test_url + +Scenario: Get Token +Given url mainUrl +And print url +And request '' +When method POST +Then status 204 +And print response diff --git a/jans-config-api/plugins/idp-plugin/src/test/resources/token.feature b/jans-config-api/plugins/idp-plugin/src/test/resources/token.feature new file mode 100644 index 00000000000..ef0ad0d262d --- /dev/null +++ b/jans-config-api/plugins/idp-plugin/src/test/resources/token.feature @@ -0,0 +1,45 @@ +@ignore +Feature: This Feature is to get token to test the test cases - Do not remove ignore tag + +Background: +* def mainUrl = testProps.get('token.endpoint'); +* def grantType = testProps.get('token.grant.type'); +* def clientId = testProps.get('test.client.id'); +* def clientSecret = testProps.get('test.client.secret'); +* def scopes = testProps.get('test.scopes'); +* def authStr = clientId+':'+clientSecret +* def Base64 = Java.type('java.util.Base64') +* def encodedAuth = Base64.encoder.encodeToString(authStr.bytes) +* def encodedScopes = java.net.URLDecoder.decode(scopes, 'UTF-8') + + +Scenario: Get Token +Given url mainUrl +And print 'mainUrl = '+mainUrl +And print 'grantType = '+grantType +And print 'clientId = '+clientId +And print 'clientSecret = '+clientSecret +And print 'scopes = '+scopes +And print 'authStr = '+authStr +And print 'encodedAuth = '+encodedAuth +And print 'encodedScopes = '+encodedScopes +And header Accept = 'application/json' +And header Authorization = 'Basic '+encodedAuth +And form field grant_type = grantType +And form field scope = scopes +When method POST +Then status 200 +And print 'token response = '+response + + + + +#Scenario: Get Token +#Given url 'https://pujavs.jans.server/jans-auth/restv1/token' +#And header Accept = 'application/json' +#And header Authorization = 'Basic MTgwMi45ZGNkOThhZC1mZTJjLTRmZDktYjcxNy1kOTQzNmQ5ZjIwMDk6dGVzdDEyMzQ=' +#And form field grant_type = 'client_credentials' +#And form field scope = 'https://jans.io/oauth/config/openid/clients.readonly' +#When method POST +#Then status 200 +#And print 'token response = '+response diff --git a/jans-config-api/plugins/pom.xml b/jans-config-api/plugins/pom.xml index b7c8c197833..8edc47a2e74 100644 --- a/jans-config-api/plugins/pom.xml +++ b/jans-config-api/plugins/pom.xml @@ -22,6 +22,7 @@ user-mgt-plugin fido2-plugin saml-plugin + idp-plugin diff --git a/jans-config-api/plugins/saml-plugin/src/main/java/io/jans/configapi/plugin/saml/form/TrustRelationshipForm.java b/jans-config-api/plugins/saml-plugin/src/main/java/io/jans/configapi/plugin/saml/form/TrustRelationshipForm.java index 4e14bf6e0fd..ea7b5a38b59 100644 --- a/jans-config-api/plugins/saml-plugin/src/main/java/io/jans/configapi/plugin/saml/form/TrustRelationshipForm.java +++ b/jans-config-api/plugins/saml-plugin/src/main/java/io/jans/configapi/plugin/saml/form/TrustRelationshipForm.java @@ -1,5 +1,5 @@ /* - * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. +] * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. * * Copyright (c) 2020, Janssen Project */ diff --git a/jans-config-api/pom.xml b/jans-config-api/pom.xml index 92d605518c4..f70c336e861 100644 --- a/jans-config-api/pom.xml +++ b/jans-config-api/pom.xml @@ -26,7 +26,7 @@ 3.0.0-M5 ${project.version} - + 6.0.3.Final 2.13.2 11.0.15 @@ -47,6 +47,7 @@ 5.7.0 3.8.0 0.8.10 + 1.5.3.Final 2.2.7 JanssenProject_jans-config-api @@ -406,7 +407,15 @@ org.apache.james apache-mime4j-core ${apache.james.version} - + + + + + org.mapstruct + mapstruct + ${mapstruct.version} + + @@ -432,6 +441,13 @@ ${compiler-plugin.version} true + + + org.mapstruct + mapstruct-processor + ${mapstruct.version} + + diff --git a/jans-config-api/profiles/default/config-api-test.properties b/jans-config-api/profiles/default/config-api-test.properties index 34395f63bd3..3987bd54fde 100644 --- a/jans-config-api/profiles/default/config-api-test.properties +++ b/jans-config-api/profiles/default/config-api-test.properties @@ -1,7 +1,7 @@ # The URL of your Jans installation test.server=https://jenkins-config-api.gluu.org -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/cacherefresh.readonly https://jans.io/oauth/config/cacherefresh.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write https://jans.io/oauth/config/saml-client-scope.readonly https://jans.io/oauth/config/saml-client-scope.write +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/cacherefresh.readonly https://jans.io/oauth/config/cacherefresh.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write https://jans.io/oauth/config/saml-client-scope.readonly https://jans.io/oauth/config/saml-client-scope.write https://jans.io/idp/config.readonly https://jans.io/idp/config.write https://jans.io/idp/realm.readonly https://jans.io/idp/realm.write https://jans.io/idp/realm.write https://jans.io/idp/saml.readonly https://jans.io/idp/saml.write token.endpoint=https://jenkins-config-api.gluu.org/jans-auth/restv1/token token.grant.type=client_credentials diff --git a/jans-config-api/profiles/jans-ui.jans.io/test.properties b/jans-config-api/profiles/jans-ui.jans.io/test.properties index 630de8e6b37..e88ae60192d 100644 --- a/jans-config-api/profiles/jans-ui.jans.io/test.properties +++ b/jans-config-api/profiles/jans-ui.jans.io/test.properties @@ -1,4 +1,4 @@ -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/cacherefresh.readonly https://jans.io/oauth/config/cacherefresh.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write https://jans.io/oauth/config/saml-client-scope.readonly https://jans.io/oauth/config/saml-client-scope.write https://jans.io/idp/config.readonly https://jans.io/idp/config.write https://jans.io/idp/realm.readonly https://jans.io/idp/realm.write https://jans.io/idp/realm.write https://jans.io/idp/saml.readonly https://jans.io/idp/saml.write # Test env Setting token.endpoint=https://jans-ui.jans.io/jans-auth/restv1/token diff --git a/jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties b/jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties index cfae01df0b4..624c772753a 100644 --- a/jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties +++ b/jans-config-api/profiles/jenkins-config-api.gluu.org/test.properties @@ -1,6 +1,6 @@ test.server=https://jenkins-config-api.gluu.org -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/cacherefresh.readonly https://jans.io/oauth/config/cacherefresh.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write https://jans.io/oauth/config/saml-client-scope.readonly https://jans.io/oauth/config/saml-client-scope.write https://jans.io/idp/config.readonly https://jans.io/idp/config.write https://jans.io/idp/realm.readonly https://jans.io/idp/realm.write https://jans.io/idp/realm.write https://jans.io/idp/saml.readonly https://jans.io/idp/saml.write token.endpoint=https://jenkins-config-api.gluu.org/jans-auth/restv1/token token.grant.type=client_credentials diff --git a/jans-config-api/profiles/local/test.properties b/jans-config-api/profiles/local/test.properties index 120a0f773b0..c7f7732dc2a 100644 --- a/jans-config-api/profiles/local/test.properties +++ b/jans-config-api/profiles/local/test.properties @@ -1,5 +1,5 @@ #LOCAL -test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/jans-link.readonly https://jans.io/oauth/config/jans-link.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write +test.scopes=https://jans.io/oauth/config/acrs.readonly https://jans.io/oauth/config/acrs.write https://jans.io/oauth/config/attributes.readonly https://jans.io/oauth/config/attributes.write https://jans.io/oauth/config/attributes.delete https://jans.io/oauth/config/cache.readonly https://jans.io/oauth/config/cache.write https://jans.io/oauth/config/openid/clients.readonly https://jans.io/oauth/config/openid/clients.write https://jans.io/oauth/config/openid/clients.delete https://jans.io/oauth/jans-auth-server/config/properties.readonly https://jans.io/oauth/jans-auth-server/config/properties.write https://jans.io/oauth/config/smtp.readonly https://jans.io/oauth/config/smtp.write https://jans.io/oauth/config/smtp.delete https://jans.io/oauth/config/scripts.readonly https://jans.io/oauth/config/scripts.write https://jans.io/oauth/config/scripts.delete https://jans.io/oauth/config/fido2.readonly https://jans.io/oauth/config/fido2.write https://jans.io/oauth/config/jwks.readonly https://jans.io/oauth/config/jwks.write https://jans.io/oauth/config/jwks.delete https://jans.io/oauth/config/database/ldap.readonly https://jans.io/oauth/config/database/ldap.write https://jans.io/oauth/config/database/ldap.delete https://jans.io/oauth/config/logging.readonly https://jans.io/oauth/config/logging.write https://jans.io/oauth/config/scopes.readonly https://jans.io/oauth/config/scopes.write https://jans.io/oauth/config/scopes.delete https://jans.io/oauth/config/uma/resources.readonly https://jans.io/oauth/config/uma/resources.write https://jans.io/oauth/config/uma/resources.delete https://jans.io/oauth/config/database/sql.readonly https://jans.io/oauth/config/database/sql.write https://jans.io/oauth/config/database/sql.delete https://jans.io/oauth/config/stats.readonly jans_stat https://jans.io/scim/users.read https://jans.io/scim/users.write https://jans.io/oauth/config/scim/users.read https://jans.io/oauth/config/scim/users.write https://jans.io/scim/config.readonly https://jans.io/scim/config.write https://jans.io/oauth/config/organization.readonly https://jans.io/oauth/config/organization.write https://jans.io/oauth/config/user.readonly https://jans.io/oauth/config/user.write https://jans.io/oauth/config/user.delete https://jans.io/oauth/config/agama.readonly https://jans.io/oauth/config/agama.write https://jans.io/oauth/config/agama.delete https://jans.io/oauth/jans-auth-server/session.readonly https://jans.io/oauth/jans-auth-server/session.delete revoke_session https://jans.io/oauth/config/read-all https://jans.io/oauth/config/write-all https://jans.io/oauth/config/delete-all https://jans.io/oauth/config/openid-read https://jans.io/oauth/config/openid-write https://jans.io/oauth/config/openid-delete https://jans.io/oauth/config/uma-read https://jans.io/oauth/config/uma-write https://jans.io/oauth/config/uma-delete https://jans.io/oauth/jans-auth-server/config/adminui/user/role.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/role.write https://jans.io/oauth/jans-auth-server/config/adminui/read-all https://jans.io/oauth/jans-auth-server/config/adminui/write-all https://jans.io/oauth/jans-auth-server/config/adminui/user/role.delete https://jans.io/oauth/jans-auth-server/config/adminui/delete-all https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.write https://jans.io/oauth/jans-auth-server/config/adminui/user/permission.delete https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.readonly https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.write https://jans.io/oauth/jans-auth-server/config/adminui/user/rolePermissionMapping.delete https://jans.io/oauth/jans-auth-server/config/adminui/license.readonly https://jans.io/oauth/jans-auth-server/config/adminui/license.write https://jans.io/oauth/config/plugin.readonly https://jans.io/oauth/client/authorizations.readonly https://jans.io/oauth/client/authorizations.delete https://jans.io/oauth/config/cacherefresh.readonly https://jans.io/oauth/config/cacherefresh.write https://jans.io/oauth/config/saml.readonly https://jans.io/oauth/config/saml.write https://jans.io/oauth/config/saml-config.readonly https://jans.io/oauth/config/saml-config.write https://jans.io/oauth/config/saml-client-scope.readonly https://jans.io/oauth/config/saml-client-scope.write https://jans.io/idp/config.readonly https://jans.io/idp/config.write https://jans.io/idp/realm.readonly https://jans.io/idp/realm.write https://jans.io/idp/realm.write https://jans.io/idp/saml.readonly https://jans.io/idp/saml.write # jans.server token.endpoint=https://jans.server3/jans-auth/restv1/token diff --git a/jans-config-api/server/src/main/resources/config-api-rs-protect.json b/jans-config-api/server/src/main/resources/config-api-rs-protect.json index 976a3e57767..be1c2da0224 100644 --- a/jans-config-api/server/src/main/resources/config-api-rs-protect.json +++ b/jans-config-api/server/src/main/resources/config-api-rs-protect.json @@ -2412,6 +2412,144 @@ ] } ] + }, + { + "path": "/jans-config-api/idp/config", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.62", + "name": "https://jans.io/idp/config.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.63", + "name": "https://jans.io/idp/config.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "POST","PUT","PATCH" + ], + "scopes": [ + { + "inum": "1800.01.63", + "name": "https://jans.io/idp/config.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] + }, + { + "path": "/jans-config-api/idp/kc/realm", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.64", + "name": "https://jans.io/idp/realm.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.65", + "name": "https://jans.io/idp/realm.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "POST","PUT","PATCH","DELETE" + ], + "scopes": [ + { + "inum": "1800.01.65", + "name": "https://jans.io/idp/realm.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] + }, + { + "path": "/jans-config-api/idp/kc/saml", + "conditions": [ + { + "httpMethods": [ + "GET" + ], + "scopes": [ + { + "inum": "1800.01.66", + "name": "https://jans.io/idp/saml.readonly" + } + ], + "groupScopes": [ + { + "inum": "1800.01.67", + "name": "https://jans.io/idp/saml.write" + } + ], + "superScopes": [ + { + "inum": "1800.03.1", + "name": "https://jans.io/oauth/config/read-all" + } + ] + }, + { + "httpMethods": [ + "POST","PUT","PATCH","DELETE" + ], + "scopes": [ + { + "inum": "1800.01.67", + "name": "https://jans.io/idp/saml.write" + } + ], + "groupScopes": [], + "superScopes": [ + { + "inum": "1800.03.2", + "name": "https://jans.io/oauth/config/write-all" + } + ] + } + ] } ] } \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/config/idp-config.json b/jans-config-api/server/src/main/resources/example/idp/config/idp-config.json new file mode 100644 index 00000000000..2ac95859ee7 --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/config/idp-config.json @@ -0,0 +1,23 @@ +{ + "applicationName": "idp", + "trustedIdpDn": "ou=trusted-idp,o=jans", + "enabled": true, + "serverUrl": "http://localhost:8180", + "realm": "master", + "clientId": "my-client-1", + "clientSecret": "aqOMI7DhNxCFbW0IieBHSrdA6HMTwxiQ", + "grantType": "PASSWORD", + "username": "admin1", + "password": "admin123", + "idpRootDir": "/opt/jans/idp/", + "idpMetadataRootDir": "/opt/idp/configs/keycloak/idp/metadata", + "idpMetadataTempDir": "/opt/idp/configs/keycloak/idp/temp_metadata", + "idpMetadataFilePattern": "%s-idp-metadata.xml", + "idpMetadataFile": "idp-metadata.xml", + "spMetadataUrl": "/realms/%s/broker/%s/endpoint/descriptor", + "spMetadataRootDir": "/opt/idp/configs/keycloak/sp/metadata", + "spMetadataTempDir": "/opt/idp/configs/keycloak/sp/temp_metadata", + "spMetadataFilePattern": "%s-sp-metadata.xml", + "spMetadataFile": "sp-metadata.xml", + "ignoreValidation": false +} \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/realm/get-all-relam.json b/jans-config-api/server/src/main/resources/example/idp/realm/get-all-relam.json new file mode 100644 index 00000000000..8b570acd0bb --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/realm/get-all-relam.json @@ -0,0 +1,705 @@ +[ + { + "id": "37790484-9cb2-4595-83a1-05981c59a0b0", + "realm": "master", + "displayName": "Keycloak", + "displayNameHtml": "
Keycloak
", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 60, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "passwordCredentialGrantAllowed": null, + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "privateKey": null, + "publicKey": null, + "certificate": null, + "codeSecret": null, + "roles": null, + "groups": null, + "defaultRoles": null, + "defaultRole": { + "id": "45f2edda-a31f-4717-810d-830effbbad1c", + "name": "default-roles-master", + "description": "${role_default-roles}", + "scopeParamRequired": null, + "composite": true, + "composites": null, + "clientRole": false, + "containerId": "37790484-9cb2-4595-83a1-05981c59a0b0", + "attributes": null + }, + "defaultGroups": null, + "requiredCredentials": [ + "password" + ], + "passwordPolicy": null, + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppMicrosoftAuthenticatorName", + "totpAppGoogleName", + "totpAppFreeOTPName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": null, + "federatedUsers": null, + "scopeMappings": null, + "clientScopeMappings": null, + "clients": null, + "clientScopes": null, + "defaultDefaultClientScopes": null, + "defaultOptionalClientScopes": null, + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "xXSSProtection": "1; mode=block", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "userFederationProviders": null, + "userFederationMappers": null, + "loginTheme": null, + "accountTheme": null, + "adminTheme": null, + "emailTheme": null, + "eventsEnabled": false, + "eventsExpiration": null, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "protocolMappers": null, + "components": null, + "internationalizationEnabled": false, + "supportedLocales": [], + "defaultLocale": null, + "authenticationFlows": null, + "authenticatorConfig": null, + "requiredActions": null, + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": null, + "userManagedAccessAllowed": false, + "social": null, + "updateProfileOnInitialSocialLogin": null, + "socialProviders": null, + "applicationScopeMappings": null, + "applications": null, + "oauthClients": null, + "clientTemplates": null, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } + }, + { + "id": "5d369be6-c911-467e-b0d4-32ce061e2c25", + "realm": "realm1", + "displayName": null, + "displayNameHtml": null, + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "passwordCredentialGrantAllowed": null, + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "privateKey": null, + "publicKey": null, + "certificate": null, + "codeSecret": null, + "roles": null, + "groups": null, + "defaultRoles": null, + "defaultRole": { + "id": "61d57799-c264-4859-a843-4df0ffee50e5", + "name": "default-roles-realm1", + "description": "${role_default-roles}", + "scopeParamRequired": null, + "composite": true, + "composites": null, + "clientRole": false, + "containerId": "5d369be6-c911-467e-b0d4-32ce061e2c25", + "attributes": null + }, + "defaultGroups": null, + "requiredCredentials": [ + "password" + ], + "passwordPolicy": null, + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppMicrosoftAuthenticatorName", + "totpAppGoogleName", + "totpAppFreeOTPName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": null, + "federatedUsers": null, + "scopeMappings": null, + "clientScopeMappings": null, + "clients": null, + "clientScopes": null, + "defaultDefaultClientScopes": null, + "defaultOptionalClientScopes": null, + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "userFederationProviders": null, + "userFederationMappers": null, + "loginTheme": null, + "accountTheme": null, + "adminTheme": null, + "emailTheme": null, + "eventsEnabled": false, + "eventsExpiration": null, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [], + "identityProviderMappers": [], + "protocolMappers": null, + "components": null, + "internationalizationEnabled": false, + "supportedLocales": [], + "defaultLocale": null, + "authenticationFlows": null, + "authenticatorConfig": null, + "requiredActions": null, + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaExpiresIn": "120", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DeviceCodeLifespan": "600", + "oauth2DevicePollingInterval": "5", + "parRequestUriLifespan": "60", + "cibaInterval": "5", + "realmReusableOtpCode": "false" + }, + "keycloakVersion": null, + "userManagedAccessAllowed": false, + "social": null, + "updateProfileOnInitialSocialLogin": null, + "socialProviders": null, + "applicationScopeMappings": null, + "applications": null, + "oauthClients": null, + "clientTemplates": null, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } + }, + { + "id": "8550ab9e-05c4-46af-bd6a-d24b381c618e", + "realm": "keycloak-internal-identity", + "displayName": "", + "displayNameHtml": "", + "notBefore": 0, + "defaultSignatureAlgorithm": "RS256", + "revokeRefreshToken": false, + "refreshTokenMaxReuse": 0, + "accessTokenLifespan": 300, + "accessTokenLifespanForImplicitFlow": 900, + "ssoSessionIdleTimeout": 1800, + "ssoSessionMaxLifespan": 36000, + "ssoSessionIdleTimeoutRememberMe": 0, + "ssoSessionMaxLifespanRememberMe": 0, + "offlineSessionIdleTimeout": 2592000, + "offlineSessionMaxLifespanEnabled": false, + "offlineSessionMaxLifespan": 5184000, + "clientSessionIdleTimeout": 0, + "clientSessionMaxLifespan": 0, + "clientOfflineSessionIdleTimeout": 0, + "clientOfflineSessionMaxLifespan": 0, + "accessCodeLifespan": 60, + "accessCodeLifespanUserAction": 300, + "accessCodeLifespanLogin": 1800, + "actionTokenGeneratedByAdminLifespan": 43200, + "actionTokenGeneratedByUserLifespan": 300, + "oauth2DeviceCodeLifespan": 600, + "oauth2DevicePollingInterval": 5, + "enabled": true, + "sslRequired": "external", + "passwordCredentialGrantAllowed": null, + "registrationAllowed": false, + "registrationEmailAsUsername": false, + "rememberMe": false, + "verifyEmail": false, + "loginWithEmailAllowed": true, + "duplicateEmailsAllowed": false, + "resetPasswordAllowed": false, + "editUsernameAllowed": false, + "bruteForceProtected": false, + "permanentLockout": false, + "maxFailureWaitSeconds": 900, + "minimumQuickLoginWaitSeconds": 60, + "waitIncrementSeconds": 60, + "quickLoginCheckMilliSeconds": 1000, + "maxDeltaTimeSeconds": 43200, + "failureFactor": 30, + "privateKey": null, + "publicKey": null, + "certificate": null, + "codeSecret": null, + "roles": null, + "groups": null, + "defaultRoles": null, + "defaultRole": { + "id": "e83e65d0-f59d-4192-98d2-cceb8579c141", + "name": "default-roles-keycloak-external-broker", + "description": "${role_default-roles}", + "scopeParamRequired": null, + "composite": true, + "composites": null, + "clientRole": false, + "containerId": "8550ab9e-05c4-46af-bd6a-d24b381c618e", + "attributes": null + }, + "defaultGroups": null, + "requiredCredentials": [ + "password" + ], + "passwordPolicy": null, + "otpPolicyType": "totp", + "otpPolicyAlgorithm": "HmacSHA1", + "otpPolicyInitialCounter": 0, + "otpPolicyDigits": 6, + "otpPolicyLookAheadWindow": 1, + "otpPolicyPeriod": 30, + "otpPolicyCodeReusable": false, + "otpSupportedApplications": [ + "totpAppMicrosoftAuthenticatorName", + "totpAppGoogleName", + "totpAppFreeOTPName" + ], + "webAuthnPolicyRpEntityName": "keycloak", + "webAuthnPolicySignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyRpId": "", + "webAuthnPolicyAttestationConveyancePreference": "not specified", + "webAuthnPolicyAuthenticatorAttachment": "not specified", + "webAuthnPolicyRequireResidentKey": "not specified", + "webAuthnPolicyUserVerificationRequirement": "not specified", + "webAuthnPolicyCreateTimeout": 0, + "webAuthnPolicyAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyAcceptableAaguids": [], + "webAuthnPolicyPasswordlessRpEntityName": "keycloak", + "webAuthnPolicyPasswordlessSignatureAlgorithms": [ + "ES256" + ], + "webAuthnPolicyPasswordlessRpId": "", + "webAuthnPolicyPasswordlessAttestationConveyancePreference": "not specified", + "webAuthnPolicyPasswordlessAuthenticatorAttachment": "not specified", + "webAuthnPolicyPasswordlessRequireResidentKey": "not specified", + "webAuthnPolicyPasswordlessUserVerificationRequirement": "not specified", + "webAuthnPolicyPasswordlessCreateTimeout": 0, + "webAuthnPolicyPasswordlessAvoidSameAuthenticatorRegister": false, + "webAuthnPolicyPasswordlessAcceptableAaguids": [], + "users": null, + "federatedUsers": null, + "scopeMappings": null, + "clientScopeMappings": null, + "clients": null, + "clientScopes": null, + "defaultDefaultClientScopes": null, + "defaultOptionalClientScopes": null, + "browserSecurityHeaders": { + "contentSecurityPolicyReportOnly": "", + "xContentTypeOptions": "nosniff", + "xRobotsTag": "none", + "xFrameOptions": "SAMEORIGIN", + "contentSecurityPolicy": "frame-src 'self'; frame-ancestors 'self'; object-src 'none';", + "xXSSProtection": "1; mode=block", + "strictTransportSecurity": "max-age=31536000; includeSubDomains" + }, + "smtpServer": {}, + "userFederationProviders": null, + "userFederationMappers": null, + "loginTheme": null, + "accountTheme": null, + "adminTheme": null, + "emailTheme": null, + "eventsEnabled": false, + "eventsExpiration": null, + "eventsListeners": [ + "jboss-logging" + ], + "enabledEventTypes": [], + "adminEventsEnabled": false, + "adminEventsDetailsEnabled": false, + "identityProviders": [ + { + "alias": "SAML_IDP_4", + "displayName": "Test SAML IDP 4", + "internalId": "f2f40e88-3df6-4282-b413-47bec3bbf140", + "providerId": "saml", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "postBrokerLoginFlowAlias": null, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + } + }, + { + "alias": "SAML_IDP_101", + "displayName": "Test SAML IDP 101", + "internalId": "5dcdb5a4-2368-4dec-b249-1d4c2136f17e", + "providerId": "saml", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "postBrokerLoginFlowAlias": null, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + } + }, + { + "alias": "SAML_IDP_102", + "displayName": "Test SAML IDP 101", + "internalId": "8254fc20-5322-41e0-b398-7c8b2f86f5e5", + "providerId": "saml", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "postBrokerLoginFlowAlias": null, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + } + }, + { + "alias": "SAML_IDP_103", + "displayName": "Test SAML IDP 103", + "internalId": "c280efd8-fdea-43e4-b167-6e88b4fc428a", + "providerId": "saml", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "postBrokerLoginFlowAlias": null, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + } + }, + { + "alias": "SAML_IDP_104", + "displayName": "Updated Test SAML IDP 104", + "internalId": "63350037-8ef7-471d-8447-c691063d8c19", + "providerId": "saml", + "enabled": true, + "updateProfileFirstLoginMode": "on", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": "first broker login", + "postBrokerLoginFlowAlias": null, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + } + } + ], + "identityProviderMappers": [], + "protocolMappers": null, + "components": null, + "internationalizationEnabled": false, + "supportedLocales": [], + "defaultLocale": null, + "authenticationFlows": null, + "authenticatorConfig": null, + "requiredActions": null, + "browserFlow": "browser", + "registrationFlow": "registration", + "directGrantFlow": "direct grant", + "resetCredentialsFlow": "reset credentials", + "clientAuthenticationFlow": "clients", + "dockerAuthenticationFlow": "docker auth", + "attributes": { + "cibaBackchannelTokenDeliveryMode": "poll", + "cibaAuthRequestedUserHint": "login_hint", + "oauth2DevicePollingInterval": "5", + "clientOfflineSessionMaxLifespan": "0", + "clientSessionIdleTimeout": "0", + "clientOfflineSessionIdleTimeout": "0", + "cibaInterval": "5", + "realmReusableOtpCode": "false", + "cibaExpiresIn": "120", + "oauth2DeviceCodeLifespan": "600", + "parRequestUriLifespan": "60", + "clientSessionMaxLifespan": "0", + "frontendUrl": "", + "acr.loa.map": "{}" + }, + "keycloakVersion": null, + "userManagedAccessAllowed": false, + "social": null, + "updateProfileOnInitialSocialLogin": null, + "socialProviders": null, + "applicationScopeMappings": null, + "applications": null, + "oauthClients": null, + "clientTemplates": null, + "clientProfiles": { + "profiles": [] + }, + "clientPolicies": { + "policies": [] + } + } +] \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-all-saml-identity-provider.json b/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-all-saml-identity-provider.json new file mode 100644 index 00000000000..1d73ca5f41b --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-all-saml-identity-provider.json @@ -0,0 +1,108 @@ +{ + "start": 0, + "totalEntriesCount": 3, + "entriesCount": 3, + "entries": [ + { + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" + }, + { + "dn": "inum=8254fc20-5322-41e0-b398-7c8b2f86f5e5,ou=trusted-idp,o=jans", + "inum": "8254fc20-5322-41e0-b398-7c8b2f86f5e5", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_102", + "displayName": "Test SAML IDP 101", + "description": "SAML IDP 101 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=8254fc20-5322-41e0-b398-7c8b2f86f5e5,ou=trusted-idp,o=jans" + }, + { + "dn": "inum=ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c,ou=trusted-idp,o=jans", + "inum": "ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=ff28d5af-80a5-4ce2-8157-bd1a37ea4a3c,ou=trusted-idp,o=jans" + } + ] +} \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-saml-identity-provider-by-inum.json b/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-saml-identity-provider-by-inum.json new file mode 100644 index 00000000000..ee54ce972bf --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/trust-idp/get-saml-identity-provider-by-inum.json @@ -0,0 +1,34 @@ +{ + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" +} \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/trust-idp/post-saml-identity-provider.json b/jans-config-api/server/src/main/resources/example/idp/trust-idp/post-saml-identity-provider.json new file mode 100644 index 00000000000..366c044eaa9 --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/trust-idp/post-saml-identity-provider.json @@ -0,0 +1,16 @@ +{ + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Test SAML IDP 104", "description": "SAML IDP 104 created using Rest API", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "SAML_IDP_104", + "trustEmail": false, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "firstBrokerLoginFlowAlias": null, + "postBrokerLoginFlowAlias": null + +} \ No newline at end of file diff --git a/jans-config-api/server/src/main/resources/example/idp/trust-idp/put-saml-identity-provider.json b/jans-config-api/server/src/main/resources/example/idp/trust-idp/put-saml-identity-provider.json new file mode 100644 index 00000000000..0b7a3d45f40 --- /dev/null +++ b/jans-config-api/server/src/main/resources/example/idp/trust-idp/put-saml-identity-provider.json @@ -0,0 +1,34 @@ +{ + "dn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans", + "inum": "63350037-8ef7-471d-8447-c691063d8c19", + "creatorId": "jans-admin-2", + "name": "SAML_IDP_104", + "displayName": "Updated Test SAML IDP 104", + "description": "SAML IDP 104 created using Rest API Updated", + "realm": "keycloak-internal-identity", + "enabled": true, + "providerId": "saml", + "trustEmail": true, + "storeToken": false, + "addReadTokenRoleOnCreate": false, + "authenticateByDefault": false, + "linkOnly": false, + "idpMetaDataFN": "63350037-8ef7-471d-8447-c691063d8c19-idp-metadata.xml", + "config": { + "validateSignature": "false", + "signingCertificate": "MIIDpzCCAo8CFEJ8QVFd7aejj6RdC9XqybNbp2eBMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQzWhcNMjQxMDExMTU0NTQzWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAoXB81Zlr2ZB1jnplGZ+w3ivhXqLLh+CQWv6DwmtL I2UVHiRZUeSUgWt50JCE4lALt3I1cTqi/uDqtB+nn9/HfxOa73KsJ6/7bY8nWsT+ j1na25H3XnPS4l6c9/WNQAw7VtAHnUvriyzSxD6Ph4Cyodu76yAVxVetgtBEwA/5 /mMVUVoJuVbQ/OvhhM+/Q/gfLgKQxto2Hw3S+wBkBOQXVBLa4Rie6+xXk5VnpAu+ HlqKwxtxdrVWVzImq5YUk/hc7itTi1kfowvXNhrZE0XdzDtiI8Fmxvv2Z2CHCA4E clSuLFmSs82z/6URbzEuxxSk6j0YwjDi/GFkCpdNBbQYDwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQAxO1WswGI8jtTTqw2WgDv/bR5/a0ykZB2TQPKtD1vaIaL9m/jI 5i0Fag3wsJYKiS/eKEXyNtEBDod3164BBan6EBh/AWdIzVHVbCaLocZuLGXINejn 2oB9BrSfYp4rG7SQgMxdh7j+a8VAGBYSUfGqqAwrl4h1QgyNs6qI6eooKTIDMpMZ shSFeKuyeC1plnwqer3BPRCPq0PliILmb9nLhhrLbm5VWrhQbYLZ2Mn8BLiZzEnE TODhtM6cR57DW3BLEvjVxlDAUigvYJihmAbfvgyCSNM2NsylJmQUB7PSO4BTknqG FfQeqvuEiE02g70E+s4HrQH2RSij9w+zl1eQ", + "postBindingLogout": "true", + "singleLogoutServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SLO", + "postBindingResponse": "true", + "nameIDPolicyFormat": "urn:mace:shibboleth:1.0:nameIdentifier", + "idpEntityId": "https://pujavs-crack-flounder.gluu.info/idp/shibboleth", + "loginHint": "false", + "enabledFromMetadata": "true", + "postBindingAuthnRequest": "true", + "singleSignOnServiceUrl": "https://pujavs-crack-flounder.gluu.info/idp/profile/SAML2/POST/SSO", + "wantAuthnRequestsSigned": "false", + "addExtensionsElementWithKeyInfo": "false", + "encryptionPublicKey": "MIIDpzCCAo8CFCp0GLTRVjJH2zR9GaTtrRct9bwUMA0GCSqGSIb3DQEBCwUAMIGP MQswCQYDVQQGEwJJTjELMAkGA1UECAwCTUgxDzANBgNVBAcMBm11bWJhaTEaMBgG A1UECgwRR2x1dSBJRFAgU2VydmVyIDIxKDAmBgNVBAMMH3B1amF2cy1jcmFjay1m bG91bmRlci5nbHV1LmluZm8xHDAaBgkqhkiG9w0BCQEWDXB1amFAZ2x1dS5vcmcw HhcNMjMxMDEyMTU0NTQyWhcNMjQxMDExMTU0NTQyWjCBjzELMAkGA1UEBhMCSU4x CzAJBgNVBAgMAk1IMQ8wDQYDVQQHDAZtdW1iYWkxGjAYBgNVBAoMEUdsdXUgSURQ IFNlcnZlciAyMSgwJgYDVQQDDB9wdWphdnMtY3JhY2stZmxvdW5kZXIuZ2x1dS5p bmZvMRwwGgYJKoZIhvcNAQkBFg1wdWphQGdsdXUub3JnMIIBIjANBgkqhkiG9w0B AQEFAAOCAQ8AMIIBCgKCAQEAsCBv4UBQPkXvnwc9IaZHMpn79Dqh6wyGesTPslsQ EbRp3L4aX5xxu/0BKeH4MJhna9rg4ikEP9KPZ7bjXuALXjqqwADnDKYp+EbI/hC5 K2X4wcFb2BGfSlUb4OdzeozSF6MJWC7nfB0o6FTgMlpHRM4A/UymxF8koqy19ESj c1GK+rD7steLhIVQ8tzO80rgkGWlixKf1FoTZw5Rhcpl0h0b15AUVGcFWSx1On1F VLZEeYrLI5vMt2QMHVhO+OfB7cioS2IGYIWeO2IW8ePTwAw/bD/ZzQP6rPYyKYAh newGXcdIjewafrXvwKiX3hUF26SJSupX6xY+W68gahi7YwIDAQABMA0GCSqGSIb3 DQEBCwUAA4IBAQB5PnQeGcMsnGkEOtETvdIKm67C2a2+OWoHyVhZtlYvHSlbFIx6 FAt3/ufX8vJ5N0+D4ReQw5tT3VdDhWhvhrshG50HKR1bF1NIW152v/0B1BxjyhQo p0M6Z2uaCOP7zUd3ZLEIsNH3tqbFV1d+DVLfZDBCUAIOTTySkHWnBjVXlQ2ss63O +SBYBA0U61nxBggzt7UiXBJLyB5+CFafTD4tcGkHjIKLk1CrDPj/4d8d+9EETm0g bVa4bIL1Qb1mUcxQ75xdeuSSp+0u/nk/QBpasFH4uQQWQ0KNviSREl3ShqerybPM EoU3+f42DbxGNwTmEPULvuQAK1k0sxcHuB5I" + }, + "baseDn": "inum=63350037-8ef7-471d-8447-c691063d8c19,ou=trusted-idp,o=jans" +} \ No newline at end of file diff --git a/jans-config-api/shared/src/main/java/io/jans/configapi/core/model/ValidationStatus.java b/jans-config-api/shared/src/main/java/io/jans/configapi/core/model/ValidationStatus.java new file mode 100644 index 00000000000..ed9fef8c738 --- /dev/null +++ b/jans-config-api/shared/src/main/java/io/jans/configapi/core/model/ValidationStatus.java @@ -0,0 +1,56 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.configapi.core.model; + +import java.util.HashMap; +import java.util.Map; + +import io.jans.orm.annotation.AttributeEnum; + + +public enum ValidationStatus implements AttributeEnum { + + PENDING("In Progress", "In Progress"), SUCCESS("Success", "Success"), SCHEDULED("Scheduled", + "Scheduled"), FAILED("Failed", "Failed"); + + private String value; + private String displayName; + + private static Map mapByValues = new HashMap(); + static { + for (ValidationStatus enumType : values()) { + mapByValues.put(enumType.getValue(), enumType); + } + } + + private ValidationStatus(String value, String displayName) { + this.value = value; + this.displayName = displayName; + } + + public String getValue() { + return value; + } + + public String getDisplayName() { + return displayName; + } + + public static ValidationStatus getByValue(String value) { + return mapByValues.get(value); + } + + public Enum resolveByValue(String value) { + return getByValue(value); + } + + @Override + public String toString() { + return value; + } + +} diff --git a/jans-core/service/src/main/java/io/jans/service/custom/CustomScriptService.java b/jans-core/service/src/main/java/io/jans/service/custom/CustomScriptService.java index 1d66e0c1f46..d77e59282c8 100644 --- a/jans-core/service/src/main/java/io/jans/service/custom/CustomScriptService.java +++ b/jans-core/service/src/main/java/io/jans/service/custom/CustomScriptService.java @@ -64,14 +64,11 @@ public PagedResult searchScripts(String pattern, String sortBy, St } Filter filter = searchFilter; - log.debug("filter:{}", filter); if (type != null) { Filter typeFilter = Filter.createEqualityFilter(OxConstants.SCRIPT_TYPE, type); filter = Filter.createANDFilter(searchFilter, typeFilter); } - log.debug("Searching CustomScript Flow with filter:{}", filter); - return persistenceEntryManager.findPagedEntries(baseDn(), CustomScript.class, filter, null, sortBy, SortOrder.getByValue(sortOrder), startIndex, limit, maximumRecCount); @@ -110,6 +107,7 @@ public PagedResult searchScripts(SearchRequest searchRequest, Cust for (Map.Entry entry : searchRequest.getFieldValueMap().entrySet()) { Filter dataFilter = Filter.createEqualityFilter(entry.getKey(), entry.getValue()); log.trace("CustomScript dataFilter:{}", dataFilter); + log.trace("CustomScript entry.getKey():{}, entry.getValue():{}, entry.getValue().getClass():{}", entry.getKey(),entry.getValue(), (entry.getValue()!=null? entry.getValue().getClass():" ")); fieldValueFilters.add(Filter.createANDFilter(dataFilter)); } searchFilter = Filter.createANDFilter(Filter.createORFilter(filters), @@ -124,8 +122,8 @@ public PagedResult searchScripts(SearchRequest searchRequest, Cust Filter typeFilter = Filter.createEqualityFilter(OxConstants.SCRIPT_TYPE, type); filter = Filter.createANDFilter(searchFilter, typeFilter); } - - log.info("Searching CustomScript Flow with filter:{}", filter); + + log.trace("Searching CustomScript Flow with filter:{}", filter); return persistenceEntryManager.findPagedEntries(baseDn(), CustomScript.class, filter, null, searchRequest.getSortBy(), SortOrder.getByValue(searchRequest.getSortOrder()), searchRequest.getStartIndex(), searchRequest.getCount(), searchRequest.getMaxCount()); diff --git a/jans-linux-setup/jans_setup/schema/jans_schema.json b/jans-linux-setup/jans_setup/schema/jans_schema.json index 0240b811474..68f3986ecb5 100644 --- a/jans-linux-setup/jans_setup/schema/jans_schema.json +++ b/jans-linux-setup/jans_setup/schema/jans_schema.json @@ -3471,16 +3471,164 @@ "x_origin": "Jans created attribute" }, { - "desc": "SAML Trust Relationship file location of metadata", - "equality": "caseIgnoreMatch", - "names": [ - "jansSAMLspMetaDataFN" - ], - "oid": "jansAttr", - "substr": "caseIgnoreSubstringsMatch", - "syntax": "1.3.6.1.4.1.1466.115.121.1.15", - "x_origin": "Jans created attribute" - } + "desc": "SAML Trust Relationship file location of metadata", + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLspMetaDataFN" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "Provider Id", + "equality": "caseIgnoreMatch", + "names": [ + "providerId" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "Realm Name", + "equality": "caseIgnoreMatch", + "names": [ + "realm" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "If enabled, email provided by this provider is not verified even if verification is enabled for the realm.", + "equality": "booleanMatch", + "names": [ + "trustEmail" + ], + "oid": "jansAttr", + "syntax": "1.3.6.1.4.1.1466.115.121.1.7", + "x_origin": "Jans created attribute" + }, + { + "desc": "Enable or disable if tokens must be stored after authenticating users.", + "equality": "booleanMatch", + "names": [ + "storeToken" + ], + "oid": "jansAttr", + "syntax": "1.3.6.1.4.1.1466.115.121.1.7", + "x_origin": "Jans created attribute" + }, + { + "desc": "Enable and disable if new users can read any stored tokens.", + "equality": "booleanMatch", + "names": [ + "addReadTokenRoleOnCreate" + ], + "oid": "jansAttr", + "syntax": "1.3.6.1.4.1.1466.115.121.1.7", + "x_origin": "Jans created attribute" + }, + { + "desc": "Authenticate enabled", + "equality": "booleanMatch", + "names": [ + "authenticateByDefault" + ], + "oid": "jansAttr", + "syntax": "1.3.6.1.4.1.1466.115.121.1.7", + "x_origin": "Jans created attribute" + }, + { + "desc": "If true, users cannot log in through this provider. They can only link to this provider. This is useful if you don´t want to allow login from the provider, but want to integrate with a provider", + "equality": "booleanMatch", + "names": [ + "linkOnly" + ], + "oid": "jansAttr", + "syntax": "1.3.6.1.4.1.1466.115.121.1.7", + "x_origin": "Jans created attribute" + }, + { + "desc": "Alias of authentication flow, which is triggered after first login with this identity provider. Term ´First Login´ means that no Keycloak account is currently linked to the authenticated identity provider account.", + "equality": "caseIgnoreMatch", + "names": [ + "firstBrokerLoginFlowAlias" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "Alias of authentication flow, which is triggered after each login with this identity provider. Useful if you want additional verification of each user authenticated with this identity provider (for example OTP). Leave this to None if you need no any additional authenticators to be triggered after login with this identity provider. Also note that authenticator implementations must assume that user is already set in ClientSession as identity provider already set it.", + "equality": "caseIgnoreMatch", + "names": [ + "postBrokerLoginFlowAlias" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "SAML Trusted IDP file location of metadata", + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLidpMetaDataFN" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "SAML Trusted IDP URI location of metadata", + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLidpMetaDataURL" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLspMetaLocation" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLidpMetaLocation" + ], + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + }, + { + "desc": "SAML IDP dynamic attribute map with key and value", + "equality": "caseIgnoreMatch", + "names": [ + "jansSAMLidpConfDyn" + ], + "json": true, + "oid": "jansAttr", + "substr": "caseIgnoreSubstringsMatch", + "syntax": "1.3.6.1.4.1.1466.115.121.1.15", + "x_origin": "Jans created attribute" + } ], "objectClasses": [ { @@ -4696,7 +4844,7 @@ "kind": "STRUCTURAL", "may": [ "inum", - "owner", + "owner", "jansClntId", "displayName", "description", @@ -4712,10 +4860,10 @@ "jansRedirectURI", "jansWebOrigins", "consentRequired", - "jansSAMLMetaDataFilter", + "jansSAMLMetaDataFilter", "jansSAMLspMetaDataSourceTyp", - "jansSAMLspMetaDataFN", - "jansSAMLspMetaDataURL", + "jansSAMLspMetaDataFN", + "jansSAMLspMetaDataURL", "jansMetaLocation", "jansIsFed", "jansEntityId", @@ -4724,10 +4872,10 @@ "jansReleasedAttr", "url", "jansPostLogoutRedirectURI", - "protocol", + "protocol", "jansStatus", "jansValidationStatus", - "jansValidationLog" + "jansValidationLog" ], "must": [ "objectclass" @@ -4740,6 +4888,67 @@ "top" ], "x_origin": "Jans created objectclass" + }, + { + "kind": "STRUCTURAL", + "may": [ + "inum", + "creatorId", + "name", + "displayName", + "description", + "realm", + "jansEnabled", + "providerId", + "trustEmail", + "storeToken", + "addReadTokenRoleOnCreate", + "authenticateByDefault", + "linkOnly", + "firstBrokerLoginFlowAlias", + "postBrokerLoginFlowAlias", + "jansSAMLspMetaDataFN", + "jansSAMLspMetaDataURL", + "jansSAMLspMetaLocation", + "jansSAMLidpMetaDataFN", + "jansSAMLidpMetaDataURL", + "jansSAMLidpMetaLocation", + "jansStatus", + "jansValidationStatus", + "jansValidationLog", + "jansSAMLidpConfDyn" + ], + "must": [ + "objectclass" + ], + "names": [ + "jansTrustedIdp" + ], + "oid": "jansObjClass", + "sup": [ + "top" + ], + "x_origin": "Jans created objectclass" + }, + { + "kind": "STRUCTURAL", + "may": [ + "inum", + "name", + "displayName", + "jansEnabled" + ], + "must": [ + "objectclass" + ], + "names": [ + "jansRealm" + ], + "oid": "jansObjClass", + "sup": [ + "top" + ], + "x_origin": "Jans created objectclass" } ], "oidMacros": { @@ -4751,4 +4960,4 @@ "jansReserved": "jansOrgOID:0", "jansSyntax": "jansPublished:1" } -} \ No newline at end of file +} diff --git a/jans-linux-setup/jans_setup/setup_app/installers/jans_saml.py b/jans-linux-setup/jans_setup/setup_app/installers/jans_saml.py index 5e0cbd4d02d..44683fdef2c 100644 --- a/jans-linux-setup/jans_setup/setup_app/installers/jans_saml.py +++ b/jans-linux-setup/jans_setup/setup_app/installers/jans_saml.py @@ -57,6 +57,30 @@ def __init__(self): self.idp_config_fn = os.path.join(self.templates_folder, 'keycloak.conf') self.clients_ldif_fn = os.path.join(self.output_folder, 'clients.ldif') + #jans-idp + self.jans_idp_enabled = 'true' + self.jans_idp_realm = 'realm:tobedefined' + self.jans_idp_client_id = 'client_id:tobecreated' + self.jans_idp_client_secret = 'client_secret:tobecreated' + self.jans_idp_grant_type = 'grant_type:tobedefined' + self.jans_idp_user_name = 'user_name:tobedefined' + self.jans_idp_password = 'password:tobedefined' + self.jans_idp_idp_root_dir = 'root_dir:tobedefined' + self.jans_idp_idp_metadata_file_pattern = 'metadata_file_pattern:tobedefined' + self.jans_idp_ignore_validation = 'true' + self.jans_idp_idp_metadata_file = 'metadata_file:tobedefined' + self.jans_idp_ldif_config_fn = os.path.join(self.output_folder, 'jans-idp-configuration.ldif') + self.jans_idp_config_json_fn = os.path.join(self.templates_folder, 'jans-idp-config.json') + self.jans_idp_server_url = 'server_url:tobedefined' + # IDP DIRS + self.jans_idp_idp_metadata_root_dir = os.path.join(self.idp_config_root_dir, 'idp/metadata') + self.jans_idp_idp_metadata_temp_dir = os.path.join(self.idp_config_root_dir, 'idp/temp_metadata') + + # SP DIRS + self.jans_idp_sp_metadata_root_dir = os.path.join(self.idp_config_root_dir, 'sp/metadata') + self.jans_idp_sp_metadata_temp_dir = os.path.join(self.idp_config_root_dir, 'sp/temp_metadata') + + # change this when we figure out this Config.keycloack_hostname = 'localhost' @@ -69,25 +93,31 @@ def install(self): def render_import_templates(self): self.logIt("Preparing base64 encodings configuration files") + self.renderTemplateInOut(self.config_json_fn, self.templates_folder, self.output_folder, pystring=True) Config.templateRenderingDict['saml_dynamic_conf_base64'] = self.generate_base64_ldap_file( - os.path.join( - self.output_folder, - os.path.basename(self.config_json_fn) - ) + os.path.join(self.output_folder,os.path.basename(self.config_json_fn)) ) - self.renderTemplateInOut(self.ldif_config_fn, self.templates_folder, self.output_folder) - self.dbUtils.import_ldif([self.ldif_config_fn]) + + self.renderTemplateInOut(self.jans_idp_config_json_fn, self.templates_folder, self.output_folder, pystring=True) + Config.templateRenderingDict['jans_idp_dynamic_conf_base64'] = self.generate_base64_ldap_file( + os.path.join(self.output_folder,os.path.basename(self.jans_idp_config_json_fn)) + ) + self.renderTemplateInOut(self.jans_idp_ldif_config_fn, self.templates_folder, self.output_folder) + + self.dbUtils.import_ldif([self.ldif_config_fn, self.jans_idp_ldif_config_fn]) def create_folders(self): for saml_dir in (self.idp_root_dir, self.idp_config_root_dir, self.idp_config_temp_meta_dir, self.idp_config_meta_dir, self.idp_config_data_dir, self.idp_config_log_dir, self.idp_config_providers_dir, + self.jans_idp_idp_metadata_root_dir, self.jans_idp_sp_metadata_root_dir, self.jans_idp_sp_metadata_temp_dir, ): self.createDirs(saml_dir) self.chown(self.idp_root_dir, Config.jetty_user, Config.jetty_group, recursive=True) + self.run([paths.cmd_chmod, '0760', saml_dir]) def create_scim_client(self): result = self.check_clients([('saml_scim_client_id', '2100.')]) diff --git a/jans-linux-setup/jans_setup/templates/base.ldif b/jans-linux-setup/jans_setup/templates/base.ldif index 0b91aa048af..6600e396211 100644 --- a/jans-linux-setup/jans_setup/templates/base.ldif +++ b/jans-linux-setup/jans_setup/templates/base.ldif @@ -137,3 +137,13 @@ dn: ou=ssa,o=jans objectClass: top objectClass: organizationalUnit ou: ssa + +dn: ou=trusted-idp,o=jans +objectClass: top +objectClass: organizationalUnit +ou: trusted-idp + +dn: ou=realm,o=jans +objectClass: top +objectClass: organizationalUnit +ou: trusted-idp diff --git a/jans-linux-setup/jans_setup/templates/jans-config-api/dynamic-conf.json b/jans-linux-setup/jans_setup/templates/jans-config-api/dynamic-conf.json index 2f0f17fb8c7..a517c41739f 100644 --- a/jans-linux-setup/jans_setup/templates/jans-config-api/dynamic-conf.json +++ b/jans-linux-setup/jans_setup/templates/jans-config-api/dynamic-conf.json @@ -97,6 +97,11 @@ "name": "saml", "description": "saml plugin", "className": "io.jans.configapi.plugin.saml.rest.ApiApplication" + }, + { + "name": "idp", + "description": "idp plugin", + "className": "io.jans.configapi.plugin.keycloak.idp.broker.rest.IdpApiApplication" } ] diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/pom.xml b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/pom.xml new file mode 100644 index 00000000000..8efe529b06b --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/pom.xml @@ -0,0 +1,101 @@ + + + 4.0.0 + io.jans + jans-keycloak-integration-idp-broker-api + jans-keycloak-integration-idp-broker-api + jar + + + io.jans + jans-keycloak-integration-parent + 1.0.20-SNAPSHOT + + + + ${maven.min-version} + + + + + + + org.keycloak + keycloak-core + + + + org.keycloak + keycloak-admin-client-jakarta + + + + org.keycloak + keycloak-server-spi-private + + + + org.keycloak + keycloak-services + + + + + + com.nimbusds + oauth2-oidc-sdk + + + + + + + + + + org.apache.maven.plugins + maven-dependency-plugin + ${maven-deps-plugin.version} + + + copy + package + + copy-dependencies + + + ${project.build.directory}/deps + runtime + false + + + + + + + + + org.apache.maven.plugins + maven-assembly-plugin + ${maven-assembly-plugin.version} + + + src/assembly/dependencies.xml + + ${project.artifactId}-${project.version} + + + + zip-dependencies + package + + single + + + + + + + + + diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/assembly/dependencies.xml b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/assembly/dependencies.xml new file mode 100644 index 00000000000..392faa3826e --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/assembly/dependencies.xml @@ -0,0 +1,17 @@ + + deps + + zip + + false + + + target/deps/ + . + + *.jar + + + + \ No newline at end of file diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/assembly/deps-zip.xml b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/assembly/deps-zip.xml new file mode 100644 index 00000000000..31321b50e74 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/assembly/deps-zip.xml @@ -0,0 +1,26 @@ + + distribution + + zip + + false + + + + io.jans:jans-scim-model + commons-collections:commons-collections + commons-lang:commons-lang + jakarta.ws.rs:jakarta.ws.rs-api + com.fasterxml.jackson.jaxrs:jackson-jaxrs-base + com.fasterxml.jackson.jaxrs:jackson-jaxrs-json-provider + org.json:json + org.apache.commons:commons-collections4 + org.apache.logging.log4j:log4j-api + org.apache.logging.log4j:log4j-core + + runtime + + + \ No newline at end of file diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/config/JansIdpBrokerConfigSource.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/config/JansIdpBrokerConfigSource.java new file mode 100644 index 00000000000..7f88a81be0a --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/config/JansIdpBrokerConfigSource.java @@ -0,0 +1,134 @@ +package io.jans.kc.idp.broker.config; + +import io.jans.kc.idp.broker.util.Constants; + +import java.io.FileInputStream; +import java.nio.file.FileSystems; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import java.util.Set; + +import org.apache.commons.lang.StringUtils; +import org.eclipse.microprofile.config.spi.ConfigSource; +import org.keycloak.component.ComponentValidationException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +public class JansIdpBrokerConfigSource implements ConfigSource { + + private static Logger logger = LoggerFactory.getLogger(JansIdpBrokerConfigSource.class); + private static final String CONFIG_FILE_NAME = "jans-kc-idp-broker.properties"; + + private String configFilePath = null; + private Properties properties = null; + private Map propertiesMap = new HashMap<>(); + + public JansIdpBrokerConfigSource() { + this.configFilePath = System.getProperty(Constants.JANS_KC_IDP_CONFIG_PROP_PATH); + logger.info("this.configFilePath:{}", configFilePath); + + if (StringUtils.isBlank(configFilePath)) { + throw new ComponentValidationException( + "Configuration property file path `System property` not set, please verify."); + } + + this.loadProperties(); + } + + @Override + public Map getProperties() { + logger.info("\n\n Getting properties \n\n"); + return propertiesMap; + } + + @Override + public Set getPropertyNames() { + logger.debug("\n\n Getting Property Names \n\n"); + try { + return properties.stringPropertyNames(); + + } catch (Exception e) { + logger.error("Unable to read properties from CONFIG_FILE_NAME:{} - error is :{}", CONFIG_FILE_NAME, e); + } + return Collections.emptySet(); + } + + @Override + public int getOrdinal() { + return 800; + } + + @Override + public String getValue(String name) { + try { + logger.trace("JansIdpBrokerConfigSource()::getValue() - name:{} - :{}", name, properties.getProperty(name)); + return properties.getProperty(name); + } catch (Exception e) { + logger.error("Unable to read properties from file:{} - error is :{} ", CONFIG_FILE_NAME, e); + } + + return null; + } + + @Override + public String getName() { + return CONFIG_FILE_NAME; + } + + public String getQualifiedFileName() { + String fileSeparator = FileSystems.getDefault().getSeparator(); + logger.info("\n\n JansIdpBrokerConfigSource()::getQualifiedFileName() - fileSeparator:{}", fileSeparator); + return this.configFilePath + fileSeparator + CONFIG_FILE_NAME; + } + + private Properties loadProperties() { + logger.info("\n\n\n ***** JansIdpBrokerConfigSource::loadProperties() - Properties form Config.Scope "); + + // Get file path + String filePath = getQualifiedFileName(); + logger.info("\n\n\n ***** JansIdpBrokerConfigSource::loadProperties() - properties:{}, filePath:{}", properties, filePath); + + if (StringUtils.isBlank(filePath)) { + logger.error("Property filePath is null!"); + throw new ComponentValidationException("Config property filePath is null!!!"); + } + + // load the file handle for main.properties + try (FileInputStream file = new FileInputStream(filePath)) { + logger.info(" JansIdpBrokerConfigSource::loadProperties() - file:{}", file); + + // load all the properties from this file + properties = new Properties(); + properties.load(file); + properties.stringPropertyNames().stream() + .forEach(key -> propertiesMap.put(key, properties.getProperty(key))); + + logger.debug("JansIdpBrokerConfigSource()::loadProperties() - properties :{}", properties); + + if (properties.isEmpty()) { + logger.error("Could not load config properties!"); + throw new ComponentValidationException("Could not load config properties!!!"); + } + + printProperties(properties); + + } catch (Exception ex) { + logger.error("Failed to load property file", ex); + throw new ComponentValidationException("Failed to load property file!!!"); + } + + return properties; + } + + private static void printProperties(Properties prop) { + if (prop == null || prop.isEmpty()) { + return; + } + prop.keySet().stream().map(key -> key + ": " + prop.getProperty(key.toString())).forEach(logger::debug); + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/exception/JansIdpBrokerException.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/exception/JansIdpBrokerException.java new file mode 100644 index 00000000000..e4d67bf3a9c --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/exception/JansIdpBrokerException.java @@ -0,0 +1,27 @@ +/* + * Janssen Project software is available under the Apache License (2004). See http://www.apache.org/licenses/ for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.kc.idp.broker.exception; + +public class JansIdpBrokerException extends RuntimeException { + + + public JansIdpBrokerException() { + } + + public JansIdpBrokerException(String message) { + super(message); + } + + public JansIdpBrokerException(Throwable cause) { + super(cause); + } + + public JansIdpBrokerException(String message, Throwable cause) { + super(message, cause); + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/model/JansIdentityProvider.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/model/JansIdentityProvider.java new file mode 100644 index 00000000000..c25bad6789b --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/model/JansIdentityProvider.java @@ -0,0 +1,113 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.kc.idp.broker.model; + + +import com.fasterxml.jackson.annotation.JsonInclude; + +import io.jans.model.GluuStatus; +import io.jans.orm.annotation.AttributeName; +import io.jans.orm.annotation.DataEntry; +import io.jans.orm.annotation.ObjectClass; +import io.jans.orm.model.base.Entry; +import io.swagger.v3.oas.annotations.Hidden; + +import java.util.Collections; +import java.util.List; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Map; +import java.io.Serializable; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Pattern; +import jakarta.validation.constraints.Size; + + +@DataEntry(sortBy = { "displayName" }) +@ObjectClass(value = "jansIdp") +@JsonInclude(JsonInclude.Include.NON_NULL) +public class JansIdentityProvider extends Entry implements Serializable { + + + private transient boolean selected; + + @AttributeName(ignoreDuringUpdate = true) + private String inum; + + @AttributeName + private String remoteIdpName; + + @AttributeName + private String remoteIdpHost; + + @AttributeName(name = "selectedSingleSignOnService") + private String selectedSingleSignOnService; + + @AttributeName(name = "supportedSingleSignOnServices") + private String supportedSingleSignOnServices; + + @AttributeName(name = "signingCertificates") + private String signingCertificates; + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + public String getInum() { + return inum; + } + + public void setInum(String inum) { + this.inum = inum; + } + + public String getRemoteIdpName() { + return remoteIdpName; + } + + public void setRemoteIdpName(String remoteIdpName) { + this.remoteIdpName = remoteIdpName; + } + + public String getRemoteIdpHost() { + return remoteIdpHost; + } + + public void setRemoteIdpHost(String remoteIdpHost) { + this.remoteIdpHost = remoteIdpHost; + } + + public String getSelectedSingleSignOnService() { + return selectedSingleSignOnService; + } + + public void setSelectedSingleSignOnService(String selectedSingleSignOnService) { + this.selectedSingleSignOnService = selectedSingleSignOnService; + } + + public String getSupportedSingleSignOnServices() { + return supportedSingleSignOnServices; + } + + public void setSupportedSingleSignOnServices(String supportedSingleSignOnServices) { + this.supportedSingleSignOnServices = supportedSingleSignOnServices; + } + + public String getSigningCertificates() { + return signingCertificates; + } + + public void setSigningCertificates(String signingCertificates) { + this.signingCertificates = signingCertificates; + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcIdentityBrokerApiApplication.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcIdentityBrokerApiApplication.java new file mode 100644 index 00000000000..5329f644616 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcIdentityBrokerApiApplication.java @@ -0,0 +1,40 @@ +package io.jans.kc.idp.broker.rest; + +import io.jans.kc.idp.broker.util.Constants; + +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.enums.SecuritySchemeType; +import io.swagger.v3.oas.annotations.info.*; +import io.swagger.v3.oas.annotations.tags.*; +import io.swagger.v3.oas.annotations.security.*; +import io.swagger.v3.oas.annotations.servers.*; + +import jakarta.ws.rs.ApplicationPath; +import jakarta.ws.rs.core.Application; +import java.util.HashSet; +import java.util.Set; + +@ApplicationPath(Constants.KEYCLOAK) +@OpenAPIDefinition(info = @Info(title = "Jans Config API - Keycloak", version = "1.0.0", contact = @Contact(name = "Gluu Support", url = "https://support.gluu.org", email = "xxx@gluu.org"), + +license = @License(name = "Apache 2.0", url = "https://github.com/JanssenProject/jans/blob/main/LICENSE")), + +tags = { @Tag(name = "Jans - Keycloak SAML Identity Broker") }, + +servers = { @Server(url = "https://jans.io/", description = "The Jans server") }) + +@SecurityScheme(name = "oauth2", type = SecuritySchemeType.OAUTH2, flows = @OAuthFlows(clientCredentials = @OAuthFlow(tokenUrl = "https://{op-hostname}/.../token", scopes = { +@OAuthScope(name = Constants.KC_SAML_IDP_READ_ACCESS, description = "View Jans Keycloak SAML Identity-broker related information"), +@OAuthcope(name = Constants.KC_SAML_IDP_WRITE_ACCESS, description = "Manage Jans Keycloak SAML Identity-broker related information")} +))) +public class KcIdentityBrokerApiApplication extends Application { + + @Override + public Set> getClasses() { + HashSet> classes = new HashSet<>(); + + classes.add(KcSAMLIdentityBrokerResource.class); + + return classes; + } +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcSAMLIdentityBrokerResource.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcSAMLIdentityBrokerResource.java new file mode 100644 index 00000000000..039b623655e --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/rest/KcSAMLIdentityBrokerResource.java @@ -0,0 +1,75 @@ +package io.jans.kc.idp.broker.rest; + +import com.github.fge.jsonpatch.JsonPatch; +import com.github.fge.jsonpatch.JsonPatchException; + +import io.jans.configapi.core.rest.ProtectedApi; +import io.jans.configapi.core.util.Jackson; +import io.jans.kc.idp.broker.util.Constants; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.parameters.RequestBody; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.ExampleObject; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.security.*; + +import java.io.IOException; +import jakarta.inject.Inject; +import jakarta.validation.constraints.NotNull; +import jakarta.ws.rs.*; +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.core.Response; + +import org.slf4j.Logger; +import org.keycloak.representations.idm.IdentityProviderRepresentation; + +@Path(Constants.IDENTITY_PROVIDER + Constants.SAML_PATH) +@Produces(MediaType.APPLICATION_JSON) +@Consumes(MediaType.APPLICATION_JSON) +public class KcSAMLIdentityBrokerResource { + + @Inject + Logger log; + + @Inject + JansKeycloakService jansKeycloakService; + + @Operation(summary = "Retrieves SAML Identity Provider", description = "Retrieves SAML Identity Provider", operationId = "get-saml-identity-provider", tags = { + "Jans - Keycloak Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.KC_SAML_IDP_READ_ACCESS })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdentityProviderRepresentation.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @GET + @ProtectedApi(scopes = {Constants.KC_SAML_IDP_READ_ACCESS}) + public Response getAllSamlIdentityProvider() { + + + return Response.ok("OK").build(); + } + + @Operation(summary = "Create SAML Identity Provider", description = "Create SAML Identity Provider", operationId = "postt-saml-identity-provider", tags = { + "Jans - Keycloak Identity Broker" }, security = @SecurityRequirement(name = "oauth2", scopes = { + Constants.KC_SAML_IDP_WRITE_ACCESS })) + @RequestBody(description = "String representing patch-document.", content = @Content(mediaType = MediaType.APPLICATION_JSON_PATCH_JSON, schema = @Schema(implementation = IdentityProviderRepresentation.class), examples = { + @ExampleObject(value = "") })) + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Ok", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = IdentityProviderRepresentation.class))), + @ApiResponse(responseCode = "401", description = "Unauthorized"), + @ApiResponse(responseCode = "500", description = "InternalServerError") }) + @POST + @ProtectedApi(scopes = { Constants.KC_SAML_IDP_WRITE_ACCESS }) + public Response patchAppConfigurationProperty(@NotNull IdentityProviderRepresentation identityProviderRepresentation) + throws IOException, JsonPatchException { + log.debug("Create identityProviderRepresentation:{}", identityProviderRepresentation); + + log.debug("Created identityProviderRepresentation:{}", identityProviderRepresentation); + return Response.ok(identityProviderRepresentation).build(); + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/service/KcIdentityBrokerService.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/service/KcIdentityBrokerService.java new file mode 100644 index 00000000000..61d42aa95f5 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/service/KcIdentityBrokerService.java @@ -0,0 +1,191 @@ +package io.jans.kc.idp.broker.service; + +import com.fasterxml.jackson.databind.JsonNode; + +import io.jans.idp.keycloak.util.JansUtil; +import io.jans.scim.model.scim2.SearchRequest; +import io.jans.scim.model.scim2.user.UserResource; +import jakarta.ws.rs.WebApplicationException; + + +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.keycloak.broker.provider.util.SimpleHttp; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.keycloak.util.JsonSerialization; + +public class KcIdentityBrokerService { + + private static Logger LOG = LoggerFactory.getLogger(KcIdentityBrokerService.class); + private static JansUtil jansUtil = new JansUtil(); + + private String getKeycloakServerUrl() { + String scimUserEndpoint = jansUtil.getScimUserEndpoint(); + LOG.info("KcIdentityBrokerService::getScimUserEndpoint() - scimUserEndpoint:{}", scimUserEndpoint); + return scimUserEndpoint; + } + + private String getScimUserSearchEndpoint() { + String scimUserSearchEndpoint = jansUtil.getScimUserSearchEndpoint(); + LOG.info("KcIdentityBrokerService::getScimUserSearchEndpoint() - scimUserSearchEndpoint:{}", scimUserSearchEndpoint); + return scimUserSearchEndpoint; + } + + private String requestAccessToken() { + LOG.info("KcIdentityBrokerService::requestAccessToken()"); + String token = null; + + try { + token = jansUtil.requestScimAccessToken(); + LOG.info("KcIdentityBrokerService::requestAccessToken() - token:{}", token); + } catch (Exception ex) { + LOG.error("KcIdentityBrokerService::requestAccessToken() - Error while generating access token for SCIM endpoint is:{}", + ex); + throw new WebApplicationException( + "KcIdentityBrokerService::requestAccessToken() - Error while generating access token for SCIM endpoint is = " + + ex); + } + return token; + } + + public UserResource getUserById(String inum) { + LOG.info(" KcIdentityBrokerService::getUserById() - inum:{}", inum); + try { + return getData(getScimUserEndpoint() + "/" + inum, this.requestAccessToken()); + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error( + "KcIdentityBrokerService::getUserById() - Error fetching user based on inum:{} from external service is:{} - {} ", + inum, ex.getMessage(), ex); + } + return null; + } + + public UserResource getUserByName(String username) { + LOG.info("KcIdentityBrokerService::getUserByName() - username:{}", username); + try { + + String filter = "userName eq \"" + username + "\""; + return postData(this.getScimUserSearchEndpoint(), this.requestAccessToken(), filter); + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error( + "KcIdentityBrokerService::getUserByName() - Error fetching user based on username:{} from external service is:{} - {} ", + username, ex.getMessage(), ex); + } + return null; + } + + public UserResource getUserByEmail(String email) { + LOG.info(" KcIdentityBrokerService::getUserByEmail() - email:{}", email); + try { + + String filter = "emails[value eq \"" + email + "\"]"; + LOG.info(" KcIdentityBrokerService::getUserByEmail() - filter:{}", filter); + return postData(this.getScimUserSearchEndpoint(), this.requestAccessToken(), filter); + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error( + " KcIdentityBrokerService::getUserByEmail() - Error fetching user based on email:{} from external service is:{} - {} ", + email, ex.getMessage(), ex); + + } + return null; + } + + public UserResource postData(String uri, String accessToken, String filter) { + UserResource user = null; + LOG.info("KcIdentityBrokerService::postData() - uri:{}, accessToken:{}, filter:{}", uri, accessToken, filter); + try { + HttpClient client = HttpClientBuilder.create().build(); + + SearchRequest searchRequest = createSearchRequest(filter); + LOG.info("KcIdentityBrokerService::postData() - client:{}, searchRequest:{}, accessToken:{}", client, searchRequest, + accessToken); + + JsonNode jsonNode = SimpleHttp.doPost(uri, client).auth(accessToken).json(searchRequest).asJson(); + + LOG.info("\n\n KcIdentityBrokerService::postData() - jsonNode:{}", jsonNode); + + user = getUserResourceFromList(jsonNode); + + LOG.info("KcIdentityBrokerService::postData() - user:{}", user); + + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error("\n\n KcIdentityBrokerService::postData() - Error while fetching data is ex:{}", ex); + } + return user; + } + + public UserResource getData(String uri, String accessToken) { + UserResource user = null; + LOG.info("KcIdentityBrokerService::getData() - uri:{}, accessToken:{}", uri, accessToken); + try { + HttpClient client = HttpClientBuilder.create().build(); + + JsonNode jsonNode = SimpleHttp.doGet(uri, client).auth(accessToken).asJson(); + + LOG.info("\n\n KcIdentityBrokerService::getData() - jsonNode:{}", jsonNode); + + user = getUserResource(jsonNode); + + LOG.info("KcIdentityBrokerService::getData() - user:{}", user); + + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error("\n\n KcIdentityBrokerService::getData() - Error while fetching data is ex:{}", ex); + } + return user; + } + + private SearchRequest createSearchRequest(String filter) { + LOG.info("KcIdentityBrokerService::createSearchRequest() - createSearchRequest() - filter:{}", filter); + SearchRequest searchRequest = new SearchRequest(); + searchRequest.setFilter(filter); + + LOG.info(" KcIdentityBrokerService::createSearchRequest() - searchRequest:{}", searchRequest); + + return searchRequest; + } + + private UserResource getUserResourceFromList(JsonNode jsonNode) { + LOG.info(" \n\n KcIdentityBrokerService::getUserResourceFromList() - jsonNode:{}", jsonNode); + + UserResource user = null; + try { + if (jsonNode != null) { + if (jsonNode.get("Resources") != null) { + JsonNode value = jsonNode.get("Resources").get(0); + LOG.info("\n\n *** KcIdentityBrokerService::getUserResourceFromList() - value:{}, value.getClass():{}", value, + value.getClass()); + user = JsonSerialization.readValue(JsonSerialization.writeValueAsBytes(value), UserResource.class); + LOG.info(" KcIdentityBrokerService::getUserResourceFromList() - user:{}, user.getClass():{}", user, + user.getClass()); + } + } + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error("\n\n KcIdentityBrokerService::getUserResourceFromList() - Error while fetching data is ex:{}", ex); + } + return user; + } + + private UserResource getUserResource(JsonNode jsonNode) { + LOG.info(" \n\n KcIdentityBrokerService::getUserResource() - jsonNode:{}", jsonNode); + + UserResource user = null; + try { + if (jsonNode != null) { + user = JsonSerialization.readValue(JsonSerialization.writeValueAsBytes(jsonNode), UserResource.class); + LOG.info(" KcIdentityBrokerService::getUserResource() - user:{}, user.getClass():{}", user, user.getClass()); + } + } catch (Exception ex) { + ex.printStackTrace(); + LOG.error("\n\n KcIdentityBrokerService::getUserResource() - Error while fetching data is ex:{}", ex); + } + return user; + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/Constants.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/Constants.java new file mode 100644 index 00000000000..8ebb2d6a8b9 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/Constants.java @@ -0,0 +1,46 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.kc.idp.broker.util; + +public class Constants { + + private Constants() {} + + public static final String JANS_KC_IDP_CONFIG_PROP_PATH = "jans.kc.idp.prop.path"; + public static final String KEYCLOAK_USER = "/keycloak-user"; + public static final String BASE_URL = "https://localhost"; + + public static final String KEYCLOAK= "/kc"; + public static final String IDENTITY_PROVIDER = "/identity-provider"; + public static final String SAML_PATH = "/saml"; + + public static final String IDP_PATH = "/idp"; + public static final String INUM_PARAM_PATH = "/{inum}"; + public static final String NAME_PARAM_PATH = "/{name}"; + + public static final String SAML = "saml"; + public static final String IDP = "idp"; + public static final String ID = "id"; + + + public static final String UTF8_STRING_ENCODING = "UTF-8"; + public static final String CLIENT_SECRET_BASIC = "client_secret_basic"; + public static final String CLIENT_CREDENTIALS = "client_credentials"; + public static final String RESOURCE_OWNER_PASSWORD_CREDENTIALS = "password"; + + //properties + public static final String KEYCLOAK_SERVER_URL = "keycloak.server.url"; + public static final String AUTH_TOKEN_ENDPOINT = "auth.token.endpoint"; + public static final String KEYCLOAK_SCIM_CLIENT_ID = "keycloak.scim.client.id"; + public static final String KEYCLOAK_SCIM_CLIENT_PASSWORD = "keycloak.scim.client.password"; + + + public static final String KC_SAML_IDP_READ_ACCESS = "https://jans.io/keycloak/saml/idp/config.readonly"; + public static final String KC_SAML_IDP_WRITE_ACCESS = "https://jans.io/keycloak/saml/idp/config.write"; + + +} \ No newline at end of file diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansDataUtil.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansDataUtil.java new file mode 100644 index 00000000000..583f7577cea --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansDataUtil.java @@ -0,0 +1,109 @@ +package io.jans.kc.idp.broker.util; + +import java.beans.IntrospectionException; +import java.beans.PropertyDescriptor; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JansDataUtil { + + private static final Logger logger = LoggerFactory.getLogger(JansDataUtil.class); + + public static Object invokeMethod(Class clazz, String methodName, Class... parameterTypes) + throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { + logger.debug("JansDataUtil::invokeMethod() - Invoke clazz:{} on methodName:{} with name:{} ", clazz, methodName, + parameterTypes); + Object obj = null; + if (clazz == null || methodName == null || parameterTypes == null) { + return obj; + } + Method m = clazz.getDeclaredMethod(methodName, parameterTypes); + obj = m.invoke(null, parameterTypes); + + logger.debug("JansDataUtil::invokeMethod() - methodName:{} returned obj:{} ", methodName, obj); + return obj; + } + + public Object invokeReflectionGetter(Object obj, String variableName) { + logger.debug("JansDataUtil::invokeMethod() - Invoke obj:{}, variableName:{}", obj, variableName); + try { + if (obj == null) { + return obj; + } + PropertyDescriptor pd = new PropertyDescriptor(variableName, obj.getClass()); + Method getter = pd.getReadMethod(); + logger.debug("JansDataUtil::invokeMethod() - Invoke getter:{}", getter); + if (getter != null) { + return getter.invoke(obj); + } else { + logger.error( + "JansDataUtil::invokeReflectionGetter() - Getter Method not found for class:{} property:{}", + obj.getClass().getName(), variableName); + } + } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException + | IntrospectionException e) { + logger.error(String.format( + "JansDataUtil::invokeReflectionGetter() - Getter Method ERROR for class: %s property: %s", + obj.getClass().getName(), variableName), e); + } + return obj; + } + + public static List getAllFields(Class type) { + logger.debug("JansDataUtil::getAllFields() - type:{} ", type); + List allFields = new ArrayList<>(); + if (type == null) { + return allFields; + } + getAllFields(allFields, type); + logger.debug("JansDataUtil::getAllFields() - Fields:{} of type:{} ", allFields, type); + return allFields; + } + + public static List getAllFields(List fields, Class type) { + logger.debug("JansDataUtil::getAllFields() - fields:{} , type:{} ", fields, type); + if (fields == null || type == null) { + return fields; + } + fields.addAll(Arrays.asList(type.getDeclaredFields())); + + if (type.getSuperclass() != null) { + getAllFields(fields, type.getSuperclass()); + } + logger.debug("JansDataUtil::getAllFields() - Final fields:{} of type:{} ", fields, type); + return fields; + } + + public static Map getFieldTypeMap(Class clazz) { + logger.debug("JansDataUtil::getFieldTypeMap() - clazz:{} ", clazz); + Map propertyTypeMap = new HashMap<>(); + + if (clazz == null) { + return propertyTypeMap; + } + + List fields = getAllFields(clazz); + logger.debug("JansDataUtil::getFieldTypeMap() - all-fields:{} ", fields); + + for (Field field : fields) { + logger.debug( + "JansDataUtil::getFieldTypeMap() - field:{} , field.getAnnotatedType():{}, field.getAnnotations():{} , field.getType().getAnnotations():{}, field.getType().getCanonicalName():{} , field.getType().getClass():{} , field.getType().getClasses():{} , field.getType().getComponentType():{}", + field, field.getAnnotatedType(), field.getAnnotations(), field.getType().getAnnotations(), + field.getType().getCanonicalName(), field.getType().getClass(), field.getType().getClasses(), + field.getType().getComponentType()); + propertyTypeMap.put(field.getName(), field.getType().getSimpleName()); + } + logger.debug("JansDataUtil::getFieldTypeMap() - Final propertyTypeMap{} ", propertyTypeMap); + return propertyTypeMap; + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansUtil.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansUtil.java new file mode 100644 index 00000000000..ddc4b6030a3 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/java/io/jans/kc/idp/broker/util/JansUtil.java @@ -0,0 +1,259 @@ +package io.jans.kc.idp.broker.util; + +import com.fasterxml.jackson.databind.JsonNode; + +import io.jans.kc.idp.broker.config.JansIdpBrokerConfigSource; + +import jakarta.ws.rs.core.MediaType; +import jakarta.ws.rs.WebApplicationException; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.util.*; +import java.util.stream.*; + +import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang.StringUtils; +import org.apache.http.client.HttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.keycloak.broker.provider.util.SimpleHttp; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class JansUtil { + private static Logger logger = LoggerFactory.getLogger(JansUtil.class); + private JansIdpBrokerConfigSource jansKcIdpBrokerConfigSource = new JansIdpBrokerConfigSource(); + private Map configProperties = null; + + public JansUtil() { + logger.debug("\n JansUtil() - Getting properties"); + configProperties = jansKcIdpBrokerConfigSource.getProperties(); + if (configProperties == null || configProperties.isEmpty()) { + throw new WebApplicationException("Config properties is null!!!"); + } + } + + public String getTokenEndpoint() { + logger.debug("\n JansUtil::getTokenEndpoint() - configProperties.get(Constants.AUTH_TOKEN_ENDPOINT)():{}", + configProperties.get(Constants.AUTH_TOKEN_ENDPOINT)); + return configProperties.get(Constants.AUTH_TOKEN_ENDPOINT); + } + + + + public String getClientId() { + logger.debug(" \n JansUtil::getClientId() - configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_ID)():{}", + configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_ID)); + return configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_ID); + } + + public String getClientPassword() { + logger.debug(" \n JansUtil::getClientPassword() - configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_PASSWORD)():{}", + configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_PASSWORD)); + return configProperties.get(Constants.KEYCLOAK_SCIM_CLIENT_PASSWORD); + } + + public String getScimOauthScope() { + logger.debug(" \n JansUtil::getScimOauthScope() - configProperties.get(Constants.SCIM_OAUTH_SCOPE)():{}", + configProperties.get(Constants.SCIM_OAUTH_SCOPE)); + return configProperties.get(Constants.SCIM_OAUTH_SCOPE); + } + + public String requestScimAccessToken() throws IOException { + logger.info(" \n JansUtil::requestScimAccessToken() "); + List scopes = new ArrayList<>(); + scopes.add(getScimOauthScope()); + String token = requestAccessToken(getClientId(), scopes); + logger.info("JansUtil::requestScimAccessToken() - token:{} ", token); + return token; + } + + public String requestAccessToken(final String clientId, final List scope) throws IOException { + logger.info("JansUtil::requestAccessToken() - Request for AccessToken - clientId:{}, scope:{} ", clientId, + scope); + + String tokenUrl = getTokenEndpoint(); + String token = getAccessToken(tokenUrl, clientId, scope); + logger.info("JansUtil::requestAccessToken() - oAuth AccessToken response - token:{}", token); + + return token; + } + + public String getAccessToken(final String tokenUrl, final String clientId, final List scopes) + throws IOException { + logger.info("JansUtil::getAccessToken() - Access Token Request - tokenUrl:{}, clientId:{}, scopes:{}", tokenUrl, + clientId, scopes); + + // Get clientSecret + String clientSecret = this.getClientPassword(); + logger.info("JansUtil::getAccessToken() - Access Token Request - clientId:{}, clientSecret:{}", clientId, + clientSecret); + + // distinct scopes + Set scopesSet = new HashSet<>(scopes); + StringBuilder scope = new StringBuilder(Constants.SCOPE_TYPE_OPENID); + for (String s : scopesSet) { + scope.append(" ").append(s); + } + + logger.info("JansUtil::getAccessToken() - Scope required - {}", scope); + + String token = requestAccessToken(tokenUrl, clientId, clientSecret, scope.toString(), + Constants.CLIENT_CREDENTIALS, Constants.CLIENT_SECRET_BASIC, MediaType.APPLICATION_FORM_URLENCODED); + logger.info("JansUtil::getAccessToken() - Final token token - {}", token); + return token; + } + + public String requestAccessToken(final String tokenUrl, final String clientId, final String clientSecret, + final String scope, String grantType, String authenticationMethod, String mediaType) throws IOException { + logger.info( + "JansUtil::requestAccessToken() - Request for Access Token - tokenUrl:{}, clientId:{}, clientSecret:{}, scope:{}, grantType:{}, authenticationMethod:{}, mediaType:{}", + tokenUrl, clientId, clientSecret, scope, grantType, authenticationMethod, mediaType); + String token = null; + try { + + logger.info(" JansUtil::requestAccessToken() - this.getEncodedCredentials():{}", + this.getEncodedCredentials(clientId, clientSecret)); + HttpClient client = HttpClientBuilder.create().build(); + JsonNode jsonNode = SimpleHttp.doPost(tokenUrl, client) + .header("Authorization", "Basic " + this.getEncodedCredentials(clientId, clientSecret)) + .header("Content-Type", mediaType).param("grant_type", "client_credentials") + .param("username", clientId + ":" + clientSecret).param("scope", scope).param("client_id", clientId) + .param("client_secret", clientSecret).param("authorization_method", "client_secret_basic").asJson(); + logger.info("\n JansUtil::requestAccessToken() - POST Request for Access Token - jsonNode:{} ", jsonNode); + + token = this.getToken(jsonNode); + + logger.info("\n JansUtil::requestAccessToken() - After Post request for Access Token - token:{} ", token); + + } catch (Exception ex) { + ex.printStackTrace(); + logger.error("\n JansUtil::requestAccessToken() - Post error is ", ex); + } + return token; + } + + public String requestUserToken(final String tokenUrl, final String username, final String password, + final String scope, String grantType, String authenticationMethod, String mediaType) throws IOException { + logger.info( + "JansUtil::requestUserToken() - Request for Access Token - tokenUrl:{}, username:{}, password:{}, scope:{}, grantType:{}, authenticationMethod:{}, mediaType:{}", + tokenUrl, username, password, scope, grantType, authenticationMethod, mediaType); + String token = null; + try { + String clientId = this.getClientId(); + String clientSecret = this.getClientPassword(); + + logger.info( + " JansUtil::requestUserToken() - clientId:{} , clientSecret:{}, this.getEncodedCredentials():{}", + clientId, clientSecret, this.getEncodedCredentials(clientId, clientSecret)); + HttpClient client = HttpClientBuilder.create().build(); + JsonNode jsonNode = SimpleHttp.doPost(tokenUrl, client) + .header("Authorization", "Basic " + this.getEncodedCredentials(clientId, clientSecret)) + .header("Content-Type", mediaType).param("grant_type", grantType).param("username", username) + .param("password", password).asJson(); + + logger.info("\n JansUtil::requestUserToken() - After invoking post request for user token - jsonNode:{} ", + jsonNode); + + token = this.getToken(jsonNode); + + logger.info("\n JansUtil::requestUserToken() -POST Request for Access Token - token:{} ", token); + + } catch (Exception ex) { + ex.printStackTrace(); + logger.error("\n JansUtil::requestUserToken() - Post error is ", ex); + } + return token; + } + + private boolean validateTokenScope(JsonNode jsonNode, String scope) { + + logger.info(" \n\n JansUtil::validateTokenScope() - jsonNode:{}, scope:{}", jsonNode, scope); + boolean validScope = false; + try { + + List scopeList = Stream.of(scope.split(" ", -1)).collect(Collectors.toList()); + + if (jsonNode != null && jsonNode.get("scope") != null) { + JsonNode value = jsonNode.get("scope"); + logger.info("\n\n *** JansUtil::validateTokenScope() - value:{}", value); + + if (value != null) { + String responseScope = value.toString(); + logger.info( + "JansUtil::validateTokenScope() - scope:{}, responseScope:{}, responseScope.contains(scope):{}", + scope, responseScope, responseScope.contains(scope)); + if (scopeList.contains(responseScope)) { + validScope = true; + } + } + + } + logger.info("JansUtil::validateTokenScope() - validScope:{}", validScope); + + } catch (Exception ex) { + ex.printStackTrace(); + logger.error("\n JansUtil::validateTokenScope() - Error while validating token scope from response is ", + ex); + } + return validScope; + + } + + private String getToken(JsonNode jsonNode) { + logger.info(" \n\n JansUtil::getToken() - jsonNode:{}", jsonNode); + + String token = null; + try { + + if (jsonNode != null && jsonNode.get("access_token") != null) { + JsonNode value = jsonNode.get("access_token"); + logger.info("\n\n *** JansUtil::getToken() - value:{}", value); + + if (value != null) { + token = value.asText(); + } + logger.info("getToken() - token:{}", token); + } + } catch (Exception ex) { + ex.printStackTrace(); + logger.error("\n\n Error while getting token from response is ", ex); + } + return token; + } + + private boolean hasCredentials(String authUsername, String authPassword) { + return (StringUtils.isNotBlank(authUsername) && StringUtils.isNotBlank(authPassword)); + } + + /** + * Returns the client credentials (URL encoded). + * + * @return The client credentials. + */ + private String getCredentials(String authUsername, String authPassword) throws UnsupportedEncodingException { + logger.info("getCredentials() - authUsername:{}, authPassword:{}", authUsername, authPassword); + return URLEncoder.encode(authUsername, Constants.UTF8_STRING_ENCODING) + ":" + + URLEncoder.encode(authPassword, Constants.UTF8_STRING_ENCODING); + } + + private String getEncodedCredentials(String authUsername, String authPassword) { + logger.info("getEncodedCredentials() - authUsername:{}, authPassword:{}", authUsername, authPassword); + try { + if (hasCredentials(authUsername, authPassword)) { + return Base64.encodeBase64String(getBytes(getCredentials(authUsername, authPassword))); + } + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return null; + } + + private static byte[] getBytes(String str) { + return str.getBytes(StandardCharsets.UTF_8); + } + +} diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource new file mode 100644 index 00000000000..fb31e7be233 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.eclipse.microprofile.config.spi.ConfigSource @@ -0,0 +1 @@ +io.jans.idp.keycloak.config.JansConfigSource \ No newline at end of file diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory new file mode 100644 index 00000000000..af5dd9ee27e --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/META-INF/services/org.keycloak.storage.UserStorageProviderFactory @@ -0,0 +1 @@ +io.jans.idp.keycloak.service.RemoteUserStorageProviderFactory \ No newline at end of file diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/jans-kc-idp-broker.properties b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/jans-kc-idp-broker.properties new file mode 100644 index 00000000000..dca70961809 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/jans-kc-idp-broker.properties @@ -0,0 +1,8 @@ +# Sample properties +keycloak.server.url=https://${keycloack_hostname} +jans.auth.server.iiser +auth.token.endpoint=https://${hostname}/jans-auth/restv1/token + +keycloak.scim.client.id=${saml_scim_client_id} +keycloak.scim.client.password=${saml_scim_client_pw} + diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/log4j2.xml b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/log4j2.xml new file mode 100644 index 00000000000..52dd0a7754f --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/main/resources/log4j2.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/test/java/io/jans/idp/keycloak/TestJenkinsRunner.java b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/test/java/io/jans/idp/keycloak/TestJenkinsRunner.java new file mode 100644 index 00000000000..cd56722799c --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-keycloak-link/idp-broker-api/src/test/java/io/jans/idp/keycloak/TestJenkinsRunner.java @@ -0,0 +1,29 @@ +/* + * Janssen Project software is available under the MIT License (2008). See http://opensource.org/licenses/MIT for full text. + * + * Copyright (c) 2020, Janssen Project + */ + +package io.jans.idp.keycloak; + +import org.apache.commons.io.FileUtils; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class TestJenkinsRunner { + + void testParallel() { + + } + + public static void generateReport(String karateOutputPath) { + + } +} diff --git a/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-config.json b/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-config.json new file mode 100644 index 00000000000..c3a33ef415a --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-config.json @@ -0,0 +1,24 @@ +{ + "applicationName":"idp", + "trustedIdpDn":"ou=trusted-idp,o=jans", + "realmDn":"ou=realm,o=jans", + "enabled": "${jans_idp_enabled}", + "serverUrl":"https://${jans_idp_server_url}", + "realm": "${jans_idp_realm}", + "clientId": "${jans_idp_client_id}", + "clientSecret": "${jans_idp_client_secret}", + "grantType": "${jans_idp_grant_type}", + "username": "${jans_idp_user_name}", + "password": "${jans_idp_password}", + "idpRootDir": "${jans_idp_idp_root_dir}", + "idpMetadataRootDir": "${jans_idp_idp_metadata_root_dir}", + "idpMetadataTempDir": "${jans_idp_idp_metadata_temp_dir}", + "idpMetadataFilePattern": "${jans_idp_idp_metadata_file_pattern}", + "idpMetadataFile": "${jans_idp_idp_metadata_file}", + "spMetadataUrl":"/realms/%s/broker/%s/endpoint/descriptor", + "spMetadataRootDir": "${jans_idp_sp_metadata_root_dir}", + "spMetadataTempDir": "${jans_idp_sp_metadata_temp_dir}", + "spMetadataFilePattern":"%s-sp-metadata.xml", + "spMetadataFile":"sp-metadata.xml", + "ignoreValidation": "${jans_idp_ignore_validation}" +} diff --git a/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-configuration.ldif b/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-configuration.ldif new file mode 100644 index 00000000000..b061505a6f4 --- /dev/null +++ b/jans-linux-setup/jans_setup/templates/jans-saml/jans-idp-configuration.ldif @@ -0,0 +1,6 @@ +dn: ou=jans-idp,ou=configuration,o=jans +jansConfDyn::%(jans_idp_dynamic_conf_base64)s +jansRevision: 1 +objectClass: top +objectClass: jansAppConf +ou: jans-idp diff --git a/jans-linux-setup/jans_setup/templates/jans.properties b/jans-linux-setup/jans_setup/templates/jans.properties index bfba508dc02..0ee748b1478 100644 --- a/jans-linux-setup/jans_setup/templates/jans.properties +++ b/jans-linux-setup/jans_setup/templates/jans.properties @@ -7,6 +7,7 @@ configApi_ConfigurationEntryDN=ou=jans-config-api,ou=configuration,o=jans link_ConfigurationEntryDN=ou=jans-link,ou=configuration,o=jans keycloakLink_ConfigurationEntryDN=ou=jans-keycloak-link,ou=configuration,o=jans saml_ConfigurationEntryDN=ou=jans-saml,ou=configuration,o=jans +idp_ConfigurationEntryDN=ou=jans-idp,ou=configuration,o=jans certsDir=%(certFolder)s confDir=