keycloak-uncached
Changes
client-api/pom.xml 31(+31 -0)
client-api/src/main/java/org/keycloak/client/registration/ClientRegistrationException.java 16(+16 -0)
dependencies/server-min/pom.xml 4(+0 -4)
distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml 1(+1 -0)
distribution/docs-dist/assembly.xml 8(+6 -2)
distribution/downloads/pom.xml 101(+100 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml 1(+0 -1)
distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml 1(+0 -1)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/bouncycastle/main/module.xml 14(+2 -12)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml 18(+9 -9)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml 17(+17 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml 17(+17 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml 17(+17 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml 35(+35 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml 24(+24 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml 45(+45 -0)
distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml 20(+20 -0)
distribution/saml-adapters/pom.xml 8(+8 -0)
distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml 1(+0 -1)
distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml 1(+0 -1)
docbook/pom.xml 2(+1 -1)
examples/js-console/README.md 2(+1 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java 10(+10 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java 4(+3 -1)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java 4(+4 -0)
federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java 6(+6 -0)
forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties 348(+347 -1)
forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties 346(+345 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js 51(+13 -38)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html 38(+19 -19)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html 12(+6 -6)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html 6(+3 -3)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html 10(+5 -5)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html 28(+14 -14)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html 28(+14 -14)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html 171(+85 -86)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-import.html 18(+9 -9)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html 8(+4 -4)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html 30(+15 -15)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html 22(+11 -11)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html 22(+11 -11)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html 20(+10 -10)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html 12(+6 -6)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html 64(+32 -32)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html 18(+9 -9)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html 30(+15 -15)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html 26(+13 -13)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html 26(+13 -13)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html 48(+24 -24)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html 44(+22 -22)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-sessions.html 20(+10 -10)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html 26(+13 -13)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html 14(+7 -7)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html 40(+20 -20)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html 22(+11 -11)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html 142(+71 -71)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html 130(+65 -65)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html 70(+35 -35)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html 4(+2 -2)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html 12(+6 -6)
forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html 40(+19 -21)
integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java 10(+10 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java 15(+15 -0)
integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java 4(+2 -2)
integration/pom.xml 2(+2 -0)
integration/servlet-adapter-spi/pom.xml 29(+8 -21)
integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java 400(+400 -0)
integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java 196(+196 -0)
integration/servlet-filter/pom.xml 83(+83 -0)
integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java 86(+86 -0)
integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java 162(+162 -0)
integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java 167(+167 -0)
integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java 23(+23 -0)
integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java 12(+7 -5)
pom.xml 77(+75 -2)
saml/client-adapter/as7-eap6/adapter/pom.xml 101(+101 -0)
saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java 57(+57 -0)
saml/client-adapter/as7-eap6/pom.xml 20(+20 -0)
saml/client-adapter/as7-eap6/subsystem/pom.xml 115(+115 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java 93(+93 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java 67(+67 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java 19(+19 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java 76(+76 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java 59(+59 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java 47(+47 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java 67(+67 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java 42(+42 -0)
saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension 1(+1 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java 2(+1 -1)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java 7(+7 -0)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java 34(+17 -17)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java 28(+13 -15)
saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java 47(+35 -12)
saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java 2(+1 -1)
saml/client-adapter/pom.xml 2(+2 -0)
saml/client-adapter/servlet-filter/pom.xml 69(+69 -0)
saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java 134(+134 -0)
saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java 157(+157 -0)
saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java 6(+2 -4)
saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java 2(+0 -2)
services/pom.xml 4(+0 -4)
services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java 11(+8 -3)
services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java 21(+10 -11)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java 16(+16 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProviderFactory.java 9(+9 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java 37(+37 -0)
services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java 30(+30 -0)
services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java 155(+155 -0)
services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProviderFactory.java 34(+34 -0)
services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java 34(+34 -0)
services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java 34(+34 -0)
services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory 1(+1 -0)
testsuite/integration/pom.xml 8(+8 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java 1(+1 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java 214(+214 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SendUsernameServlet.java 6(+5 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java 20(+20 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java 147(+147 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlKeycloakRule.java 125(+125 -0)
testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java 8(+3 -5)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java 15(+4 -11)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java 77(+77 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 4(+4 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java 306(+306 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java 2(+2 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java 2(+2 -0)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty81/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty91/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/jetty/jetty92/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/performance/pom.xml 4(+0 -4)
testsuite/tomcat6/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml 4(+2 -2)
testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml 4(+2 -2)
Details
client-api/pom.xml 31(+31 -0)
diff --git a/client-api/pom.xml b/client-api/pom.xml
new file mode 100755
index 0000000..e386849
--- /dev/null
+++ b/client-api/pom.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-client-api</artifactId>
+ <name>Keycloak Client API</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+
+</project>
diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
new file mode 100644
index 0000000..24422c5
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistration.java
@@ -0,0 +1,275 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.HttpHeaders;
+import org.apache.http.HttpRequest;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpDelete;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.client.methods.HttpPut;
+import org.apache.http.entity.StringEntity;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.util.Base64;
+import org.keycloak.util.JsonSerialization;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegistration {
+
+ private String clientRegistrationUrl;
+ private HttpClient httpClient;
+ private Auth auth;
+
+ public static ClientRegistrationBuilder create() {
+ return new ClientRegistrationBuilder();
+ }
+
+ private ClientRegistration() {
+ }
+
+ public ClientRepresentation create(ClientRepresentation client) throws ClientRegistrationException {
+ String content = serialize(client);
+ InputStream resultStream = doPost(content);
+ return deserialize(resultStream, ClientRepresentation.class);
+ }
+
+ public ClientRepresentation get() throws ClientRegistrationException {
+ if (auth instanceof ClientIdSecretAuth) {
+ String clientId = ((ClientIdSecretAuth) auth).clientId;
+ return get(clientId);
+ } else {
+ throw new ClientRegistrationException("Requires client authentication");
+ }
+ }
+
+ public ClientRepresentation get(String clientId) throws ClientRegistrationException {
+ InputStream resultStream = doGet(clientId);
+ return resultStream != null ? deserialize(resultStream, ClientRepresentation.class) : null;
+ }
+
+ public void update(ClientRepresentation client) throws ClientRegistrationException {
+ String content = serialize(client);
+ doPut(content, client.getClientId());
+ }
+
+ public void delete() throws ClientRegistrationException {
+ if (auth instanceof ClientIdSecretAuth) {
+ String clientId = ((ClientIdSecretAuth) auth).clientId;
+ delete(clientId);
+ } else {
+ throw new ClientRegistrationException("Requires client authentication");
+ }
+ }
+
+ public void delete(String clientId) throws ClientRegistrationException {
+ doDelete(clientId);
+ }
+
+ public void close() throws ClientRegistrationException {
+ if (httpClient instanceof CloseableHttpClient) {
+ try {
+ ((CloseableHttpClient) httpClient).close();
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to close http client", e);
+ }
+ }
+ }
+
+ private InputStream doPost(String content) throws ClientRegistrationException {
+ try {
+ HttpPost request = new HttpPost(clientRegistrationUrl);
+
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+ request.setEntity(new StringEntity(content));
+
+ auth.addAuth(request);
+
+ HttpResponse response = httpClient.execute(request);
+ InputStream responseStream = null;
+ if (response.getEntity() != null) {
+ responseStream = response.getEntity().getContent();
+ }
+
+ if (response.getStatusLine().getStatusCode() == 201) {
+ return responseStream;
+ } else {
+ responseStream.close();
+ throw new HttpErrorException(response.getStatusLine());
+ }
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to send request", e);
+ }
+ }
+
+ private InputStream doGet(String endpoint) throws ClientRegistrationException {
+ try {
+ HttpGet request = new HttpGet(clientRegistrationUrl + "/" + endpoint);
+
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+
+ auth.addAuth(request);
+
+ HttpResponse response = httpClient.execute(request);
+ InputStream responseStream = null;
+ if (response.getEntity() != null) {
+ responseStream = response.getEntity().getContent();
+ }
+
+ if (response.getStatusLine().getStatusCode() == 200) {
+ return responseStream;
+ } else if (response.getStatusLine().getStatusCode() == 404) {
+ responseStream.close();
+ return null;
+ } else {
+ responseStream.close();
+ throw new HttpErrorException(response.getStatusLine());
+ }
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to send request", e);
+ }
+ }
+
+ private void doPut(String content, String endpoint) throws ClientRegistrationException {
+ try {
+ HttpPut request = new HttpPut(clientRegistrationUrl + "/" + endpoint);
+
+ request.setHeader(HttpHeaders.CONTENT_TYPE, "application/json");
+ request.setHeader(HttpHeaders.ACCEPT, "application/json");
+ request.setEntity(new StringEntity(content));
+
+ auth.addAuth(request);
+
+ HttpResponse response = httpClient.execute(request);
+ if (response.getEntity() != null) {
+ response.getEntity().getContent().close();
+ }
+
+ if (response.getStatusLine().getStatusCode() != 200) {
+ throw new HttpErrorException(response.getStatusLine());
+ }
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to send request", e);
+ }
+ }
+
+ private void doDelete(String endpoint) throws ClientRegistrationException {
+ try {
+ HttpDelete request = new HttpDelete(clientRegistrationUrl + "/" + endpoint);
+
+ auth.addAuth(request);
+
+ HttpResponse response = httpClient.execute(request);
+ if (response.getEntity() != null) {
+ response.getEntity().getContent().close();
+ }
+
+ if (response.getStatusLine().getStatusCode() != 200) {
+ throw new HttpErrorException(response.getStatusLine());
+ }
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to send request", e);
+ }
+ }
+
+ private String serialize(ClientRepresentation client) throws ClientRegistrationException {
+ try {
+ return JsonSerialization.writeValueAsString(client);
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to write json object", e);
+ }
+ }
+
+ private <T> T deserialize(InputStream inputStream, Class<T> clazz) throws ClientRegistrationException {
+ try {
+ return JsonSerialization.readValue(inputStream, clazz);
+ } catch (IOException e) {
+ throw new ClientRegistrationException("Failed to read json object", e);
+ }
+ }
+
+ public static class ClientRegistrationBuilder {
+
+ private String realm;
+
+ private String authServerUrl;
+
+ private Auth auth;
+
+ private HttpClient httpClient;
+
+ public ClientRegistrationBuilder realm(String realm) {
+ this.realm = realm;
+ return this;
+ }
+ public ClientRegistrationBuilder authServerUrl(String authServerUrl) {
+ this.authServerUrl = authServerUrl;
+ return this;
+ }
+
+ public ClientRegistrationBuilder auth(String token) {
+ this.auth = new TokenAuth(token);
+ return this;
+ }
+
+ public ClientRegistrationBuilder auth(String clientId, String clientSecret) {
+ this.auth = new ClientIdSecretAuth(clientId, clientSecret);
+ return this;
+ }
+
+ public ClientRegistrationBuilder httpClient(HttpClient httpClient) {
+ this.httpClient = httpClient;
+ return this;
+ }
+
+ public ClientRegistration build() {
+ ClientRegistration clientRegistration = new ClientRegistration();
+ clientRegistration.clientRegistrationUrl = authServerUrl + "/realms/" + realm + "/client-registration/default";
+
+ clientRegistration.httpClient = httpClient != null ? httpClient : HttpClients.createDefault();
+ clientRegistration.auth = auth;
+
+ return clientRegistration;
+ }
+
+ }
+
+ public interface Auth {
+ void addAuth(HttpRequest httpRequest);
+ }
+
+ public static class AuthorizationHeaderAuth implements Auth {
+ private String credentials;
+
+ public AuthorizationHeaderAuth(String credentials) {
+ this.credentials = credentials;
+ }
+
+ public void addAuth(HttpRequest httpRequest) {
+ httpRequest.setHeader(HttpHeaders.AUTHORIZATION, credentials);
+ }
+ }
+
+ public static class TokenAuth extends AuthorizationHeaderAuth {
+ public TokenAuth(String token) {
+ super("Bearer " + token);
+ }
+ }
+
+ public static class ClientIdSecretAuth extends AuthorizationHeaderAuth {
+ private String clientId;
+
+ public ClientIdSecretAuth(String clientId, String clientSecret) {
+ super("Basic " + Base64.encodeBytes((clientId + ":" + clientSecret).getBytes()));
+ this.clientId = clientId;
+ }
+ }
+
+}
diff --git a/client-api/src/main/java/org/keycloak/client/registration/ClientRegistrationException.java b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistrationException.java
new file mode 100644
index 0000000..43f743c
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/ClientRegistrationException.java
@@ -0,0 +1,16 @@
+package org.keycloak.client.registration;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegistrationException extends Exception {
+
+ public ClientRegistrationException(String s, Throwable throwable) {
+ super(s, throwable);
+ }
+
+ public ClientRegistrationException(String s) {
+ super(s);
+ }
+
+}
diff --git a/client-api/src/main/java/org/keycloak/client/registration/HttpErrorException.java b/client-api/src/main/java/org/keycloak/client/registration/HttpErrorException.java
new file mode 100644
index 0000000..b25e033
--- /dev/null
+++ b/client-api/src/main/java/org/keycloak/client/registration/HttpErrorException.java
@@ -0,0 +1,22 @@
+package org.keycloak.client.registration;
+
+import org.apache.http.StatusLine;
+
+import java.io.IOException;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class HttpErrorException extends IOException {
+
+ private StatusLine statusLine;
+
+ public HttpErrorException(StatusLine statusLine) {
+ this.statusLine = statusLine;
+ }
+
+ public StatusLine getStatusLine() {
+ return statusLine;
+ }
+
+}
diff --git a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
index 99f8f3c..ee96b04 100755
--- a/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserRepresentation.java
@@ -1,14 +1,12 @@
package org.keycloak.representations.idm;
-import java.util.ArrayList;
+import org.codehaus.jackson.annotate.JsonIgnore;
+
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
-import org.codehaus.jackson.annotate.JsonIgnore;
-import org.keycloak.util.MultivaluedHashMap;
-
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
dependencies/server-min/pom.xml 4(+0 -4)
diff --git a/dependencies/server-min/pom.xml b/dependencies/server-min/pom.xml
index 7e1565e..71807a5 100755
--- a/dependencies/server-min/pom.xml
+++ b/dependencies/server-min/pom.xml
@@ -33,10 +33,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
diff --git a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
index 4ecf698..3fb12cc 100755
--- a/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
+++ b/distribution/adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -14,6 +14,7 @@
<module name="org.apache.httpcomponents"/>
<module name="org.bouncycastle" />
<module name="org.keycloak.keycloak-common"/>
+ <module name="org.picketbox"/>
</dependencies>
</module>
distribution/docs-dist/assembly.xml 8(+6 -2)
diff --git a/distribution/docs-dist/assembly.xml b/distribution/docs-dist/assembly.xml
index c1a1bd9..00862c2 100755
--- a/distribution/docs-dist/assembly.xml
+++ b/distribution/docs-dist/assembly.xml
@@ -17,8 +17,12 @@
<outputDirectory>rest-api</outputDirectory>
</fileSet>
<fileSet>
- <directory>../../docbook/target/docbook/publish/en-US</directory>
- <outputDirectory>userguide</outputDirectory>
+ <directory>../../docbook/auth-server-docs/target/docbook/publish/en-US</directory>
+ <outputDirectory>userguide/keycloak-server</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>../../docbook/saml-adapter-docs/target/docbook/publish/en-US</directory>
+ <outputDirectory>userguide/saml-client-adapter</outputDirectory>
</fileSet>
</fileSets>
diff --git a/distribution/docs-dist/src/index.html b/distribution/docs-dist/src/index.html
index 891f778..f196cd5 100755
--- a/distribution/docs-dist/src/index.html
+++ b/distribution/docs-dist/src/index.html
@@ -1,8 +1,11 @@
<h1>Keyloak Documentation</h1>
<ul>
- <li><a href="userguide/html/index.html">Userguide HTML</a></li>
- <li><a href="userguide/html_single/index.html">Userguide HTML Single Page</a></li>
- <li><a href="userguide/pdf/keycloak-reference-guide-en-US.pdf">Userguide PDF</a></li>
+ <li><a href="userguide/keycloak-server/html/index.html">Server and Keycloak Adapter Userguide HTML</a></li>
+ <li><a href="userguide/keycloak-server/html_single/index.html">Server and Keycloak Adapter Userguide HTML Single Page</a></li>
+ <li><a href="userguide/keycloak-server/pdf/keycloak-reference-guide-en-US.pdf">Server and Keycloak Adapter Userguide PDF</a></li>
+ <li><a href="userguide/saml-client-adapter/html/index.html">SAML Client Adapter Userguide HTML</a></li>
+ <li><a href="userguide/saml-client-adapter/html_single/index.html">>SAML Client Adapter Userguide HTML Single Page</a></li>
+ <li><a href="userguide/saml-client-adapter/pdf/keycloak-reference-guide-en-US.pdf">SAML Client Adapter Userguide PDF</a></li>
<li><a href="rest-api/overview-index.html">Admin REST API</a></li>
<li><a href="javadocs/index.html">Javadocs</a></li>
</ul>
\ No newline at end of file
distribution/downloads/pom.xml 101(+100 -1)
diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml
index f15b757..041f1c4 100755
--- a/distribution/downloads/pom.xml
+++ b/distribution/downloads/pom.xml
@@ -248,7 +248,106 @@
<type>tar.gz</type>
</artifactItem>
</artifactItems>
- <outputDirectory>target/${project.version}/adapters</outputDirectory>
+ <outputDirectory>target/${project.version}/adapters/keycloak-oidc</outputDirectory>
+ </configuration>
+ </execution>
+ <execution>
+ <id>saml-adapter-downloads</id>
+ <phase>install</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-eap6-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-eap6-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty81-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty81-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty92-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty92-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat6-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat6-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat7-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat7-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat8-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat8-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
+ <type>zip</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
+ <type>tar.gz</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>target/${project.version}/adapters/saml</outputDirectory>
</configuration>
</execution>
</executions>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
index 936b532..32cd209 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
@@ -11,7 +11,6 @@
<module name="org.keycloak.keycloak-connections-file" services="import"/>
<module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/>
- <module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
<module name="org.keycloak.keycloak-email-api" services="import"/>
<module name="org.keycloak.keycloak-email-freemarker" services="import"/>
<module name="org.keycloak.keycloak-events-api" services="import"/>
diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml
index ee59819..122af6d 100755
--- a/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/feature-packs/server-feature-pack/src/main/resources/modules/system/layers/base/org/keycloak/keycloak-services/main/module.xml
@@ -21,7 +21,6 @@
<module name="org.keycloak.keycloak-connections-file" services="import"/>
<module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/>
- <module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
<module name="org.keycloak.keycloak-email-api" services="import"/>
<module name="org.keycloak.keycloak-email-freemarker" services="import"/>
<module name="org.keycloak.keycloak-events-api" services="import"/>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
new file mode 100755
index 0000000..1acb6aa
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
@@ -0,0 +1,36 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${project.build.directory}/unpacked</directory>
+ <includes>
+ <include>org/keycloak/keycloak-common/**</include>
+ <include>org/keycloak/keycloak-saml-core/**</include>
+ <include>org/keycloak/keycloak-saml-adapter-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
+ <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
+ <include>org/bouncycastle/**</include>
+ <include>org/keycloak/keycloak-saml-as7-adapter/**</include>
+ <include>org/keycloak/keycloak-saml-as7-subsystem/**</include>
+ <include>org/keycloak/keycloak-saml-adapter-subsystem/**</include>
+ </includes>
+ <excludes>
+ <exclude>**/*.war</exclude>
+ </excludes>
+ <outputDirectory>modules</outputDirectory>
+ </fileSet>
+ </fileSets>
+ <files>
+ <file>
+ <source>../../shared-cli/adapter-install.cli</source>
+ <outputDirectory>bin</outputDirectory>
+ </file>
+ </files>
+</assembly>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml
new file mode 100755
index 0000000..8cee17b
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml
@@ -0,0 +1,79 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-as7-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML AS7 Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-modules</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-modules</artifactId>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml
new file mode 100755
index 0000000..4a34435
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml
@@ -0,0 +1,22 @@
+<assembly>
+ <id>dist</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>../../</directory>
+ <includes>
+ <include>License.html</include>
+ </includes>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>${project.build.directory}/modules</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+</assembly>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml
new file mode 100755
index 0000000..f62783b
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml
@@ -0,0 +1,88 @@
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2012, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+
+<project name="module-repository" basedir="." default="all">
+
+ <import file="lib.xml"/>
+
+ <property name="output.dir" value="target"/>
+
+ <target name="all">
+ <antcall target="modules">
+ <param name="mavenized.modules" value="false"/>
+ <param name="output.dir" value="target"/>
+ </antcall>
+ </target>
+
+
+ <target name="modules">
+
+ <!-- server min dependencies -->
+
+ <module-def name="org.keycloak.keycloak-common">
+ <maven-resource group="org.keycloak" artifact="keycloak-common"/>
+ </module-def>
+ <module-def name="org.bouncycastle">
+ <maven-resource group="org.bouncycastle" artifact="bcprov-jdk15on"/>
+ <maven-resource group="org.bouncycastle" artifact="bcpkix-jdk15on"/>
+ </module-def>
+
+ <!-- subsystems -->
+
+ <module-def name="org.keycloak.keycloak-adapter-spi">
+ <maven-resource group="org.keycloak" artifact="keycloak-adapter-spi"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-tomcat-adapter-spi"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-as7-adapter-spi"/>
+ </module-def>
+
+ <module-def name="org.keycloak.keycloak-saml-core">
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-core"/>
+ </module-def>
+
+ <module-def name="org.keycloak.keycloak-saml-adapter-core">
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-adapter-core"/>
+ </module-def>
+
+ <module-def name="org.keycloak.keycloak-jboss-adapter-core">
+ <maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
+ </module-def>
+
+ <module-def name="org.keycloak.keycloak-saml-as7-adapter">
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-as7-adapter"/>
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-tomcat-adapter-core"/>
+ </module-def>
+
+ <module-def name="org.keycloak.keycloak-saml-as7-subsystem">
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-as7-subsystem"/>
+ </module-def>
+
+ </target>
+
+ <target name="clean-target">
+ <delete dir="${output.dir}"/>
+ </target>
+
+ <target name="clean" depends="clean-target">
+ <delete file="maven-ant-tasks.jar"/>
+ </target>
+
+</project>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml
new file mode 100755
index 0000000..3d9438a
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml
@@ -0,0 +1,282 @@
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2010, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+
+<project name="module-repository-lib">
+
+ <property name="src.dir" value="src"/>
+ <property name="module.repo.src.dir" value="${src.dir}/main/resources/modules"/>
+ <property name="module.xml" value="module.xml"/>
+
+ <taskdef resource="net/sf/antcontrib/antlib.xml"/>
+ <taskdef name="jandex" classname="org.jboss.jandex.JandexAntTask" />
+
+ <macrodef name="module-def">
+ <attribute name="name"/>
+ <attribute name="slot" default="main"/>
+ <element name="resources" implicit="yes" optional="yes"/>
+
+ <sequential>
+ <echo message="Initializing module -> @{name}"/>
+ <property name="module.repo.output.dir" value="${output.dir}/modules"/>
+ <!-- Figure out the correct module path -->
+ <define-module-dir name="@{name}" slot="@{slot}"/>
+
+ <!-- Make the module output director -->
+ <mkdir dir="${module.repo.output.dir}/${current.module.path}"/>
+
+ <!-- Copy the module.xml and other stuff to the output director -->
+ <copy todir="${module.repo.output.dir}/${current.module.path}" overwrite="true">
+ <fileset dir="${module.repo.src.dir}/${current.module.path}">
+ <include name="**"/>
+ </fileset>
+ </copy>
+
+ <!-- Process the resource -->
+ <resources/>
+
+ <!-- Add keycloak version property to module xml -->
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}"
+ token="$${project.version}"
+ value="${project.version}"/>
+
+ <!-- Some final cleanup -->
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacetoken>
+ <![CDATA[
+ <!-- Insert resources here -->]]></replacetoken>
+ <replacevalue>
+ </replacevalue>
+ </replace>
+
+ </sequential>
+ </macrodef>
+
+ <macrodef name="bundle-def">
+ <attribute name="name"/>
+ <attribute name="slot" default="main"/>
+ <element name="resources" implicit="yes" optional="yes"/>
+
+ <sequential>
+ <echo message="Initializing bundle -> @{name}"/>
+ <property name="bundle.repo.output.dir" value="${output.dir}/bundles/system/layers/base"/>
+ <!-- Figure out the correct bundle path -->
+ <define-bundle-dir name="@{name}" slot="@{slot}" />
+
+ <!-- Make the bundle output director -->
+ <mkdir dir="${bundle.repo.output.dir}/${current.bundle.path}"/>
+
+ <!-- Process the resource -->
+ <resources/>
+
+ </sequential>
+ </macrodef>
+
+ <macrodef name="maven-bundle" >
+ <attribute name="group"/>
+ <attribute name="artifact"/>
+
+ <sequential>
+ <!-- Copy the jar to the bundle dir -->
+ <property name="bundle.repo.output.dir" value="${output.dir}/bundles/system/layers/base"/>
+ <copy todir="${bundle.repo.output.dir}/${current.bundle.path}" failonerror="true">
+ <fileset file="${@{group}:@{artifact}:jar}"/>
+ <mapper type="flatten" />
+ </copy>
+ </sequential>
+ </macrodef>
+
+ <scriptdef name="define-module-dir" language="javascript" manager="bsf">
+ <attribute name="name"/>
+ <attribute name="slot"/>
+ <![CDATA[
+ name = attributes.get("name");
+ name = name.replace(".", "/");
+ project.setProperty("current.module.path", name + "/" + attributes.get("slot"));
+ ]]>
+ </scriptdef>
+
+ <scriptdef name="define-bundle-dir" language="javascript" manager="bsf">
+ <attribute name="name"/>
+ <attribute name="slot"/>
+ <![CDATA[
+ name = attributes.get("name");
+ name = name.replace(".", "/");
+ project.setProperty("current.bundle.path", name + "/" + attributes.get("slot"));
+ ]]>
+ </scriptdef>
+
+ <!--
+ Get the version from the parent directory of the jar. If the parent directory is 'target' this
+ means that the jar is contained in AS build so extract the version from the file name
+ -->
+ <scriptdef name="define-maven-artifact" language="javascript" manager="bsf">
+ <attribute name="group"/>
+ <attribute name="artifact"/>
+ <attribute name="classifier"/>
+ <attribute name="element"/>
+ <attribute name="path"/>
+ <![CDATA[
+ importClass(Packages.java.io.File);
+ group = attributes.get("group");
+ artifact = attributes.get("artifact");
+ classifier = attributes.get("classifier");
+ element = attributes.get("element");
+ path = attributes.get("path");
+ if(path.indexOf('${') != -1) {
+ throw "Module resource root not found, make sure it is listed in build/pom.xml" + path;
+ }
+ fp = new File(path);
+ version = fp.getParentFile().getName();
+ if (version.equals("target")) {
+ version = fp.getName();
+ version = version.substring(artifact.length() + 1);
+ suffix = ".jar";
+ if (classifier) {
+ suffix = "-" + classifier + suffix;
+ }
+ version = version.replace(suffix, "");
+ }
+
+ root = "<" + element + " name=\"" + group + ":" + artifact + ":" + version;
+ if (classifier) {
+ root = root + ":" + classifier;
+ }
+ root = root + "\"/>";
+ project.setProperty("current.maven.root", root);
+ ]]>
+ </scriptdef>
+
+ <macrodef name="maven-resource" >
+ <attribute name="group"/>
+ <attribute name="artifact"/>
+ <attribute name="jandex" default="false" />
+
+ <sequential>
+ <if>
+ <equals arg1="${mavenized.modules}" arg2="true"/>
+ <then>
+ <define-maven-artifact group="@{group}" artifact="@{artifact}" element="artifact" path="${@{group}:@{artifact}:jar}"/>
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacefilter token="<!-- Insert resources here -->" value="${current.maven.root} <!-- Insert resources here -->"/>
+ </replace>
+ </then>
+
+ <else>
+ <!-- Copy the jar to the module dir -->
+ <copy todir="${module.repo.output.dir}/${current.module.path}" failonerror="true">
+ <fileset file="${@{group}:@{artifact}:jar}"/>
+ <mapper type="flatten" />
+ </copy>
+
+ <basename file="${@{group}:@{artifact}:jar}" property="resourcename.@{group}.@{artifact}"/>
+ <!-- Generate the Jandex Index -->
+ <jandex run="@{jandex}" newJar="true" >
+ <fileset dir="${module.repo.output.dir}/${current.module.path}" />
+ </jandex>
+ <!-- Update the resource entry in module.xml -->
+ <define-resource-root path="${resourcename.@{group}.@{artifact}}" jandex="@{jandex}"/>
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacefilter token="<!-- Insert resources here -->" value="${current.resource.root} <!-- Insert resources here -->"/>
+ </replace>
+ </else>
+ </if>
+ </sequential>
+ </macrodef>
+
+
+
+ <macrodef name="maven-resource-with-classifier" >
+ <attribute name="group"/>
+ <attribute name="artifact"/>
+ <attribute name="classifier"/>
+ <attribute name="jandex" default="false" />
+
+ <sequential>
+ <if>
+ <equals arg1="${mavenized.modules}" arg2="true"/>
+ <then>
+ <define-maven-artifact group="@{group}" artifact="@{artifact}" element="artifact" classifier="@{classifier}" path="${@{group}:@{artifact}:jar:@{classifier}}"/>
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacefilter token="<!-- Insert resources here -->" value="${current.maven.root} <!-- Insert resources here -->"/>
+ </replace>
+ </then>
+ <else>
+ <!-- Copy the jar to the module dir -->
+ <copy todir="${module.repo.output.dir}/${current.module.path}" failonerror="true">
+ <fileset file="${@{group}:@{artifact}:jar:@{classifier}}"/>
+ <!-- http://jira.codehaus.org/browse/MANTRUN-159 -->
+ <mapper type="flatten" />
+ </copy>
+
+ <basename file="${@{group}:@{artifact}:jar:@{classifier}}" property="resourcename.@{group}.@{artifact}.@{classifier}"/>
+
+ <!-- Update the resource entry in module.xml -->
+ <define-resource-root path="${resourcename.@{group}.@{artifact}.@{classifier}}"/>
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacefilter token="<!-- Insert resources here -->" value="${current.resource.root} <!-- Insert resources here -->"/>
+ </replace>
+ </else>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <macrodef name="extract-native-jar" >
+ <attribute name="group"/>
+ <attribute name="artifact"/>
+ <sequential>
+ <if>
+ <equals arg1="${mavenized.modules}" arg2="true"/>
+ <then>
+ <define-maven-artifact group="@{group}" artifact="@{artifact}" element="native-artifact" path="${@{group}:@{artifact}:jar}"/>
+ <replace file="${module.repo.output.dir}/${current.module.path}/${module.xml}">
+ <replacefilter token="<!-- Insert resources here -->" value="${current.maven.root} <!-- Insert resources here -->"/>
+ </replace>
+ </then>
+
+ <else>
+ <unzip src="${@{group}:@{artifact}:jar}" dest="${module.repo.output.dir}/${current.module.path}">
+ <patternset>
+ <include name="lib/**"/>
+ </patternset>
+ </unzip>
+ </else>
+ </if>
+ </sequential>
+ </macrodef>
+
+ <scriptdef name="define-resource-root" language="javascript" manager="bsf">
+ <attribute name="path"/>
+ <attribute name="jandex"/>
+ <![CDATA[
+ path = attributes.get("path");
+ root = "<resource-root path=\"" + path + "\"/>";
+ if(path.indexOf('${') != -1) {
+ throw "Module resource root not found, make sure it is listed in build/pom.xml" + path;
+ }
+ if(attributes.get("jandex") == "true" ) {
+ root = root + "\n\t<resource-root path=\"" + path.replace(".jar","-jandex.jar") + "\"/>";
+ }
+ project.setProperty("current.resource.root", root);
+ ]]>
+ </scriptdef>
+
+</project>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml
new file mode 100755
index 0000000..0f1b317
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-as7-modules</artifactId>
+
+ <name>Keycloak SAML AS7 / JBoss EAP 6 Modules</name>
+ <packaging>pom</packaging>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-jboss-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-subsystem</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-tomcat-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-jboss-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <inherited>false</inherited>
+ <executions>
+ <execution>
+ <id>build-dist</id>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <phase>compile</phase>
+ <configuration>
+ <target>
+ <ant antfile="build.xml" inheritRefs="true">
+ <target name="all"/>
+ </ant>
+ </target>
+ </configuration>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss</groupId>
+ <artifactId>jandex</artifactId>
+ <version>1.0.3.Final</version>
+ </dependency>
+ <dependency>
+ <groupId>ant-contrib</groupId>
+ <artifactId>ant-contrib</artifactId>
+ <version>1.0b3</version>
+ <exclusions>
+ <exclusion>
+ <groupId>ant</groupId>
+ <artifactId>ant</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.ant</groupId>
+ <artifactId>ant-apache-bsf</artifactId>
+ <version>1.9.3</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.bsf</groupId>
+ <artifactId>bsf-api</artifactId>
+ <version>3.1</version>
+ </dependency>
+ <dependency>
+ <groupId>rhino</groupId>
+ <artifactId>js</artifactId>
+ <version>1.7R2</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <!-- here the phase you need -->
+ <phase>validate</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${project.build.directory}/modules/org/keycloak/keycloak-saml-adapter-subsystem</outputDirectory>
+ <resources>
+ <resource>
+ <directory>src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
new file mode 100755
index 0000000..b7bcee4
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-common">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="org.bouncycastle" />
+ <module name="javax.api"/>
+ <module name="javax.activation.api"/>
+ <module name="sun.jdk" optional="true" />
+ <module name="sun.jdk.jgss" optional="true" />
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
new file mode 100755
index 0000000..cf89825
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-jboss-adapter-core">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
+ <module name="org.keycloak.keycloak-common"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml
new file mode 100755
index 0000000..f9399db
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-adapter-core">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
+ <module name="org.keycloak.keycloak-saml-core"/>
+ <module name="org.keycloak.keycloak-common"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml
new file mode 100755
index 0000000..edbea10
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2014, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-adapter-subsystem">
+
+ <resources>
+ <resource-root path="."/>
+ <!-- Insert resources here -->
+ </resources>
+
+ <dependencies>
+ <module name="org.keycloak.keycloak-saml-as7-subsystem" export="true" services="export"/>
+ </dependencies>
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml
new file mode 100755
index 0000000..ebf3ce3
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-as7-adapter">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.bouncycastle" />
+ <module name="javax.servlet.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.as.security"/>
+ <module name="org.jboss.as.web"/>
+ <module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-saml-as7-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
+ <module name="org.keycloak.keycloak-saml-core"/>
+ <module name="org.keycloak.keycloak-saml-adapter-core"/>
+ <module name="org.keycloak.keycloak-common"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml
new file mode 100755
index 0000000..8a14597
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2014, Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags. See the copyright.txt file in the
+ ~ distribution for a full listing of individual contributors.
+ ~
+ ~ This is free software; you can redistribute it and/or modify it
+ ~ under the terms of the GNU Lesser General Public License as
+ ~ published by the Free Software Foundation; either version 2.1 of
+ ~ the License, or (at your option) any later version.
+ ~
+ ~ This software is distributed in the hope that it will be useful,
+ ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ ~ Lesser General Public License for more details.
+ ~
+ ~ You should have received a copy of the GNU Lesser General Public
+ ~ License along with this software; if not, write to the Free
+ ~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-as7-subsystem">
+
+ <resources>
+ <resource-root path="."/>
+ <!-- Insert resources here -->
+ </resources>
+
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.keycloak.keycloak-saml-as7-adapter"/>
+ <module name="org.jboss.staxmapper"/>
+ <module name="org.jboss.as.controller"/>
+ <module name="org.jboss.as.server"/>
+ <module name="org.jboss.as.web"/>
+ <module name="org.jboss.modules"/>
+ <module name="org.jboss.msc"/>
+ <module name="org.jboss.logging"/>
+ <module name="org.jboss.vfs"/>
+ <module name="org.jboss.metadata"/>
+ </dependencies>
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml
new file mode 100755
index 0000000..6a7cd65
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-core">
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="org.jboss.logging"/>
+ <module name="org.keycloak.keycloak-common"/>
+ <module name="org.apache.santuario.xmlsec">
+ <imports>
+ <exclude path="javax/*"/>
+ </imports>
+ </module>
+ <module name="javax.api"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
new file mode 100755
index 0000000..f844a41
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
@@ -0,0 +1,36 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${project.build.directory}/unpacked</directory>
+ <includes>
+ <include>org/keycloak/keycloak-common/**</include>
+ <include>org/keycloak/keycloak-saml-core/**</include>
+ <include>org/keycloak/keycloak-saml-adapter-core/**</include>
+ <include>org/keycloak/keycloak-adapter-spi/**</include>
+ <include>org/keycloak/keycloak-jboss-adapter-core/**</include>
+ <include>org/bouncycastle/**</include>
+ <include>org/keycloak/keycloak-saml-as7-adapter/**</include>
+ <include>org/keycloak/keycloak-saml-as7-subsystem/**</include>
+ <include>org/keycloak/keycloak-saml-adapter-subsystem/**</include>
+ </includes>
+ <excludes>
+ <exclude>**/*.war</exclude>
+ </excludes>
+ <outputDirectory>modules/system/layers/base</outputDirectory>
+ </fileSet>
+ </fileSets>
+ <files>
+ <file>
+ <source>../../shared-cli/adapter-install.cli</source>
+ <outputDirectory>bin</outputDirectory>
+ </file>
+ </files>
+</assembly>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml
new file mode 100755
index 0000000..3e00456
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml
@@ -0,0 +1,79 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-eap6-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML JBoss EAP 6 Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-modules</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>prepare-package</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-modules</artifactId>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/as7-eap6-adapter/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/pom.xml
new file mode 100755
index 0000000..b608eab
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/pom.xml
@@ -0,0 +1,21 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <name>Keycloak SAML AS7 / JBoss EAP 6 Adapter Distros</name>
+ <description/>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-as7-eap6-adapter-dist-pom</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>as7-modules</module>
+ <module>as7-adapter-zip</module>
+ <module>eap6-adapter-zip</module>
+ </modules>
+</project>
diff --git a/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml
new file mode 100755
index 0000000..d28d51a
--- /dev/null
+++ b/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml
@@ -0,0 +1,26 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-jetty81-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.eclipse.jetty:jetty-server</exclude>
+ <exclude>org.eclipse.jetty:jetty-util</exclude>
+ <exclude>org.eclipse.jetty:jetty-security</exclude>
+ </excludes>
+ <outputDirectory>lib/keycloak</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/jetty81-adapter-zip/pom.xml b/distribution/saml-adapters/jetty81-adapter-zip/pom.xml
new file mode 100755
index 0000000..412a5c8
--- /dev/null
+++ b/distribution/saml-adapters/jetty81-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-jetty81-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Jetty 8.1.x Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty81-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml
new file mode 100755
index 0000000..13eb8e0
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml
@@ -0,0 +1,39 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory></directory>
+ <includes>
+ <include>keycloak.mod</include>
+ </includes>
+ <outputDirectory>modules</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>${project.build.directory}/modules</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-jetty91-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.eclipse.jetty:jetty-server</exclude>
+ <exclude>org.eclipse.jetty:jetty-util</exclude>
+ <exclude>org.eclipse.jetty:jetty-security</exclude>
+ </excludes>
+ <outputDirectory>lib/keycloak</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod b/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod
new file mode 100755
index 0000000..10550d8
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod
@@ -0,0 +1,11 @@
+#
+# Keycloak Jetty Adapter
+#
+
+[depend]
+server
+security
+
+[lib]
+lib/keycloak/*.jar
+
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/pom.xml b/distribution/saml-adapters/jetty91-adapter-zip/pom.xml
new file mode 100755
index 0000000..2c15be4
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-jetty91-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Jetty 9.1.x Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty91-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml
new file mode 100755
index 0000000..08a4cba
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml
@@ -0,0 +1,39 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory></directory>
+ <includes>
+ <include>keycloak.mod</include>
+ </includes>
+ <outputDirectory>modules</outputDirectory>
+ </fileSet>
+ <fileSet>
+ <directory>${project.build.directory}/modules</directory>
+ <outputDirectory></outputDirectory>
+ </fileSet>
+ </fileSets>
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-jetty92-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.eclipse.jetty:jetty-server</exclude>
+ <exclude>org.eclipse.jetty:jetty-util</exclude>
+ <exclude>org.eclipse.jetty:jetty-security</exclude>
+ </excludes>
+ <outputDirectory>lib/keycloak</outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod b/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod
new file mode 100755
index 0000000..10550d8
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod
@@ -0,0 +1,11 @@
+#
+# Keycloak Jetty Adapter
+#
+
+[depend]
+server
+security
+
+[lib]
+lib/keycloak/*.jar
+
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/pom.xml b/distribution/saml-adapters/jetty92-adapter-zip/pom.xml
new file mode 100755
index 0000000..62881cc
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-jetty92-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Jetty 9.2.x Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty92-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
distribution/saml-adapters/pom.xml 8(+8 -0)
diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml
index 8e08bdf..1a1c4b1 100755
--- a/distribution/saml-adapters/pom.xml
+++ b/distribution/saml-adapters/pom.xml
@@ -16,5 +16,13 @@
<modules>
<module>wf9-adapter</module>
+ <module>tomcat6-adapter-zip</module>
+ <module>tomcat7-adapter-zip</module>
+ <module>tomcat8-adapter-zip</module>
+ <module>jetty81-adapter-zip</module>
+ <!-- jetty 9.1 doesn't work right now
+ <module>jetty91-adapter-zip</module> -->
+ <module>jetty92-adapter-zip</module>
+ <module>as7-eap6-adapter</module>
</modules>
</project>
diff --git a/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml
new file mode 100755
index 0000000..ae01a39
--- /dev/null
+++ b/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml
@@ -0,0 +1,24 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-tomcat6-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.apache.tomcat:catalina</exclude>
+ </excludes>
+ <outputDirectory></outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml
new file mode 100755
index 0000000..89609d4
--- /dev/null
+++ b/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-tomcat6-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Tomcat 6 Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat6-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml
new file mode 100755
index 0000000..6c205d1
--- /dev/null
+++ b/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml
@@ -0,0 +1,25 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-tomcat7-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.apache.tomcat:tomcat-servlet-api</exclude>
+ <exclude>org.apache.tomcat:tomcat-catalina</exclude>
+ </excludes>
+ <outputDirectory></outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml
new file mode 100755
index 0000000..4486d74
--- /dev/null
+++ b/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-tomcat7-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Tomcat 7 Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat7-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml
new file mode 100755
index 0000000..76665bc
--- /dev/null
+++ b/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml
@@ -0,0 +1,25 @@
+<assembly>
+ <id>war-dist</id>
+
+ <formats>
+ <format>zip</format>
+ <format>tar.gz</format>
+ </formats>
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <dependencySets>
+ <dependencySet>
+ <unpack>false</unpack>
+ <useTransitiveDependencies>true</useTransitiveDependencies>
+ <useTransitiveFiltering>true</useTransitiveFiltering>
+ <includes>
+ <include>org.keycloak:keycloak-saml-tomcat8-adapter</include>
+ </includes>
+ <excludes>
+ <exclude>org.apache.tomcat:tomcat-servlet-api</exclude>
+ <exclude>org.apache.tomcat:tomcat-catalina</exclude>
+ </excludes>
+ <outputDirectory></outputDirectory>
+ </dependencySet>
+ </dependencySets>
+</assembly>
diff --git a/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml
new file mode 100755
index 0000000..13741dd
--- /dev/null
+++ b/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-tomcat8-adapter-dist</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak SAML Tomcat 8 Adapter Distro</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat8-adapter</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>assemble</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <outputDirectory>
+ target
+ </outputDirectory>
+ <workDirectory>
+ target/assembly/work
+ </workDirectory>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/distribution/server-overlay/eap6/eap6-server-modules/build.xml b/distribution/server-overlay/eap6/eap6-server-modules/build.xml
index d07ef72..3941fb9 100755
--- a/distribution/server-overlay/eap6/eap6-server-modules/build.xml
+++ b/distribution/server-overlay/eap6/eap6-server-modules/build.xml
@@ -63,10 +63,6 @@
<maven-resource group="org.keycloak" artifact="keycloak-broker-saml"/>
</module-def>
- <module-def name="org.keycloak.keycloak-core-jaxrs">
- <maven-resource group="org.keycloak" artifact="keycloak-core-jaxrs"/>
- </module-def>
-
<module-def name="org.keycloak.keycloak-services">
<maven-resource group="org.keycloak" artifact="keycloak-services"/>
</module-def>
diff --git a/distribution/server-overlay/eap6/eap6-server-modules/pom.xml b/distribution/server-overlay/eap6/eap6-server-modules/pom.xml
index d5e0366..f9d5fc6 100755
--- a/distribution/server-overlay/eap6/eap6-server-modules/pom.xml
+++ b/distribution/server-overlay/eap6/eap6-server-modules/pom.xml
@@ -32,10 +32,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-server-subsystem</artifactId>
</dependency>
<dependency>
diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
index 936b532..32cd209 100755
--- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
+++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-as7-server-subsystem/main/server-war/WEB-INF/jboss-deployment-structure.xml
@@ -11,7 +11,6 @@
<module name="org.keycloak.keycloak-connections-file" services="import"/>
<module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/>
- <module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
<module name="org.keycloak.keycloak-email-api" services="import"/>
<module name="org.keycloak.keycloak-email-freemarker" services="import"/>
<module name="org.keycloak.keycloak-events-api" services="import"/>
diff --git a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
index 807018d..75e1181 100755
--- a/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
+++ b/distribution/server-overlay/eap6/eap6-server-modules/src/main/resources/modules/org/keycloak/keycloak-services/main/module.xml
@@ -21,7 +21,6 @@
<module name="org.keycloak.keycloak-connections-file" services="import"/>
<module name="org.keycloak.keycloak-common" services="import"/>
<module name="org.keycloak.keycloak-core" services="import"/>
- <module name="org.keycloak.keycloak-core-jaxrs" services="import"/>
<module name="org.keycloak.keycloak-email-api" services="import"/>
<module name="org.keycloak.keycloak-email-freemarker" services="import"/>
<module name="org.keycloak.keycloak-events-api" services="import"/>
diff --git a/docbook/auth-server-docs/reference/en/en-US/master.xml b/docbook/auth-server-docs/reference/en/en-US/master.xml
index 7a798f8..2af744f 100755
--- a/docbook/auth-server-docs/reference/en/en-US/master.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/master.xml
@@ -47,6 +47,7 @@
<!ENTITY ProtocolMappers SYSTEM "modules/protocol-mappers.xml">
<!ENTITY Recaptcha SYSTEM "modules/recaptcha.xml">
<!ENTITY AuthSPI SYSTEM "modules/auth-spi.xml">
+ <!ENTITY FilterAdapter SYSTEM "modules/servlet-filter-adapter.xml">
]>
<book>
@@ -94,11 +95,18 @@ This one is short
types are supported and how to configure and install them so that you can use Keycloak to secure
your applications.
</para>
+ <para>
+ These client adapters use an extension of the OpenID Connect protocol (a derivate of OAuth 2.0).
+ This extension provides support for clustering, backchannel logout, and other non-standard adminstrative functions.
+ The Keycloak project also provides a separate, standalone, generic, SAML client adapter. But that is describe in a separate
+ document and has a different download.
+ </para>
&AdapterConfig;
&JBossAdapter;
&TomcatAdapter;
&Jetty9Adapter;
&Jetty8Adapter;
+ &FilterAdapter;
&FuseAdapter;
&JavascriptAdapter;
&SpringBootAdapter;
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/javascript-adapter.xml b/docbook/auth-server-docs/reference/en/en-US/modules/javascript-adapter.xml
index 2e9f941..33d62dc 100755
--- a/docbook/auth-server-docs/reference/en/en-US/modules/javascript-adapter.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/javascript-adapter.xml
@@ -209,6 +209,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
<listitem>redirectUri - specifies the uri to redirect to after login</listitem>
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)</listitem>
<listitem>loginHint - used to pre-fill the username/email field on the login form</listitem>
+ <listitem>action - if value is 'register' then user is redirected to registration page, otherwise to login page</listitem>
</itemizedlist>
</para>
</simplesect>
@@ -247,6 +248,20 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
</simplesect>
<simplesect>
+ <title>register(options)</title>
+
+ <para>Redirects to registration form. It's a shortcut for doing login with option action = 'register'</para>
+ <para>Options are same as login method but 'action' is overwritten to 'register'</para>
+ </simplesect>
+
+ <simplesect>
+ <title>createRegisterUrl(options)</title>
+
+ <para>Returns the url to registration page. It's a shortcut for doing createRegisterUrl with option action = 'register'</para>
+ <para>Options are same as createLoginUrl method but 'action' is overwritten to 'register'</para>
+ </simplesect>
+
+ <simplesect>
<title>accountManagement()</title>
<para>Redirects to account management</para>
diff --git a/docbook/auth-server-docs/reference/en/en-US/modules/servlet-filter-adapter.xml b/docbook/auth-server-docs/reference/en/en-US/modules/servlet-filter-adapter.xml
new file mode 100755
index 0000000..f785b07
--- /dev/null
+++ b/docbook/auth-server-docs/reference/en/en-US/modules/servlet-filter-adapter.xml
@@ -0,0 +1,51 @@
+<section>
+ <title>Java Servlet Filter Adapter</title>
+ <para>
+ If you want to use Keycloak with a Java servlet application that doesn't have an adapter for that servlet
+ platform, you can opt to use the servlet filter adapter that Keycloak has. This adapter works a little
+ differently than the other adapters. You do not define security constraints in web.xml. Instead you define
+ a filter mapping using the Keycloak servlet filter adapter to secure the url patterns you want to secure.
+ </para>
+ <warning>
+ <para>
+ Backchannel logout works a bit differently than the standard adapters. Instead of invalidating the http session
+ it instead marks the session id as logged out. There's just no way of arbitrarily invalidating an http session
+ based on a session id.
+ </para>
+ </warning>
+
+ <programlisting>
+ <![CDATA[
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>customer-portal</module-name>
+
+ <filter>
+ <filter-name>Keycloak Filter</filter-name>
+ <filter-class>org.keycloak.adapters.servlet.KeycloakOIDCFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>Keycloak Filter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+</web-app>
+]]>
+ </programlisting>
+ <para>
+ The Keycloak filter has the same configuration parameters available as the other adapters except you must define
+ them as filter init params instead of context params.
+ </para>
+ <para>
+ To use this filter, include this maven artifact in your WAR poms
+ </para>
+ <programlisting><![CDATA[
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-filter-adapter</artifactId>
+ <version>&project.version;</version>
+ </dependency>
+]]></programlisting>
+</section>
\ No newline at end of file
docbook/pom.xml 2(+1 -1)
diff --git a/docbook/pom.xml b/docbook/pom.xml
index 91d4a50..5b9c60b 100755
--- a/docbook/pom.xml
+++ b/docbook/pom.xml
@@ -10,7 +10,7 @@
<description/>
<modelVersion>4.0.0</modelVersion>
- <artifactId>keycloak-saml-pom</artifactId>
+ <artifactId>keycloak-docbook-parent</artifactId>
<packaging>pom</packaging>
<modules>
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/master.xml b/docbook/saml-adapter-docs/reference/en/en-US/master.xml
index 51040ac..5b798a1 100755
--- a/docbook/saml-adapter-docs/reference/en/en-US/master.xml
+++ b/docbook/saml-adapter-docs/reference/en/en-US/master.xml
@@ -1,13 +1,21 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
"http://www.docbook.org/xml/4.4/docbookx.dtd"
[
+ <!ENTITY Overview SYSTEM "modules/overview.xml">
+ <!ENTITY AdapterConfig SYSTEM "modules/adapter-config.xml">
+ <!ENTITY JBossAdapter SYSTEM "modules/jboss-adapter.xml">
+ <!ENTITY TomcatAdapter SYSTEM "modules/tomcat-adapter.xml">
+ <!ENTITY Jetty9Adapter SYSTEM "modules/jetty9-adapter.xml">
+ <!ENTITY Jetty8Adapter SYSTEM "modules/jetty8-adapter.xml">
+ <!ENTITY FilterAdapter SYSTEM "modules/servlet-filter-adapter.xml">
+ <!ENTITY Logout SYSTEM "modules/logout.xml">
]>
<book>
<bookinfo>
<title>Keycloak SAML Client Adapter Reference Guide</title>
- <subtitle>SAML 2.0 Client Adapters for Java Applications</subtitle>
+ <subtitle>SAML 2.0 Client Adapters</subtitle>
<releaseinfo>&project.version;</releaseinfo>
</bookinfo>
@@ -33,6 +41,15 @@ This one is short
</programlisting>
</para>
</preface>
+ &Overview;
+ &AdapterConfig;
+ &JBossAdapter;
+ &TomcatAdapter;
+ &Jetty9Adapter;
+ &Jetty8Adapter;
+ &FilterAdapter;
+ &Logout;
+
</book>
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
new file mode 100755
index 0000000..cce3faf
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
@@ -0,0 +1,475 @@
+<chapter id="adapter-config">
+ <title>General Adapter Config</title>
+ <para>
+ Each SAML adapter supported by Keycloak can be configured by a simple XML text file. This is what one might
+ look like:
+ </para>
+ <para>
+<programlisting><![CDATA[
+<keycloak-saml-adapter>
+ <SP entityID="http://localhost:8081/sales-post-sig/"
+ sslPolicy="EXTERNAL"
+ nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
+ logoutPage="/logout.jsp"
+ forceAuthentication="false">
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ <PrincipalNameMapping policy="FROM_NAME_ID"/>
+ <RoleMapping>
+ <Attribute name="Role"/>
+ </RoleMapping>
+ <IDP entityID="idp"
+ signaturesRequired="true">
+ <SingleSignOnService requestBinding="POST"
+ bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+
+ <SingleLogoutService
+ requestBinding="POST"
+ responseBinding="POST"
+ postBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ redirectBindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
+ />
+ <Keys>
+ <Key signing="true">
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <Certificate alias="demo"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+ </IDP>
+ </SP>
+</keycloak-saml-adapter>]]>
+
+</programlisting>
+ </para>
+ <para>
+ Some of these configuration switches may be adapter specific and some are common across all adapters.
+ For Java adapters you can use <literal>${...}</literal> enclosure as System property replacement.
+ For example <literal>${jboss.server.config.dir}</literal>.
+ </para>
+ <section>
+ <title>SP Element</title>
+ <para>
+ Here is the explanation of the SP element attributes
+ </para>
+ <programlisting><![CDATA[
+<SP entityID="sp"
+ sslPolicy="ssl"
+ nameIDPolicyFormat="format"
+ forceAuthentication="true">
+...
+</SP>]]></programlisting>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>entityID</term>
+ <listitem>
+ <para>
+ This is the identifier for this client. The IDP needs this value to determine
+ who the client is that is communicating with it.
+ <emphasis>REQUIRED.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>sslPolicy</term>
+ <listitem>
+ <para>
+ This is the SSL policy the adapter will enforce. Valid values are:
+ ALL, EXTERNAL, and NONE. For ALL, all requests must come in via HTTPS. For
+ EXTERNAL, only non-private IP addresses must come over the wire via HTTPS. For
+ NONE, no requests are required to come over via HTTPS. This is
+ <emphasis>OPTIONAL.</emphasis> and defaults to EXTERNAL.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>nameIDPolicyFormat</term>
+ <listitem>
+ <para>
+ SAML clients can request a specific NameID Subject format. Fill in this value
+ if you want a specific format. It must be a standard SAML format identifier, i.e.
+ <literal>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</literal>
+ <emphasis>OPTIONAL.</emphasis>. By default, no special format is requested.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>forceAuthentication</term>
+ <listitem>
+ <para>
+ SAML clients can request that a user is re-authenticated even if
+ they are already logged in at the IDP. Set this to true if you
+ want this.
+ <emphasis>OPTIONAL.</emphasis>. Set to <literal>false</literal> by default.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </section>
+ <section id="sp_keys">
+ <title>SP Keys and Key elements</title>
+ <para>
+ If the IDP requires that the SP sign all of its requests and/or if the IDP will
+ encrypt assertions, you must define the keys used to do this. For client signed
+ documents you must define both the private and public key or certificate that will
+ be used to sign documents. For encryption, you only have to define the private key
+ that will be used to decrypt.
+ </para>
+ <para>
+ There are two ways to describe your keys. Either they are stored within a Java KeyStore
+ or you can cut and paste the keys directly within <literal>keycloak-saml.xml</literal>
+ in the PEM format.
+ </para>
+ <programlisting><![CDATA[
+ <Keys>
+ <Key signing="true" >
+ <KeyStore resource="/WEB-INF/keystore.jks" password="store123">
+ <PrivateKey alias="http://localhost:8080/sales-post-sig/" password="test123"/>
+ <Certificate alias="http://localhost:8080/sales-post-sig/"/>
+ </KeyStore>
+ </Key>
+ </Keys>
+]]>
+ </programlisting>
+ <para>
+ The <literal>Key</literal> element has two optional attributes <literal>signing</literal>
+ and <literal>encryption</literal>. When set to true these tell the adapter what the
+ key will be used for. If both attributes are set to true, then the key will be used for both
+ signing documents and decrypting encrypted assertions. You must set at least one of these
+ attributes to true.
+ </para>
+ <section id="keystore">
+ <title>KeyStore element</title>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>file</term>
+ <listitem>
+ <para>
+ File path to the key store.
+ <emphasis>OPTIONAL.</emphasis> The file or resource attribute
+ must be set.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>resource</term>
+ <listitem>
+ <para>
+ WAR resource path to the KeyStore. This is a path used in method call to ServletContext.getResourceAsStream().
+ <emphasis>OPTIONAL.</emphasis> The file or resource attribute
+ must be set.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>password</term>
+ <listitem>
+ <para>
+ The password of the KeyStore
+ <emphasis>REQUIRED.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+
+ </para>
+ <para>
+ You can and must also specify references to your private keys and certificates within
+ the Java KeyStore. The <literal>PrivateKey</literal> and <literal>Certificate</literal>
+ elements do this. The <literal>alias</literal> attribute defines the alias within the
+ KeyStore for the key. For <literal>PrivateKey</literal>, a password is required to access this key
+ specify that value in the <literal>password</literal> attribute.
+ </para>
+ </section>
+ <section id="key_pems">
+ <title>Key PEMS</title>
+ <para>
+ Within the <literal>Key</literal> element you alternatively declare your keys and certificates
+ directly using the sub elements <literal>PrivateKeyPem</literal>, <literal>PublicKeyPem</literal>, and
+ <literal>CertificatePem</literal>. The values contained in these elements must conform to the
+ PEM key format. You usually use this option if you are generating keys using <literal>openssl</literal>
+ </para>
+ </section>
+ </section>
+ <section>
+ <title>SP PrincipalNameMapping element</title>
+ <para>
+ This element is optional. When creating a Java Principal object that you obtain from
+ methods like HttpServletRequest.getUserPrincipal(), you can define what name that is returned
+ by the Principal.getName() method. The <literal>policy</literal> attribute defines the
+ policy used to populate this value. The values are <literal>FROM_NAME_ID</literal>. This policy
+ just grabs whatever the SAML subject value is. The other is <literal>FROM_ATTRIBUTE</literal>. This will
+ pull the value of Principal.getName() from one of the attributes in the SAML assertion received from the server.
+ The default value is <literal>FROM_NAME_ID</literal>.
+ </para>
+ </section>
+ <section>
+ <title>RoleIdentifiers element</title>
+ <programlisting><![CDATA[
+<RoleIdentifiers>
+ <Attribute name="Role"/>
+ <Attribute name="member"/>
+ <Attribute name="memberOf"/>
+</RoleIdentifiers>
+]]></programlisting>
+ <para>
+ This element is optional. It defines which SAML attribute values in the assertion should be
+ mapped to a Java EE role. By default <literal>Role</literal> attribute values are converted
+ to Java EE roles. Some IDPs send roles via a <literal>member</literal> or <literal>memberOf</literal>
+ attribute assertion. You define one or more <literal>Attribute</literal> elements to specify
+ which SAML attributes must be converted into roles.
+ </para>
+ </section>
+ <section>
+ <title>IDP Element</title>
+ <para>
+ Everything in the IDP element describes the settings for the IDP the SP is communicating
+ with.
+ </para>
+ <programlisting>
+<![CDATA[
+<IDP entityID="idp"
+ signaturesRequired="true"
+ signatureAlgorithm="RSA_SHA1"
+ signatureCanonicalizationMethod="http://www.w3.org/2001/10/xml-exc-c14n#">
+...
+</IDP>]]>
+ </programlisting>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>entityID</term>
+ <listitem>
+ <para>
+ This is the issuer ID of the IDP.
+ <emphasis>REQUIRED.</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>signaturesRequired</term>
+ <listitem>
+ <para>
+ If set to true, the client adapter will sign every document
+ it sends to the IDP. Also, the client will expect that the IDP
+ will be signing an documents sent to it. This switch sets
+ the default for all request and response types, but you will see
+ later that you have some fine grain control over this.
+ <emphasis>OPTIONAL.</emphasis>
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>signatureAlgorithm</term>
+ <listitem>
+ <para>
+ This is the signature algorithm that the IDP expects signed documents
+ to use
+ <emphasis>OPTIONAL.</emphasis>. The default value is RSA_SHA256, but
+ you can also use RSA_SHA1, RSA_256, RSA_512, and DSA_SHA1.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>signatureCanonicalizationMethod</term>
+ <listitem>
+ <para>
+ This is the signature canonicalization method that the IDP expects signed documents
+ to use
+ <emphasis>OPTIONAL.</emphasis>. The default value is <literal>http://www.w3.org/2001/10/xml-exc-c14n#</literal>
+ and should be good for most IDPs.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </section>
+ <section>
+ <title>IDP SingleSignOnService sub element</title>
+ <para>
+ The <literal>SignleSignOnService</literal> sub element defines the
+ login SAML endpoint of the IDP.
+ </para>
+ <programlisting><![CDATA[
+<SingleSignOnService signRequest="true"
+ validateResponseSignature="true"
+ requestBinding="post"
+ bindingUrl="url"/>
+]]></programlisting>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>signRequest</term>
+ <listitem>
+ <para>
+ Should the client sign authn requests?
+ <emphasis>OPTIONAL.</emphasis>. Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>validateResponseSignature</term>
+ <listitem>
+ <para>
+ Should the client expect the IDP to sign the assertion response document
+ sent back from an auhtn request?
+ <emphasis>OPTIONAL.</emphasis> Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>requestBinding</term>
+ <listitem>
+ <para>
+ This is the SAML binding type used for communicating with the IDP
+ <emphasis>OPTIONAL.</emphasis>. The default value is POST, but
+ you can set it to REDIRECT as well.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>responseBinding</term>
+ <listitem>
+ <para>
+ SAML allows the client to request what binding type it wants authn responses
+ to use. The values of this can be POST or REDIRECT
+ <emphasis>OPTIONAL.</emphasis>. The default is that the client will not request
+ a specific binding type for responses.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>bindingUrl</term>
+ <listitem>
+ <para>
+ This is the URL for the ID login service that the client will send requests to.
+ <emphasis>REQUIRED.</emphasis>.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+ </section> <section>
+ <title>IDP SingleSignOnService sub element</title>
+ <para>
+ The <literal>SignleSignOnService</literal> sub element defines the
+ login SAML endpoint of the IDP.
+ </para>
+ <programlisting><![CDATA[
+<SingleLogoutService validateRequestSignature="true"
+ validateResponseSignature="true"
+ signRequest="true"
+ signResponse="true"
+ requestBinding="redirect"
+ responseBinding="post"
+ postBindingUrl="posturl"
+ redirectBindingUrl="redirecturl">
+]]></programlisting>
+ <para>
+ <variablelist>
+ <varlistentry>
+ <term>signRequest</term>
+ <listitem>
+ <para>
+ Should the client sign logout requests it makes to the IDP?
+ <emphasis>OPTIONAL.</emphasis>. Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>signResponse</term>
+ <listitem>
+ <para>
+ Should the client sign logout responses it sends to the IDP requests?
+ <emphasis>OPTIONAL.</emphasis>. Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>validateRequestSignature</term>
+ <listitem>
+ <para>
+ Should the client expect signed logout request documents from the IDP?
+ <emphasis>OPTIONAL.</emphasis> Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>validateResponseSignature</term>
+ <listitem>
+ <para>
+ Should the client expect signed logout response documents from the IDP?
+ <emphasis>OPTIONAL.</emphasis> Defaults to whatever the
+ IDP <literal>signaturesRequired</literal> element value is.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>requestBinding</term>
+ <listitem>
+ <para>
+ This is the SAML binding type used for communicating SAML requests to the IDP
+ <emphasis>OPTIONAL.</emphasis>. The default value is POST, but
+ you can set it to REDIRECT as well.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>responseBinding</term>
+ <listitem>
+ <para>
+ This is the SAML binding type used for communicating SAML responses to the IDP
+ The values of this can be POST or REDIRECT
+ <emphasis>OPTIONAL.</emphasis>. The default value is POST, but
+ you can set it to REDIRECT as well.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>postBindingUrl</term>
+ <listitem>
+ <para>
+ This is the URL for the IDP's logout service when using the POST binding.
+ <emphasis>REQUIRED</emphasis> if using the POST binding at all.
+ </para>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>redirectBindingUrl</term>
+ <listitem>
+ <para>
+ This is the URL for the IDP's logout service when using the REDIRECT binding.
+ <emphasis>REQUIRED</emphasis> if using the REDIRECT binding at all.
+ </para>
+ </listitem>
+ </varlistentry>
+ </variablelist>
+ </para>
+</section>
+ <section>
+ <title>IDP Keys subelement</title>
+ <para>
+ The Keys sub element of IDP is only used to define the certificate or
+ public key to use to verify documents signed by the IDP. It is defined
+ in the same way as the <link linkend="sp_keys">SP's Key's element</link>. But
+ again, you only have to define one certificate or public key reference.
+ </para>
+
+ </section>
+
+</chapter>
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml
new file mode 100755
index 0000000..fc29402
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml
@@ -0,0 +1,188 @@
+<chapter id="jboss-adapter">
+ <title>JBoss/Wildfly Adapter</title>
+ <para>
+ To be able to secure WAR apps deployed on JBoss EAP 6.x or Wildfly, you must install and
+ configure the Keycloak SAML Adapter Subsystem. You then provide a keycloak
+ config, <literal>/WEB-INF/keycloak-saml</literal> file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml.
+ Both methods are described in this section.
+ </para>
+ <section id="jboss-adapter-installation">
+ <title>Adapter Installation</title>
+ <para>
+ SAML Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+ </para>
+ <para>
+ Install on Wildfly 9:
+<programlisting>
+$ cd $WILDFLY_HOME
+$ unzip keycloak-saml-wf9-adapter-dist.zip
+</programlisting>
+ </para>
+ <para>
+ Install on JBoss EAP 6.x:
+<programlisting>
+$ cd $JBOSS_HOME
+$ unzip keycloak-saml-eap6-adapter-dist.zip
+</programlisting>
+ </para>
+ <para>
+ This zip file creates new JBoss Modules specific to the Wildfly Keycloak SAML Adapter within your Wildfly distro.
+ </para>
+ <para>
+ After adding the Keycloak modules, you must then enable the Keycloak SAML Subsystem within your app server's server configuration:
+ <literal>domain.xml</literal> or <literal>standalone.xml</literal>.
+ </para>
+ <para>
+ There is a CLI script that will help you modify your server configuration. Start the server and run the script
+ from the server's bin directory:
+<programlisting>
+$ cd $JBOSS_HOME/bin
+$ jboss-cli.sh -c --file=adapter-install.cli
+</programlisting>
+ The script will add the extension, subsystem, and optional security-domain as described below.
+ </para>
+ <para>
+<programlisting><![CDATA[
+<server xmlns="urn:jboss:domain:1.4">
+
+ <extensions>
+ <extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
+ ...
+ </extensions>
+
+ <profile>
+ <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6"/>
+ ...
+ </profile>
+]]>
+</programlisting>
+ </para>
+ <para>
+ The keycloak security domain should be used with EJBs and other components when you need the security context created
+ in the secured web tier to be propagated to the EJBs (other EE component) you are invoking. Otherwise
+ this configuration is optional.
+ </para>
+<programlisting><![CDATA[
+<server xmlns="urn:jboss:domain:1.4">
+ <subsystem xmlns="urn:jboss:domain:security:1.2">
+ <security-domains>
+...
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule"
+ flag="required"/>
+ </authentication>
+ </security-domain>
+ </security-domains>
+]]>
+</programlisting>
+ <para>
+ For example, if you have a JAX-RS service that is an EJB within your WEB-INF/classes directory, you'll want
+ to annotate it with the @SecurityDomain annotation as follows:
+ </para>
+<programlisting><![CDATA[
+import org.jboss.ejb3.annotation.SecurityDomain;
+import org.jboss.resteasy.annotations.cache.NoCache;
+
+import javax.annotation.security.RolesAllowed;
+import javax.ejb.EJB;
+import javax.ejb.Stateless;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.Produces;
+import java.util.ArrayList;
+import java.util.List;
+
+@Path("customers")
+@Stateless
+@SecurityDomain("keycloak")
+public class CustomerService {
+
+ @EJB
+ CustomerDB db;
+
+ @GET
+ @Produces("application/json")
+ @NoCache
+ @RolesAllowed("db_user")
+ public List<String> getCustomers() {
+ return db.getCustomers();
+ }
+}
+]]>
+</programlisting>
+ <para>
+ We hope to improve our integration in the future so that you don't have to specify the @SecurityDomain
+ annotation when you want to propagate a keycloak security context to the EJB tier.
+ </para>
+
+ </section>
+ <section>
+ <title>Required Per WAR Configuration</title>
+ <para>
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+ </para>
+ <para>
+ The first thing you must do is create
+ a <literal>keycloak-saml.xml</literal> adapter config file within the <literal>WEB-INF</literal> directory
+ of your WAR. The format of this config file is describe in the <link linkend='adapter-config'>general adapter configuration</link>
+ section.
+ </para>
+ <para>
+ Next you must set the <literal>auth-method</literal> to <literal>KEYCLOAK-SAML</literal> in <literal>web.xml</literal>. You also
+ have to use standard servlet security to specify role-base constraints on your URLs. Here's an example
+ pulled from one of the examples that comes distributed with Keycloak.
+ </para>
+ <para>
+<programlisting>
+<![CDATA[
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>customer-portal</module-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Admins</web-resource-name>
+ <url-pattern>/admin/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>admin</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Customers</web-resource-name>
+ <url-pattern>/customers/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK-SAML</auth-method>
+ <realm-name>this is ignored currently</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
+]]>
+</programlisting>
+ </para>
+ </section>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml
new file mode 100755
index 0000000..dbbef0c
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml
@@ -0,0 +1,49 @@
+<chapter id="jetty8-adapter">
+ <title>Jetty 8.1.x SAML Adapter</title>
+ <para>
+ Keycloak has a separate SAML adapter for Jetty 8.1.x that you will have to install into your Jetty
+ installation. You then have to provide some extra configuration in each WAR you deploy to
+ Jetty. Let's go over these steps.
+ </para>
+ <section id="jetty8-adapter-installation">
+ <title>Adapter Installation</title>
+ <para>
+ Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+ </para>
+ <para>
+ You must unzip the Jetty 8.1.x distro into Jetty 8.1.x's root directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work!
+ </para>
+ <para>
+<programlisting>
+$ cd $JETTY_HOME
+$ unzip keycloak-saml-jetty81-adapter-dist.zip
+</programlisting>
+ </para>
+ <para>
+ Next, you will have to enable the keycloak option. Edit start.ini and add keycloak to the options
+ </para>
+ <para>
+<programlisting>
+<![CDATA[
+#===========================================================
+# Start classpath OPTIONS.
+# These control what classes are on the classpath
+# for a full listing do
+# java -jar start.jar --list-options
+#-----------------------------------------------------------
+OPTIONS=Server,jsp,jmx,resources,websocket,ext,plus,annotations,keycloak
+]]>
+</programlisting>
+
+ </para>
+ </section>
+
+ <section>
+ <title>Required Per WAR Configuration</title>
+ <para>
+ Enabling Keycloak for your WARs is the same as the Jetty 9.x adapter. See <link linkend="jetty9_per_war">Required Per WAR Configuration</link>
+ </para>
+ </section>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml
new file mode 100755
index 0000000..44cd972
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml
@@ -0,0 +1,106 @@
+<chapter id="jetty9-adapter">
+ <title>Jetty 9.x SAML Adapters</title>
+ <para>
+ Keycloak has a separate SAML adapter for Jetty 9.x. You then have to provide some extra configuration in each WAR you deploy to
+ Jetty. Let's go over these steps.
+ </para>
+ <section id="jetty9-adapter-installation">
+ <title>Adapter Installation</title>
+ <para>
+ Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+ </para>
+ <para>
+ You must unzip the Jetty 9.x distro into Jetty 9.x's root directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work!
+ </para>
+ <para>
+<programlisting>
+$ cd $JETTY_HOME
+$ unzip keycloak-saml-jetty92-adapter-dist.zip
+</programlisting>
+ </para>
+ <para>
+ Next, you will have to enable the keycloak module for your jetty.base.
+ </para>
+ <para>
+<programlisting>
+$ cd your-base
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
+</programlisting>
+
+ </para>
+ </section>
+
+ <section id="jetty9_per_war">
+ <title>Required Per WAR Configuration</title>
+ <para>
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+ </para>
+ <para>
+ The first thing you must do is create a <literal>WEB-INF/jetty-web.xml</literal> file in your WAR package. This is
+ a Jetty specific config file and you must define a Keycloak specific authenticator within it.
+ </para>
+ <programlisting>
+<![CDATA[
+<?xml version="1.0"?>
+<!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://www.eclipse.org/jetty/configure_9_0.dtd">
+<Configure class="org.eclipse.jetty.webapp.WebAppContext">
+ <Get name="securityHandler">
+ <Set name="authenticator">
+ <New class="org.keycloak.adapters.saml.jetty.KeycloakSamlAuthenticator">
+ </New>
+ </Set>
+ </Get>
+</Configure>]]>
+ </programlisting>
+ <para>
+ Next you must create
+ a <literal>keycloak-saml.xml</literal> adapter config file within the <literal>WEB-INF</literal> directory
+ of your WAR. The format of this config file is describe in the <link linkend='adapter-config'>general adapter configuration</link>
+ section.
+ </para>
+ <para>
+ Finally you must specify both a <literal>login-config</literal> and use standard servlet security to specify
+ role-base constraints on your URLs. Here's an example:
+ </para>
+ <para>
+<programlisting>
+<![CDATA[
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>customer-portal</module-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Customers</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ <user-data-constraint>
+ <transport-guarantee>CONFIDENTIAL</transport-guarantee>
+ </user-data-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>this is ignored currently</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
+]]>
+</programlisting>
+ </para>
+ </section>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml
new file mode 100755
index 0000000..e258d60
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml
@@ -0,0 +1,10 @@
+<chapter>
+ <title>Logout</title>
+ <para>
+ There are multiple ways you can logout from a web application. For Java EE servlet containers, you can call
+ HttpServletRequest.logout().
+ For any other browser application, you can point the browser at any url of your web application that has
+ a security constraing and pass in a query parameter GLO, i.e. <literal>http://myapp?GLO=true</literal>.
+ This will log you out if you have an SSO session with your browser.
+ </para>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml
new file mode 100755
index 0000000..322aadb
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml
@@ -0,0 +1,56 @@
+<chapter id="multi_tenancy">
+ <title>Multi Tenancy</title>
+ <para>
+ Multi Tenancy, in our context, means that one single target application (WAR) can be secured by a single (or clustered) Keycloak server, authenticating
+ its users against different realms. In practice, this means that one application needs to use different <literal>keycloak.json</literal> files.
+ For this case, there are two possible solutions:
+ <itemizedlist>
+
+ <listitem>
+ The same WAR file deployed under two different names, each with its own Keycloak configuration (probably via the Keycloak Subsystem).
+ This scenario is suitable when the number of realms is known in advance or when there's a dynamic provision of application instances.
+ One example would be a service provider that dynamically creates servers/deployments for their clients, like a PaaS.
+ </listitem>
+
+ <listitem>
+ A WAR file deployed once (possibly in a cluster), that decides which realm to authenticate against based on the request parameters.
+ This scenario is suitable when there are an undefined number of realms. One example would be a SaaS provider that have only one deployment
+ (perhaps in a cluster) serving several companies, differentiating between clients based on the hostname
+ (<literal>client1.acme.com</literal>, <literal>client2.acme.com</literal>) or path (<literal>/app/client1/</literal>,
+ <literal>/app/client2/</literal>) or even via a special HTTP Header.
+ </listitem>
+
+ </itemizedlist>
+
+ This chapter of the reference guide focus on this second scenario.
+ </para>
+
+ <para>
+ Keycloak provides an extension point for applications that need to evaluate the realm on a request basis. During the authentication
+ and authorization phase of the incoming request, Keycloak queries the application via this extension point and expects the application
+ to return a complete representation of the realm. With this, Keycloak then proceeds the authentication and authorization process,
+ accepting or refusing the request based on the incoming credentials and on the returned realm.
+
+ For this scenario, an application needs to:
+
+ <itemizedlist>
+
+ <listitem>
+ Add a context parameter to the <literal>web.xml</literal>, named <literal>keycloak.config.resolver</literal>.
+ The value of this property should be the fully qualified name of the class extending
+ <literal>org.keycloak.adapters.KeycloakConfigResolver</literal>.
+ </listitem>
+
+ <listitem>
+ A concrete implementation of <literal>org.keycloak.adapters.KeycloakConfigResolver</literal>. Keycloak will call the
+ <literal>resolve(org.keycloak.adapters.HttpFacade.Request)</literal> method and expects a complete
+ <literal>org.keycloak.adapters.KeycloakDeployment</literal> in response. Note that Keycloak will call this for every request,
+ so, take the usual performance precautions.
+ </listitem>
+
+ </itemizedlist>
+ </para>
+ <para>
+ An implementation of this feature can be found in the examples.
+ </para>
+</chapter>
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/overview.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/overview.xml
new file mode 100755
index 0000000..1616bbb
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/overview.xml
@@ -0,0 +1,9 @@
+<chapter>
+ <title>Overview</title>
+ <para>
+ This document describes the Keycloak SAML client adapter and how it can be configured for a variety of platforms.
+ The Keycloak SAML client adapter is a standalone component that provides generic SAML 2.0 support for your web applications.
+ There are no Keycloak server extensions built into it. As long as the IDP you are talking to supports standard SAML, the
+ Keycloak SAML client adapter should be able to integrate with it.
+ </para>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/servlet-filter-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/servlet-filter-adapter.xml
new file mode 100755
index 0000000..dc6526a
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/servlet-filter-adapter.xml
@@ -0,0 +1,55 @@
+<chapter>
+ <title>Java Servlet Filter Adapter</title>
+ <para>
+ If you want to use SAML with a Java servlet application that doesn't have an adapter for that servlet
+ platform, you can opt to use the servlet filter adapter that Keycloak has. This adapter works a little
+ differently than the other adapters. You do not define security constraints in web.xml. Instead you define
+ a filter mapping using the Keycloak servlet filter adapter to secure the url patterns you want to secure.
+ </para>
+ <warning>
+ <para>
+ Backchannel logout works a bit differently than the standard adapters. Instead of invalidating the http session
+ it instead marks the session id as logged out. There's just no way of arbitrarily invalidating an http session
+ based on a session id.
+ </para>
+ </warning>
+ <warning>
+ <para>
+ Backchannel logout does not currently work when you have a clustered application that uses the SAML filter.
+ </para>
+ </warning>
+ <programlisting>
+ <![CDATA[
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>customer-portal</module-name>
+
+ <filter>
+ <filter-name>Keycloak Filter</filter-name>
+ <filter-class>org.keycloak.adapters.saml.servlet.SamlFilter</filter-class>
+ </filter>
+ <filter-mapping>
+ <filter-name>Keycloak Filter</filter-name>
+ <url-pattern>/*</url-pattern>
+ </filter-mapping>
+</web-app>
+]]>
+ </programlisting>
+ <para>
+ The Keycloak filter has the same configuration parameters available as the other adapters except you must define
+ them as filter init params instead of context params.
+ </para>
+ <para>
+ To use this filter, include this maven artifact in your WAR poms
+ </para>
+ <programlisting><![CDATA[
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+ <version>&project.version;</version>
+ </dependency>
+]]></programlisting>
+</chapter>
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
new file mode 100755
index 0000000..f6db1b0
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
@@ -0,0 +1,92 @@
+<chapter id="tomcat-adapter">
+ <title>Tomcat 6, 7 and 8 SAML dapters</title>
+ <para>
+ To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 SAML adapter
+ into your Tomcat installation. You then have to provide some extra configuration in each WAR you deploy to
+ Tomcat. Let's go over these steps.
+ </para>
+ <section id="tomcat-adapter-installation">
+ <title>Adapter Installation</title>
+ <para>
+ Adapters are no longer included with the appliance or war distribution. Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+ </para>
+ <para>
+ You must unzip the adapter distro into Tomcat's <literal>lib/</literal> directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work! The Keycloak SAML adapter is implemented as a Valve
+ and valve code must reside in Tomcat's main lib/ directory.
+ </para>
+ <para>
+<programlisting>
+$ cd $TOMCAT_HOME/lib
+$ unzip keycloak-saml-tomcat6-adapter-dist.zip
+ or
+$ unzip keycloak-saml-tomcat7-adapter-dist.zip
+ or
+$ unzip keycloak-saml-tomcat8-adapter-dist.zip
+</programlisting>
+ </para>
+ </section>
+
+ <section>
+ <title>Required Per WAR Configuration</title>
+ <para>
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+ </para>
+ <para>
+ The first thing you must do is create a <literal>META-INF/context.xml</literal> file in your WAR package. This is
+ a Tomcat specific config file and you must define a Keycloak specific Valve.
+ </para>
+ <programlisting>
+<![CDATA[
+<Context path="/your-context-path">
+ <Valve className="org.keycloak.adapters.saml.tomcat.SamlAuthenticatorValve"/>
+</Context>]]>
+ </programlisting>
+ <para>
+ Next you must create
+ a <literal>keycloak-saml.xml</literal> adapter config file within the <literal>WEB-INF</literal> directory
+ of your WAR. The format of this config file is describe in the <link linkend='adapter-config'>general adapter configuration</link>
+ section.
+ </para>
+ <para>
+ Finally you must specify both a <literal>login-config</literal> and use standard servlet security to specify
+ role-base constraints on your URLs. Here's an example:
+ </para>
+ <para>
+<programlisting>
+<![CDATA[
+<web-app xmlns="http://java.sun.com/xml/ns/javaee"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
+ version="3.0">
+
+ <module-name>customer-portal</module-name>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Customers</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>BASIC</auth-method>
+ <realm-name>this is ignored currently</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
+]]>
+</programlisting>
+ </para>
+ </section>
+</chapter>
\ No newline at end of file
diff --git a/events/api/src/main/java/org/keycloak/events/EventType.java b/events/api/src/main/java/org/keycloak/events/EventType.java
index eacce62..ac93a4f 100755
--- a/events/api/src/main/java/org/keycloak/events/EventType.java
+++ b/events/api/src/main/java/org/keycloak/events/EventType.java
@@ -69,7 +69,16 @@ public enum EventType {
IMPERSONATE(true),
CUSTOM_REQUIRED_ACTION(true),
CUSTOM_REQUIRED_ACTION_ERROR(true),
- EXECUTE_ACTIONS(true);
+ EXECUTE_ACTIONS(true),
+
+ CLIENT_INFO(false),
+ CLIENT_INFO_ERROR(false),
+ CLIENT_REGISTER(true),
+ CLIENT_REGISTER_ERROR(true),
+ CLIENT_UPDATE(true),
+ CLIENT_UPDATE_ERROR(true),
+ CLIENT_DELETE(true),
+ CLIENT_DELETE_ERROR(true);
private boolean saveByDefault;
examples/js-console/README.md 2(+1 -1)
diff --git a/examples/js-console/README.md b/examples/js-console/README.md
index 749c0f3..fa9a02d 100644
--- a/examples/js-console/README.md
+++ b/examples/js-console/README.md
@@ -12,6 +12,6 @@ Open the Keycloak admin console, click on Add Realm, click on 'Choose a JSON fil
Deploy the JS Console to Keycloak by running:
- mvn install jboss-as:deploy
+ mvn install wildfly:deploy
Open the console at http://localhost:8080/js-console and login with username: 'user', and password: 'password'.
diff --git a/examples/js-console/src/main/webapp/index.html b/examples/js-console/src/main/webapp/index.html
index d85b63b..2cca0f8 100644
--- a/examples/js-console/src/main/webapp/index.html
+++ b/examples/js-console/src/main/webapp/index.html
@@ -7,6 +7,7 @@
<div>
<button onclick="keycloak.login()">Login</button>
<button onclick="keycloak.logout()">Logout</button>
+ <button onclick="keycloak.register()">Register</button>
<button onclick="refreshToken(9999)">Refresh Token</button>
<button onclick="refreshToken(30)">Refresh Token (if <30s validity)</button>
<button onclick="loadProfile()">Get Profile</button>
@@ -18,6 +19,7 @@
<button onclick="output(keycloak)">Show Details</button>
<button onclick="output(keycloak.createLoginUrl())">Show Login URL</button>
<button onclick="output(keycloak.createLogoutUrl())">Show Logout URL</button>
+ <button onclick="output(keycloak.createRegisterUrl())">Show Register URL</button>
</div>
<h2>Result</h2>
diff --git a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
index c3132e2..a094acf 100755
--- a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
+++ b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/examples/saml/post-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/post-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
index 875d52c..d098103 100755
--- a/examples/saml/post-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
+++ b/examples/saml/post-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/examples/saml/redirect-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/redirect-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
index b658234..34ee414 100755
--- a/examples/saml/redirect-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
+++ b/examples/saml/redirect-with-signature/src/main/webapp/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java
index e9f30f8..be4ddcd 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/query/internal/LDAPQuery.java
@@ -47,6 +47,8 @@ public class LDAPQuery {
private final List<UserFederationMapperModel> mappers = new ArrayList<UserFederationMapperModel>();
private int searchScope = SearchControls.SUBTREE_SCOPE;
+
+ private String ldapFilter = null;
public LDAPQuery(LDAPFederationProvider ldapProvider) {
this.ldapFedProvider = ldapProvider;
@@ -189,4 +191,12 @@ public class LDAPQuery {
return this.conditions;
}
+ public String getLdapFilter() {
+ return ldapFilter;
+ }
+
+ public void setLdapFilter(String ldapFilter) {
+ this.ldapFilter = ldapFilter;
+ }
+
}
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
index dc93742..9bc51f3 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/idm/store/ldap/LDAPIdentityStore.java
@@ -255,7 +255,9 @@ public class LDAPIdentityStore implements IdentityStore {
for (Condition condition : identityQuery.getConditions()) {
applyCondition(filter, condition);
}
-
+ if (!(identityQuery.getLdapFilter() == null || identityQuery.getLdapFilter().isEmpty())) {
+ filter.append(identityQuery.getLdapFilter());
+ }
filter.insert(0, "(&");
filter.append(getObjectClassesFilter(identityQuery.getObjectClasses()));
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
index 09375fc..76d2df9 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapper.java
@@ -54,6 +54,9 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
// See docs for Mode enum
public static final String MODE = "mode";
+
+ // Customized LDAP filter which is added to the whole LDAP query
+ public static final String ROLES_LDAP_FILTER = "roles.ldap.filter";
// List of IDs of UserFederationMapperModels where syncRolesFromLDAP was already called in this KeycloakSession. This is to improve performance
@@ -128,6 +131,7 @@ public class RoleLDAPFederationMapper extends AbstractLDAPFederationMapper {
ldapQuery.addObjectClasses(roleObjectClasses);
String rolesRdnAttr = getRoleNameLdapAttribute(mapperModel);
+ ldapQuery.setLdapFilter(mapperModel.getConfig().get(RoleLDAPFederationMapper.ROLES_LDAP_FILTER));
String membershipAttr = getMembershipLdapAttribute(mapperModel);
ldapQuery.addReturningLdapAttribute(rolesRdnAttr);
ldapQuery.addReturningLdapAttribute(membershipAttr);
diff --git a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
index 2d6d24c..07b4635 100644
--- a/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
+++ b/federation/ldap/src/main/java/org/keycloak/federation/ldap/mappers/RoleLDAPFederationMapperFactory.java
@@ -51,6 +51,12 @@ public class RoleLDAPFederationMapperFactory extends AbstractLDAPFederationMappe
"Object class (or classes) of the role object. It's divided by comma if more classes needed. In typical LDAP deployment it could be 'groupOfNames' . In Active Directory it's usually 'group' ",
ProviderConfigProperty.STRING_TYPE, null);
configProperties.add(roleObjectClasses);
+
+ ProviderConfigProperty ldapFilter = createConfigProperty(RoleLDAPFederationMapper.ROLES_LDAP_FILTER,
+ "LDAP Filter",
+ "LDAP Filter adds additional custom filter to the whole query.",
+ ProviderConfigProperty.STRING_TYPE, null);
+ configProperties.add(ldapFilter);
List<String> modes = new LinkedList<String>();
for (RoleLDAPFederationMapper.Mode mode : RoleLDAPFederationMapper.Mode.values()) {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties
index 6442e6e..10549e1 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_de.properties
@@ -6,7 +6,13 @@ cancel=de Cancel
onText=AN
offText=AUS
client=de Client
+clients=de Clients
clear=de Clear
+selectOne=de Select One...
+
+true=de True
+false=de False
+
# Realm settings
realm-detail.enabled.tooltip=de Users and clients can only access a realm if it's enabled
@@ -111,6 +117,346 @@ active-sessions=de Active Sessions
sessions=de Sessions
not-before=de Not Before
not-before.tooltip=de Revoke any tokens issued before this date.
-set-to-now=de Set To Now
+set-to-now=de Set to now
push=de Push
push.tooltip=de For every client that has an admin URL, notify them of the new revocation policy.
+
+#Protocol Mapper
+usermodel.prop.label=de Property
+usermodel.prop.tooltip=de Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.
+usermodel.attr.label=de User Attribute
+usermodel.attr.tooltip=de Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.
+userSession.modelNote.label=de User Session Note
+userSession.modelNote.tooltip=de Name of stored user session note within the UserSessionModel.note map.
+multivalued.label=de Multivalued
+multivalued.tooltip=de Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim
+selectRole.label=de Select Role
+selectRole.tooltip=de Enter role in the textbox to the left, or click this button to browse and select the role you want
+tokenClaimName.label=de Token Claim Name
+tokenClaimName.tooltip=de Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.
+jsonType.label=de Claim JSON Type
+jsonType.tooltip=de JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.
+includeInIdToken.label=de Add to ID token
+includeInIdTokenTooltip.Should the claim be added to the ID token?
+includeInAccessToken.label=de Add to access token
+includeInAccessToken.tooltip=de Should the claim be added to the access token?
+
+
+# client details
+clients.tooltip=de Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.
+search.placeholder=de Search...
+create=de Create
+import=de Import
+client-id=de Client ID
+base-url=de Base URL
+actions=de Actions
+not-defined=de Not defined
+edit=de Edit
+delete=de Delete
+no-results=de No results
+no-clients-available=de No clients available
+add-client=de Add Client
+select-file=de Select file
+view-details=de View details
+clear-import=de Clear import
+client-id.tooltip=de Specifies ID referenced in URI and tokens. For example 'my-client'
+client.name.tooltip=de Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}
+client.enabled.tooltip=de Disabled clients cannot initiate a login or have obtain access tokens.
+consent-required=de Consent Required
+consent-required.tooltip=de If enabled users have to consent to client access.
+direct-grants-only=de Direct Grants Only
+direct-grants-only.tooltip=de When enabled, client can only obtain grants from grant REST API.
+client-protocol=de Client Protocol
+client-protocol.tooltip=de 'OpenID connect' allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.'SAML' enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information.
+access-type=de Access Type
+access-type.tooltip=de 'Confidential' clients require a secret to initiate login protocol. 'Public' clients do not require a secret. 'Bearer-only' clients are web services that never initiate a login.
+service-accounts-enabled=de Service Accounts Enabled
+service-accounts-enabled.tooltip=de Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client.
+include-authnstatement=de Include AuthnStatement
+include-authnstatement.tooltip=de Should a statement specifying the method and timestamp be included in login responses?
+sign-documents=de Sign Documents
+sign-documents.tooltip=de Should SAML documents be signed by the realm?
+sign-assertions=de Sign Assertions
+sign-assertions.tooltip=de Should assertions inside SAML documents be signed? This setting isn't needed if document is already being signed.
+signature-algorithm=de Signature Algorithm
+signature-algorithm.tooltip=de The signature algorithm to use to sign documents.
+canonicalization-method=de Canonicalization Method
+canonicalization-method.tooltip=de Canonicalization Method for XML signatures.
+encrypt-assertions=de Encrypt Assertions
+encrypt-assertions.tooltip=de Should SAML assertions be encrypted with client's public key using AES?
+client-signature-required=de Client Signature Required
+client-signature-required.tooltip=de Will the client sign their saml requests and responses? And should they be validated?
+force-post-binding=de Force POST Binding
+force-post-binding.tooltip=de Always use POST binding for responses.
+front-channel-logout=de Front Channel Logout
+front-channel-logout.tooltip=de When true, logout requires a browser redirect to client. When false, server performs a background invocation for logout.
+force-name-id-format=de Force Name ID Format
+force-name-id-format.tooltip=de Ignore requested NameID subject format and use admin console configured one.
+name-id-format=de Name ID Format
+name-id-format.tooltip=de The name ID format to use for the subject.
+root-url=de Root URL
+root-url.tooltip=de Root URL appended to relative URLs
+valid-redirect-uris=de Valid Redirect URIs
+valid-redirect-uris.tooltip=de Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed i.e. 'http://example.com/*'. Relative path can be specified too i.e. /my/relative/path/*. Relative paths will generate a redirect URI using the request's host and port. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request.
+base-url.tooltip=de Default URL to use when the auth server needs to redirect or link back to the client.
+admin-url=de Admin URL
+admin-url.tooltip=de URL to the admin interface of the client. Set this if the client supports the adapter REST API. This REST API allows the auth server to push revocation policies and other adminstrative tasks. Usually this is set to the base URL of the client.
+master-saml-processing-url=de Master SAML Processing URL
+master-saml-processing-url.tooltip=de If configured, this URL will be used for every binding to both the SP's Assertion Consumer and Single Logout Services. This can be individually overiden for each binding and service in the Fine Grain SAML Endpoint Configuration.
+idp-sso-url-ref=de IDP Initiated SSO URL Name
+idp-sso-url-ref.tooltip=de URL fragment name to reference client when you want to do IDP Initiated SSO. Leaving this empty will disable IDP Initiated SSO. The URL you will reference from your browser will be: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}
+idp-sso-relay-state=de IDP Initiated SSO Relay State
+idp-sso-relay-state.tooltip=de Relay state you want to send with SAML request when you want to do IDP Initiated SSO.
+web-origins=de Web Origins
+web-origins.tooltip=de Allowed CORS origins. To permit all origins of Valid Redirect URIs add '+'. To permit all origins add '*'.
+fine-saml-endpoint-conf=de Fine Grain SAML Endpoint Configuration
+fine-saml-endpoint-conf.tooltip=de Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service.
+assertion-consumer-post-binding-url=de Assertion Consumer Service POST Binding URL
+assertion-consumer-post-binding-url.tooltip=de SAML POST Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.
+assertion-consumer-redirect-binding-url=de Assertion Consumer Service Redirect Binding URL
+assertion-consumer-redirect-binding-url.tooltip=de SAML Redirect Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.
+logout-service-binding-post-url=de Logout Service POST Binding URL
+logout-service-binding-post-url.tooltip=de SAML POST Binding URL for the client's single logout service. You can leave this blank if you are using a different binding
+logout-service-redir-binding-url=de Logout Service Redirect Binding URL
+logout-service-redir-binding-url.tooltip=de SAML Redirect Binding URL for the client's single logout service. You can leave this blank if you are using a different binding.
+
+# client import
+import-client=de Import Client
+format-option=de Format Option
+select-format=de Select a Format
+import-file=de Import File
+
+# client tabs
+settings=de Settings
+credentials=de Credentials
+saml-keys=de SAML Keys
+roles=de Roles
+mappers=de Mappers
+mappers.tootip=de Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the client and auth server.
+scope=de Scope
+scope.tooltip=de Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client.
+sessions.tooltip=de View active sessions for this client. Allows you to see which users are active and when they logged in.
+offline-access=de Offline Access
+offline-access.tooltip=de View offline sessions for this client. Allows you to see which users retrieve offline token and when they retrieve it. To revoke all tokens for the client, go to Revocation tab and set not before value to now.
+clustering=de Clustering
+installation=de Installation
+installation.tooltip=de Helper utility for generating various client adapter configuration formats which you can download or cut and paste to configure your clients.
+service-account-roles=de Service Account Roles
+service-account-roles.tooltip=de Allows you to authenticate role mappings for the service account dedicated to this client.
+
+# client credentials
+client-authenticator=de Client Authenticator
+client-authenticator.tooltip=de Client Authenticator used for authentication this client against Keycloak server
+certificate.tooltip=de Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.
+no-client-certificate-configured=de No client certificate configured
+gen-new-keys-and-cert=de Generate new keys and certificate
+import-certificate=de Import Certificate
+gen-client-private-key=de Generate Client Private Key
+generate-private-key=de Generate Private Key
+archive-format=de Archive Format
+archive-format.tooltip=de Java keystore or PKCS12 archive format.
+key-alias=de Key Alias
+key-alias.tooltip=de Archive alias for your private key and certificate.
+key-password=de Key Password
+key-password.tooltip=de Password to access the private key in the archive
+store-password=de Store Password
+store-password.tooltip=de Password to access the archive itself
+generate-and-download=de Generate and Download
+client-certificate-import=de Client Certificate Import
+import-client-certificate=de Import Client Certificate
+jwt-import.key-alias.tooltip=de Archive alias for your certificate.
+secret=de Secret
+regenerate-secret=de Regenerate Secret
+add-role=de Add Role
+role-name=de Role Name
+composite=de Composite
+description=de Description
+no-client-roles-available=de No client roles available
+scope-param-required=de Scope Param Required
+scope-param-required.tooltip=de This role will only be granted if scope parameter with role name is used during authorization/token request.
+composite-roles=de Composite Roles
+composite-roles.tooltip=de When this role is (un)assigned to a user any role associated with it will be (un)assigned implicitly.
+realm-roles=de Realm Roles
+available-roles=de Available Roles
+add-selected=de Add selected
+associated-roles=de Associated Roles
+composite.associated-realm-roles.tooltip=de Realm level roles associated with this composite role.
+composite.available-realm-roles.tooltip=de Realm level roles associated with this composite role.
+remove-selected=de Remove selected
+client-roles=de Client Roles
+select-client-to-view-roles=de Select client to view roles for client
+available-roles.tooltip=de Roles from this client that you can associate to this composite role.
+client.associated-roles.tooltip=de Client roles associated with this composite role.
+add-builtin=de Add Builtin
+category=de Category
+type=de Type
+no-mappers-available=de No mappers available
+add-builtin-protocol-mappers=de Add Builtin Protocol Mappers
+add-builtin-protocol-mapper=de Add Builtin Protocol Mapper
+scope-mappings=de Scope Mappings
+full-scope-allowed=de Full Scope Allowed
+full-scope-allowed.tooltip=de Allows you to disable all restrictions.
+scope.available-roles.tooltip=de Realm level roles that can be assigned to scope.
+assigned-roles=de Assigned Roles
+assigned-roles.tooltip=de Realm level roles assigned to scope.
+effective-roles=de Effective Roles
+realm.effective-roles.tooltip=de Assigned realm level roles that may have been inherited from a composite role.
+select-client-roles.tooltip=de Select client to view roles for client
+assign.available-roles.tooltip=de Client roles available to be assigned.
+client.assigned-roles.tooltip=de Assigned client roles.
+client.effective-roles.tooltip=de Assigned client roles that may have been inherited from a composite role.
+basic-configuration=de Basic configuration
+node-reregistration-timeout=de Node Re-registration Timeout
+node-reregistration-timeout.tooltip=de Interval to specify max time for registered clients cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak
+registered-cluster-nodes=de Registered cluster nodes
+register-node-manually=de Register node manually
+test-cluster-availability=de Test cluster availability
+last-registration=de Last registration
+node-host=de Node host
+no-registered-cluster-nodes=de No registered cluster nodes available
+cluster-nodes=de Cluster Nodes
+add-node=de Add Node
+active-sessions.tooltip=de Total number of active user sessions for this client.
+show-sessions=de Show Sessions
+show-sessions.tooltip=de Warning, this is a potentially expensive operation depending on number of active sessions.
+user=de User
+from-ip=de From IP
+session-start=de Session Start
+first-page=de First Page
+previous-page=de Previous Page
+next-page=de Next Page
+client-revoke.not-before.tooltip=de Revoke any tokens issued before this date for this client.
+client-revoke.push.tooltip=de If admin URL is configured for this client, push this policy to that client.
+select-a-format=de Select a Format
+download=de Download
+offline-tokens=de Offline Tokens
+offline-tokens.tooltip=de Total number of offline tokens for this client.
+show-offline-tokens=de Show Offline Tokens
+show-offline-tokens.tooltip=de Warning, this is a potentially expensive operation depending on number of offline tokens.
+token-issued=de Token Issued
+key-export=de Key Export
+key-import=de Key Import
+export-saml-key=de Export SAML Key
+import-saml-key=de Import SAML Key
+realm-certificate-alias=de Realm Certificate Alias
+realm-certificate-alias.tooltip=de Realm certificate is stored in archive too. This is the alias to it.
+signing-key=de Signing Key
+saml-signing-key=de SAML Signing Key.
+private-key=de Private Key
+generate-new-keys=de Generate new keys
+export=de Export
+encryption-key=de Encryption Key
+saml-encryption-key.tooltip=de SAML Encryption Key.
+service-accounts=de Service Accounts
+service-account.available-roles.tooltip=de Realm level roles that can be assigned to service account.
+service-account.assigned-roles.tooltip=de Realm level roles assigned to service account.
+service-account-is-not-enabled-for=de Service account is not enabled for {{client}}
+create-protocol-mappers=de Create Protocol Mappers
+create-protocol-mapper=de Create Protocol Mapper
+protocol=de Protocol
+protocol.tooltip=de Protocol.
+id=de ID
+mapper.name.tooltip=de Name of the mapper.
+mapper.consent-required.tooltip=de When granting temporary access, must the user consent to providing this data to the client?
+consent-text=de Consent Text
+consent-text.tooltip=de Text to display on consent page.
+mapper-type=de Mapper Type
+select-role=de Select role
+select-role.tooltip=de Enter role in the textbox to the left, or click this button to browse and select the role you want.
+
+# realm identity providers
+identity-providers=de Identity Providers
+table-of-identity-providers=de Table of identity providers
+add-provider.placeholder=de Add provider...
+provider=de Provider
+gui-order=de GUI order
+redirect-uri=de Redirect URI
+redirect-uri.tooltip=de The redirect uri to use when configuring the identity provider.
+alias=de Alias
+identity-provider.alias.tooltip=de The alias uniquely identifies an identity provider and it is also used to build the redirect uri.
+identity-provider.enabled.tooltip=de Enable/disable this identity provider.
+authenticate-by-default=de Authenticate by Default
+identity-provider.authenticate-by-default.tooltip=de Indicates if this provider should be tried by default for authentication even before displaying login screen.
+store-tokens=de Store Tokens
+identity-provider.store-tokens.tooltip=de Enable/disable if tokens must be stored after authenticating users.
+stored-tokens-readable=de Stored Tokens Readable
+identity-provider.stored-tokens-readable.tooltip=de Enable/disable if new users can read any stored tokens. This assigns the broker.read-token role.
+update-profile-on-first-login=de Update Profile on First Login
+on=de On
+on-missing-info=de On missing info
+off=de Off
+update-profile-on-first-login.tooltip=de Define conditions under which a user has to update their profile during first-time login.
+trust-email=de Trust Email
+trust-email.tooltip=de If enabled then email provided by this provider is not verified even if verification is enabled for the realm.
+gui-order.tooltip=de Number defining order of the provider in GUI (eg. on Login page).
+openid-connect-config=de OpenID Connect Config
+openid-connect-config.tooltip=de OIDC SP and external IDP configuration.
+authorization-url=de Authorization URL
+authorization-url.tooltip=de The Authorization Url.
+token-url=de Token URL
+token-url.tooltip=de The Token URL.
+logout-url=de Logout URL
+identity-provider.logout-url.tooltip=de End session endpoint to use to logout user from external IDP.
+backchannel-logout=de Backchannel Logout
+backchannel-logout.tooltip=de Does the external IDP support backchannel logout?
+user-info-url=de User Info URL
+user-info-url.tooltip=de The User Info Url. This is optional.
+identity-provider.client-id.tooltip=de The client or client identifier registered within the identity provider.
+client-secret=de Client Secret
+show-secret=de Show secret
+hide-secret=de Hide secret
+client-secret.tooltip=de The client or client secret registered within the identity provider.
+issuer=de Issuer
+issuer.tooltip=de The issuer identifier for the issuer of the response. If not provided, no validation will be performed.
+default-scopes=de Default Scopes
+identity-provider.default-scopes.tooltip=de The scopes to be sent when asking for authorization. It can be a space-separated list of scopes. Defaults to 'openid'.
+prompt=de Prompt
+unspecified.option=de unspecified
+none.option=de none
+consent.option=de consent
+login.option=de login
+select-account.option=de select_account
+prompt.tooltip=de Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
+validate-signatures=de Validate Signatures
+identity-provider.validate-signatures.tooltip=de Enable/disable signature validation of external IDP signatures.
+validating-public-key=de Validating Public Key
+identity-provider.validating-public-key.tooltip=de The public key in PEM format that must be used to verify external IDP signatures.
+import-external-idp-config=de Import External IDP Config
+import-external-idp-config.tooltip=de Allows you to load external IDP metadata from a config file or to download it from a URL.
+import-from-url=de Import from URL
+identity-provider.import-from-url.tooltip=de Import metadata from a remote IDP discovery descriptor.
+import-from-file=de Import from file
+identity-provider.import-from-file.tooltip=de Import metadata from a downloaded IDP discovery descriptor.
+saml-config=de SAML Config
+identity-provider.saml-config.tooltip=de SAML SP and external IDP configuration.
+single-signon-service-url=de Single Sign-On Service URL
+saml.single-signon-service-url.tooltip=de The Url that must be used to send authentication requests (SAML AuthnRequest).
+single-logout-service-url=de Single Logout Service URL
+saml.single-logout-service-url.tooltip=de The Url that must be used to send logout requests.
+nameid-policy-format=de NameID Policy Format
+nameid-policy-format.tooltip=de Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.
+http-post-binding-response=de HTTP-POST Binding Response
+http-post-binding-response.tooltip=de Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
+http-post-binding-for-authn-request=de HTTP-POST Binding for AuthnRequest
+http-post-binding-for-authn-request.tooltip=de Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
+want-authn-requests-signed=de Want AuthnRequests Signed
+want-authn-requests-signed.tooltip=de Indicates whether the identity provider expects signed a AuthnRequest.
+force-authentication=de Force Authentication
+identity-provider.force-authentication.tooltip=de Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
+validate-signature=de Validate Signature
+saml.validate-signature.tooltip=de Enable/disable signature validation of SAML responses.
+validating-x509-certificate=de Validating X509 Certificate
+validating-x509-certificate.tooltip=de The certificate in PEM format that must be used to check for signatures.
+saml.import-from-url.tooltip=de Import metadata from a remote IDP SAML entity descriptor.
+social.client-id.tooltip=de The client identifier registered with the identity provider.
+social.client-secret.tooltip=de The client secret registered with the identity provider.
+social.default-scopes.tooltip=de The scopes to be sent when asking for authorization. See documentation for possible values, separator and default value'.
+key=de Key
+stackoverflow.key.tooltip=de The Key obtained from Stack Overflow client registration.
+
+realms=de Realms
+realm=de Realm
+
+identity-provider-mappers=de Identity Provider Mappers
+create-identity-provider-mapper=de Create Identity Provider Mapper
+add-identity-provider-mapper=de Add Identity Provider Mapper
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index be3ef2d..d5193e7 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -6,7 +6,13 @@ cancel=Cancel
onText=ON
offText=OFF
client=Client
+clients=Clients
clear=Clear
+selectOne=Select One...
+
+true=True
+false=False
+
# Realm settings
realm-detail.enabled.tooltip=Users and clients can only access a realm if it's enabled
@@ -111,6 +117,344 @@ active-sessions=Active Sessions
sessions=Sessions
not-before=Not Before
not-before.tooltip=Revoke any tokens issued before this date.
-set-to-now=Set To Now
+set-to-now=Set to now
push=Push
push.tooltip=For every client that has an admin URL, notify them of the new revocation policy.
+
+#Protocol Mapper
+usermodel.prop.label=Property
+usermodel.prop.tooltip=Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.
+usermodel.attr.label=User Attribute
+usermodel.attr.tooltip=Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.
+userSession.modelNote.label=User Session Note
+userSession.modelNote.tooltip=Name of stored user session note within the UserSessionModel.note map.
+multivalued.label=Multivalued
+multivalued.tooltip=Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim
+selectRole.label=Select Role
+selectRole.tooltip=Enter role in the textbox to the left, or click this button to browse and select the role you want.
+tokenClaimName.label=Token Claim Name
+tokenClaimName.tooltip=Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.
+jsonType.label=Claim JSON Type
+jsonType.tooltip=JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.
+includeInIdToken.label=Add to ID token
+includeInIdTokenTooltip.Should the claim be added to the ID token?
+includeInAccessToken.label=Add to access token
+includeInAccessToken.tooltip=Should the claim be added to the access token?
+
+
+# client details
+clients.tooltip=Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.
+search.placeholder=Search...
+create=Create
+import=Import
+client-id=Client ID
+base-url=Base URL
+actions=Actions
+not-defined=Not defined
+edit=Edit
+delete=Delete
+no-results=No results
+no-clients-available=No clients available
+add-client=Add Client
+select-file=Select file
+view-details=View details
+clear-import=Clear import
+client-id.tooltip=Specifies ID referenced in URI and tokens. For example 'my-client'
+client.name.tooltip=Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}
+client.enabled.tooltip=Disabled clients cannot initiate a login or have obtain access tokens.
+consent-required=Consent Required
+consent-required.tooltip=If enabled users have to consent to client access.
+direct-grants-only=Direct Grants Only
+direct-grants-only.tooltip=When enabled, client can only obtain grants from grant REST API.
+client-protocol=Client Protocol
+client-protocol.tooltip='OpenID connect' allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.'SAML' enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information.
+access-type=Access Type
+access-type.tooltip='Confidential' clients require a secret to initiate login protocol. 'Public' clients do not require a secret. 'Bearer-only' clients are web services that never initiate a login.
+service-accounts-enabled=Service Accounts Enabled
+service-accounts-enabled.tooltip=Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client.
+include-authnstatement=Include AuthnStatement
+include-authnstatement.tooltip=Should a statement specifying the method and timestamp be included in login responses?
+sign-documents=Sign Documents
+sign-documents.tooltip=Should SAML documents be signed by the realm?
+sign-assertions=Sign Assertions
+sign-assertions.tooltip=Should assertions inside SAML documents be signed? This setting isn't needed if document is already being signed.
+signature-algorithm=Signature Algorithm
+signature-algorithm.tooltip=The signature algorithm to use to sign documents.
+canonicalization-method=Canonicalization Method
+canonicalization-method.tooltip=Canonicalization Method for XML signatures.
+encrypt-assertions=Encrypt Assertions
+encrypt-assertions.tooltip=Should SAML assertions be encrypted with client's public key using AES?
+client-signature-required=Client Signature Required
+client-signature-required.tooltip=Will the client sign their saml requests and responses? And should they be validated?
+force-post-binding=Force POST Binding
+force-post-binding.tooltip=Always use POST binding for responses.
+front-channel-logout=Front Channel Logout
+front-channel-logout.tooltip=When true, logout requires a browser redirect to client. When false, server performs a background invocation for logout.
+force-name-id-format=Force Name ID Format
+force-name-id-format.tooltip=Ignore requested NameID subject format and use admin console configured one.
+name-id-format=Name ID Format
+name-id-format.tooltip=The name ID format to use for the subject.
+root-url=Root URL
+root-url.tooltip=Root URL appended to relative URLs
+valid-redirect-uris=Valid Redirect URIs
+valid-redirect-uris.tooltip=Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed i.e. 'http://example.com/*'. Relative path can be specified too i.e. /my/relative/path/*. Relative paths will generate a redirect URI using the request's host and port. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request.
+base-url.tooltip=Default URL to use when the auth server needs to redirect or link back to the client.
+admin-url=Admin URL
+admin-url.tooltip=URL to the admin interface of the client. Set this if the client supports the adapter REST API. This REST API allows the auth server to push revocation policies and other adminstrative tasks. Usually this is set to the base URL of the client.
+master-saml-processing-url=Master SAML Processing URL
+master-saml-processing-url.tooltip=If configured, this URL will be used for every binding to both the SP's Assertion Consumer and Single Logout Services. This can be individually overiden for each binding and service in the Fine Grain SAML Endpoint Configuration.
+idp-sso-url-ref=IDP Initiated SSO URL Name
+idp-sso-url-ref.tooltip=URL fragment name to reference client when you want to do IDP Initiated SSO. Leaving this empty will disable IDP Initiated SSO. The URL you will reference from your browser will be: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}
+idp-sso-relay-state=IDP Initiated SSO Relay State
+idp-sso-relay-state.tooltip=Relay state you want to send with SAML request when you want to do IDP Initiated SSO.
+web-origins=Web Origins
+web-origins.tooltip=Allowed CORS origins. To permit all origins of Valid Redirect URIs add '+'. To permit all origins add '*'.
+fine-saml-endpoint-conf=Fine Grain SAML Endpoint Configuration
+fine-saml-endpoint-conf.tooltip=Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service.
+assertion-consumer-post-binding-url=Assertion Consumer Service POST Binding URL
+assertion-consumer-post-binding-url.tooltip=SAML POST Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.
+assertion-consumer-redirect-binding-url=Assertion Consumer Service Redirect Binding URL
+assertion-consumer-redirect-binding-url.tooltip=SAML Redirect Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.
+logout-service-binding-post-url=Logout Service POST Binding URL
+logout-service-binding-post-url.tooltip=SAML POST Binding URL for the client's single logout service. You can leave this blank if you are using a different binding
+logout-service-redir-binding-url=Logout Service Redirect Binding URL
+logout-service-redir-binding-url.tooltip=SAML Redirect Binding URL for the client's single logout service. You can leave this blank if you are using a different binding.
+
+# client import
+import-client=Import Client
+format-option=Format Option
+select-format=Select a Format
+import-file=Import File
+
+# client tabs
+settings=Settings
+credentials=Credentials
+saml-keys=SAML Keys
+roles=Roles
+mappers=Mappers
+mappers.tootip=Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the client and auth server.
+scope=Scope
+scope.tooltip=Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client.
+sessions.tooltip=View active sessions for this client. Allows you to see which users are active and when they logged in.
+offline-access=Offline Access
+offline-access.tooltip=View offline sessions for this client. Allows you to see which users retrieve offline token and when they retrieve it. To revoke all tokens for the client, go to Revocation tab and set not before value to now.
+clustering=Clustering
+installation=Installation
+installation.tooltip=Helper utility for generating various client adapter configuration formats which you can download or cut and paste to configure your clients.
+service-account-roles=Service Account Roles
+service-account-roles.tooltip=Allows you to authenticate role mappings for the service account dedicated to this client.
+
+# client credentials
+client-authenticator=Client Authenticator
+client-authenticator.tooltip=Client Authenticator used for authentication this client against Keycloak server
+certificate.tooltip=Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.
+no-client-certificate-configured=No client certificate configured
+gen-new-keys-and-cert=Generate new keys and certificate
+import-certificate=Import Certificate
+gen-client-private-key=Generate Client Private Key
+generate-private-key=Generate Private Key
+archive-format=Archive Format
+archive-format.tooltip=Java keystore or PKCS12 archive format.
+key-alias=Key Alias
+key-alias.tooltip=Archive alias for your private key and certificate.
+key-password=Key Password
+key-password.tooltip=Password to access the private key in the archive
+store-password=Store Password
+store-password.tooltip=Password to access the archive itself
+generate-and-download=Generate and Download
+client-certificate-import=Client Certificate Import
+import-client-certificate=Import Client Certificate
+jwt-import.key-alias.tooltip=Archive alias for your certificate.
+secret=Secret
+regenerate-secret=Regenerate Secret
+add-role=Add Role
+role-name=Role Name
+composite=Composite
+description=Description
+no-client-roles-available=No client roles available
+scope-param-required=Scope Param Required
+scope-param-required.tooltip=This role will only be granted if scope parameter with role name is used during authorization/token request.
+composite-roles=Composite Roles
+composite-roles.tooltip=When this role is (un)assigned to a user any role associated with it will be (un)assigned implicitly.
+realm-roles=Realm Roles
+available-roles=Available Roles
+add-selected=Add selected
+associated-roles=Associated Roles
+composite.associated-realm-roles.tooltip=Realm level roles associated with this composite role.
+composite.available-realm-roles.tooltip=Realm level roles associated with this composite role.
+remove-selected=Remove selected
+client-roles=Client Roles
+select-client-to-view-roles=Select client to view roles for client
+available-roles.tooltip=Roles from this client that you can associate to this composite role.
+client.associated-roles.tooltip=Client roles associated with this composite role.
+add-builtin=Add Builtin
+category=Category
+type=Type
+no-mappers-available=No mappers available
+add-builtin-protocol-mappers=Add Builtin Protocol Mappers
+add-builtin-protocol-mapper=Add Builtin Protocol Mapper
+scope-mappings=Scope Mappings
+full-scope-allowed=Full Scope Allowed
+full-scope-allowed.tooltip=Allows you to disable all restrictions.
+scope.available-roles.tooltip=Realm level roles that can be assigned to scope.
+assigned-roles=Assigned Roles
+assigned-roles.tooltip=Realm level roles assigned to scope.
+effective-roles=Effective Roles
+realm.effective-roles.tooltip=Assigned realm level roles that may have been inherited from a composite role.
+select-client-roles.tooltip=Select client to view roles for client
+assign.available-roles.tooltip=Client roles available to be assigned.
+client.assigned-roles.tooltip=Assigned client roles.
+client.effective-roles.tooltip=Assigned client roles that may have been inherited from a composite role.
+basic-configuration=Basic configuration
+node-reregistration-timeout=Node Re-registration Timeout
+node-reregistration-timeout.tooltip=Interval to specify max time for registered clients cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak
+registered-cluster-nodes=Registered cluster nodes
+register-node-manually=Register node manually
+test-cluster-availability=Test cluster availability
+last-registration=Last registration
+node-host=Node host
+no-registered-cluster-nodes=No registered cluster nodes available
+cluster-nodes=Cluster Nodes
+add-node=Add Node
+active-sessions.tooltip=Total number of active user sessions for this client.
+show-sessions=Show Sessions
+show-sessions.tooltip=Warning, this is a potentially expensive operation depending on number of active sessions.
+user=User
+from-ip=From IP
+session-start=Session Start
+first-page=First Page
+previous-page=Previous Page
+next-page=Next Page
+client-revoke.not-before.tooltip=Revoke any tokens issued before this date for this client.
+client-revoke.push.tooltip=If admin URL is configured for this client, push this policy to that client.
+select-a-format=Select a Format
+download=Download
+offline-tokens=Offline Tokens
+offline-tokens.tooltip=Total number of offline tokens for this client.
+show-offline-tokens=Show Offline Tokens
+show-offline-tokens.tooltip=Warning, this is a potentially expensive operation depending on number of offline tokens.
+token-issued=Token Issued
+key-export=Key Export
+key-import=Key Import
+export-saml-key=Export SAML Key
+import-saml-key=Import SAML Key
+realm-certificate-alias=Realm Certificate Alias
+realm-certificate-alias.tooltip=Realm certificate is stored in archive too. This is the alias to it.
+signing-key=Signing Key
+saml-signing-key=SAML Signing Key.
+private-key=Private Key
+generate-new-keys=Generate new keys
+export=Export
+encryption-key=Encryption Key
+saml-encryption-key.tooltip=SAML Encryption Key.
+service-accounts=Service Accounts
+service-account.available-roles.tooltip=Realm level roles that can be assigned to service account.
+service-account.assigned-roles.tooltip=Realm level roles assigned to service account.
+service-account-is-not-enabled-for=Service account is not enabled for {{client}}
+create-protocol-mappers=Create Protocol Mappers
+create-protocol-mapper=Create Protocol Mapper
+protocol=Protocol
+protocol.tooltip=Protocol.
+id=ID
+mapper.name.tooltip=Name of the mapper.
+mapper.consent-required.tooltip=When granting temporary access, must the user consent to providing this data to the client?
+consent-text=Consent Text
+consent-text.tooltip=Text to display on consent page.
+mapper-type=Mapper Type
+
+# realm identity providers
+identity-providers=Identity Providers
+table-of-identity-providers=Table of identity providers
+add-provider.placeholder=Add provider...
+provider=Provider
+gui-order=GUI order
+redirect-uri=Redirect URI
+redirect-uri.tooltip=The redirect uri to use when configuring the identity provider.
+alias=Alias
+identity-provider.alias.tooltip=The alias uniquely identifies an identity provider and it is also used to build the redirect uri.
+identity-provider.enabled.tooltip=Enable/disable this identity provider.
+authenticate-by-default=Authenticate by Default
+identity-provider.authenticate-by-default.tooltip=Indicates if this provider should be tried by default for authentication even before displaying login screen.
+store-tokens=Store Tokens
+identity-provider.store-tokens.tooltip=Enable/disable if tokens must be stored after authenticating users.
+stored-tokens-readable=Stored Tokens Readable
+identity-provider.stored-tokens-readable.tooltip=Enable/disable if new users can read any stored tokens. This assigns the broker.read-token role.
+update-profile-on-first-login=Update Profile on First Login
+on=On
+on-missing-info=On missing info
+off=Off
+update-profile-on-first-login.tooltip=Define conditions under which a user has to update their profile during first-time login.
+trust-email=Trust Email
+trust-email.tooltip=If enabled then email provided by this provider is not verified even if verification is enabled for the realm.
+gui-order.tooltip=Number defining order of the provider in GUI (eg. on Login page).
+openid-connect-config=OpenID Connect Config
+openid-connect-config.tooltip=OIDC SP and external IDP configuration.
+authorization-url=Authorization URL
+authorization-url.tooltip=The Authorization Url.
+token-url=Token URL
+token-url.tooltip=The Token URL.
+logout-url=Logout URL
+identity-provider.logout-url.tooltip=End session endpoint to use to logout user from external IDP.
+backchannel-logout=Backchannel Logout
+backchannel-logout.tooltip=Does the external IDP support backchannel logout?
+user-info-url=User Info URL
+user-info-url.tooltip=The User Info Url. This is optional.
+identity-provider.client-id.tooltip=The client or client identifier registered within the identity provider.
+client-secret=Client Secret
+show-secret=Show secret
+hide-secret=Hide secret
+client-secret.tooltip=The client or client secret registered within the identity provider.
+issuer=Issuer
+issuer.tooltip=The issuer identifier for the issuer of the response. If not provided, no validation will be performed.
+default-scopes=Default Scopes
+identity-provider.default-scopes.tooltip=The scopes to be sent when asking for authorization. It can be a space-separated list of scopes. Defaults to 'openid'.
+prompt=Prompt
+unspecified.option=unspecified
+none.option=none
+consent.option=consent
+login.option=login
+select-account.option=select_account
+prompt.tooltip=Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
+validate-signatures=Validate Signatures
+identity-provider.validate-signatures.tooltip=Enable/disable signature validation of external IDP signatures.
+validating-public-key=Validating Public Key
+identity-provider.validating-public-key.tooltip=The public key in PEM format that must be used to verify external IDP signatures.
+import-external-idp-config=Import External IDP Config
+import-external-idp-config.tooltip=Allows you to load external IDP metadata from a config file or to download it from a URL.
+import-from-url=Import from URL
+identity-provider.import-from-url.tooltip=Import metadata from a remote IDP discovery descriptor.
+import-from-file=Import from file
+identity-provider.import-from-file.tooltip=Import metadata from a downloaded IDP discovery descriptor.
+saml-config=SAML Config
+identity-provider.saml-config.tooltip=SAML SP and external IDP configuration.
+single-signon-service-url=Single Sign-On Service URL
+saml.single-signon-service-url.tooltip=The Url that must be used to send authentication requests (SAML AuthnRequest).
+single-logout-service-url=Single Logout Service URL
+saml.single-logout-service-url.tooltip=The Url that must be used to send logout requests.
+nameid-policy-format=NameID Policy Format
+nameid-policy-format.tooltip=Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.
+http-post-binding-response=HTTP-POST Binding Response
+http-post-binding-response.tooltip=Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
+http-post-binding-for-authn-request=HTTP-POST Binding for AuthnRequest
+http-post-binding-for-authn-request.tooltip=Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.
+want-authn-requests-signed=Want AuthnRequests Signed
+want-authn-requests-signed.tooltip=Indicates whether the identity provider expects signed a AuthnRequest.
+force-authentication=Force Authentication
+identity-provider.force-authentication.tooltip=Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.
+validate-signature=Validate Signature
+saml.validate-signature.tooltip=Enable/disable signature validation of SAML responses.
+validating-x509-certificate=Validating X509 Certificate
+validating-x509-certificate.tooltip=The certificate in PEM format that must be used to check for signatures.
+saml.import-from-url.tooltip=Import metadata from a remote IDP SAML entity descriptor.
+social.client-id.tooltip=The client identifier registered with the identity provider.
+social.client-secret.tooltip=The client secret registered with the identity provider.
+social.default-scopes.tooltip=The scopes to be sent when asking for authorization. See documentation for possible values, separator and default value'.
+key=Key
+stackoverflow.key.tooltip=The Key obtained from Stack Overflow client registration.
+
+realms=Realms
+realm=Realm
+
+identity-provider-mappers=Identity Provider Mappers
+create-identity-provider-mapper=Create Identity Provider Mapper
+add-identity-provider-mapper=Add Identity Provider Mapper
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 8af3ef8..92a1702 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -14,16 +14,57 @@ var loadingTimer = -1;
angular.element(document).ready(function () {
var keycloakAuth = new Keycloak(configUrl);
+ function whoAmI(success, error) {
+ var req = new XMLHttpRequest();
+ req.open('GET', consoleBaseUrl + "/whoami", true);
+ req.setRequestHeader('Accept', 'application/json');
+ req.setRequestHeader('Authorization', 'bearer ' + keycloakAuth.token);
+
+ req.onreadystatechange = function () {
+ if (req.readyState == 4) {
+ if (req.status == 200) {
+ var data = JSON.parse(req.responseText);
+ success(data);
+ } else {
+ error();
+ }
+ }
+ }
+
+ req.send();
+ }
+
+ function hasAnyAccess(user) {
+ return user && user['realm_access'];
+ }
+
keycloakAuth.onAuthLogout = function() {
location.reload();
}
keycloakAuth.init({ onLoad: 'login-required' }).success(function () {
auth.authz = keycloakAuth;
- module.factory('Auth', function() {
- return auth;
+
+ auth.refreshPermissions = function(success, error) {
+ whoAmI(function(data) {
+ auth.user = data;
+ auth.loggedIn = true;
+ auth.hasAnyAccess = hasAnyAccess(data);
+
+ success();
+ }, function() {
+ error();
+ });
+ };
+
+ auth.refreshPermissions(function() {
+ module.factory('Auth', function() {
+ return auth;
+ });
+ angular.bootstrap(document, ["keycloak"]);
+ }, function() {
+ window.location.reload();
});
- angular.bootstrap(document, ["keycloak"]);
}).error(function () {
window.location.reload();
});
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 15dc679..d100df7 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -1,31 +1,9 @@
-module.controller('GlobalCtrl', function($scope, $http, Auth, WhoAmI, Current, $location, Notifications, ServerInfo) {
- $scope.addMessage = function() {
- Notifications.success("test");
- };
-
+module.controller('GlobalCtrl', function($scope, $http, Auth, Current, $location, Notifications, ServerInfo) {
$scope.authUrl = authUrl;
$scope.resourceUrl = resourceUrl;
$scope.auth = Auth;
$scope.serverInfo = ServerInfo.get();
- function hasAnyAccess() {
- var realmAccess = Auth.user && Auth.user['realm_access'];
- if (realmAccess) {
- for (var p in realmAccess){
- return true;
- }
- return false;
- } else {
- return false;
- }
- }
-
- WhoAmI.get(function (data) {
- Auth.user = data;
- Auth.loggedIn = true;
- Auth.hasAnyAccess = hasAnyAccess();
- });
-
function getAccess(role) {
if (!Current.realm) {
return false;
@@ -155,7 +133,7 @@ module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $l
}
});
-module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, WhoAmI, $location, $route, Dialog, Notifications, Auth, $modal) {
+module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, $location, $route, Dialog, Notifications, Auth, $modal) {
console.log('RealmCreateCtrl');
Current.realm = null;
@@ -193,22 +171,17 @@ module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $
}, true);
$scope.$watch('realm.realm', function() {
- if (create) {
$scope.realm.id = $scope.realm.realm;
- }
}, true);
$scope.save = function() {
var realmCopy = angular.copy($scope.realm);
Realm.create(realmCopy, function() {
- Realm.query(function(data) {
- Current.realms = data;
-
- WhoAmI.get(function(user) {
- Auth.user = user;
+ Notifications.success("The realm has been created.");
+ Auth.refreshPermissions(function() {
+ $scope.$apply(function() {
$location.url("/realms/" + realmCopy.realm);
- Notifications.success("The realm has been created.");
});
});
});
@@ -227,7 +200,7 @@ module.controller('ObjectModalCtrl', function($scope, object) {
$scope.object = object;
});
-module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, WhoAmI, Auth) {
+module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, Auth) {
$scope.createRealm = !realm.realm;
$scope.serverInfo = serverInfo;
@@ -272,11 +245,13 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
});
if (nameChanged) {
- WhoAmI.get(function(user) {
- Auth.user = user;
-
- $location.url("/realms/" + realmCopy.realm);
- Notifications.success("Your changes have been saved to the realm.");
+ Auth.refreshPermissions(function() {
+ Auth.refreshPermissions(function() {
+ Notifications.success("Your changes have been saved to the realm.");
+ $scope.$apply(function() {
+ $location.url("/realms/" + realmCopy.realm);
+ });
+ });
});
} else {
$location.url("/realms/" + realmCopy.realm);
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 03f4b99..b92fd83 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -184,10 +184,6 @@ module.factory('Notifications', function($rootScope, $timeout) {
return notifications;
});
-module.factory('WhoAmI', function($resource) {
- return $resource(consoleBaseUrl + '/whoami');
-});
-
module.factory('Realm', function($resource) {
return $resource(authUrl + '/admin/realms/:id', {
id : '@realm'
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
index 08c9938..085562d 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering.html
@@ -1,17 +1,17 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
<kc-tabs-client></kc-tabs-client>
<form class="form-horizontal" name="clusteringForm" novalidate kc-read-only="!access.manageClients">
- <legend><span class="text">Basic configuration</span></legend>
+ <legend><span class="text">{{:: 'basic-configuration' | translate}}</span></legend>
<fieldset >
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="nodeReRegistrationTimeout">Node Re-registration Timeout</label>
+ <label class="col-md-2 control-label" for="nodeReRegistrationTimeout">{{:: 'node-reregistration-timeout' | translate}}</label>
<div class="col-sm-5">
<div class="row">
<div class="col-md-6 form-inline">
@@ -19,42 +19,42 @@
max="31536000" data-ng-model="client.nodeReRegistrationTimeout"
id="nodeReRegistrationTimeout" name="nodeReRegistrationTimeout"/>
<select class="form-control" name="nodeReRegistrationTimeoutUnit" data-ng-model="client.nodeReRegistrationTimeoutUnit" >
- <option data-ng-selected="!client.nodeReRegistrationTimeoutUnit">Seconds</option>
- <option>Minutes</option>
- <option>Hours</option>
- <option>Days</option>
+ <option data-ng-selected="!client.nodeReRegistrationTimeoutUnit">{{:: 'seconds' | translate}}</option>
+ <option>{{:: 'minutes' | translate}}</option>
+ <option>{{:: 'hours' | translate}}</option>
+ <option>{{:: 'days' | translate}}</option>
</select>
</div>
</div>
</div>
- <kc-tooltip>Interval to specify max time for registered clients cluster nodes to re-register. If cluster node won't send re-registration request to Keycloak within this time, it will be unregistered from Keycloak</kc-tooltip>
+ <kc-tooltip>{{:: 'node-reregistration-timeout.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button data-kc-save data-ng-disabled="!changed">Save</button>
- <button data-kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button data-kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button data-kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</fieldset>
<fieldset>
- <legend><span class="text">Registered cluster nodes</span></legend>
+ <legend><span class="text">{{:: 'registered-cluster-nodes' | translate}}</span></legend>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th class="kc-table-actions" colspan="5" data-ng-show="access.manageClients">
<div class="pull-right">
<a class="btn btn-default" tooltip="Manually register cluster node. This is usually not needed as cluster node should be registered automatically by adapter"
- tooltip-trigger="mouseover mouseout" tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Register node manually</a>
- <a class="btn btn-default" data-ng-click="testNodesAvailable()" data-ng-show="nodeRegistrations && nodeRegistrations.length > 0">Test cluster availability</a>
+ tooltip-trigger="mouseover mouseout" tooltip-placement="bottom" href="#/register-node/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'register-node-manually' | translate}}</a>
+ <a class="btn btn-default" data-ng-click="testNodesAvailable()" data-ng-show="nodeRegistrations && nodeRegistrations.length > 0">{{:: 'test-cluster-availability' | translate}}</a>
</div>
</th>
</tr>
<tr data-ng-hide="!nodeRegistrations || nodeRegistrations.length == 0">
- <th>Node host</th>
- <th>Last registration</th>
- <th colspan="2">Actions</th>
+ <th>{{:: 'node-host' | translate}}</th>
+ <th>{{:: 'last-registration' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -62,14 +62,14 @@
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering/{{node.host}}">{{node.host}}</a></td>
<td>{{node.lastRegistration}}</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/clustering/{{node.host}}">Edit</button>
+ <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/clustering/{{node.host}}">{{:: 'edit' | translate}}</button>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" data-ng-click="removeNode(node)">Delete</button>
+ <button class="btn btn-default btn-block btn-sm" data-ng-click="removeNode(node)">{{:: 'delete' | translate}}</button>
</td>
</tr>
<tr data-ng-show="!nodeRegistrations || nodeRegistrations.length == 0">
- <td class="text-muted">No registered cluster nodes available</td>
+ <td class="text-muted">{{:: 'no-registered-cluster-nodes' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
index 442fa0d..91f909f 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-clustering-node.html
@@ -1,13 +1,13 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Cluster Nodes</a></li>
- <li data-ng-show="create">Add Node</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'cluster-nodes' | translate}}</a></li>
+ <li data-ng-show="create">{{:: 'add-node' | translate}}</li>
<li data-ng-hide="create">{{node.host|capitalize}}</li>
</ol>
- <h1 data-ng-show="create">Add Node</h1>
+ <h1 data-ng-show="create">{{:: 'add-node' | translate}}</h1>
<h1 data-ng-hide="create">
{{node.host|capitalize}}
<i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="unregisterNode()"></i>
@@ -21,14 +21,14 @@
</div>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="lastRegistration">Last Registration</label>
+ <label class="col-md-2 control-label" for="lastRegistration">{{:: 'last-registration' | translate}}</label>
<div class="col-sm-6">
<input ng-disabled="true" class="form-control" type="text" id="lastRegistration" name="lastRegistration" data-ng-model="node.lastRegistration">
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button data-kc-save data-ng-show="create">Save</button>
+ <button data-kc-save data-ng-show="create">{{:: 'save' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
index 18f6a4a..96f16ca 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -10,7 +10,7 @@
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientAuthenticatorType"> Client Authenticator</label>
+ <label class="col-md-2 control-label" for="clientAuthenticatorType"> {{:: 'client-authenticator' | translate}}</label>
<div class="col-md-2">
<div>
<select class="form-control" id="clientAuthenticatorType"
@@ -20,7 +20,7 @@
</select>
</div>
</div>
- <kc-tooltip>Client Authenticator used for authentication this client against Keycloak server</kc-tooltip>
+ <kc-tooltip>{{:: 'client-authenticator.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
index e249914..631939c 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-generic.html
@@ -6,8 +6,8 @@
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
index 3da4d7d..aa03203 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt.html
@@ -1,20 +1,20 @@
<div>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSignedJWTCtrl">
<div class="form-group">
- <label class="col-md-2 control-label" for="signingCert">Certificate</label>
- <kc-tooltip>Client Certificate for validate JWT issued by client and signed by Client private key from your keystore.</kc-tooltip>
+ <label class="col-md-2 control-label" for="signingCert">{{:: 'certificate' | translate}}</label>
+ <kc-tooltip>{{:: 'certificate.tooltip' | translate}}</kc-tooltip>
<div class="col-sm-10" data-ng-show="signingKeyInfo.certificate">
<textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5" kc-select-action="click" readonly>{{signingKeyInfo.certificate}}</textarea>
</div>
<div class="col-sm-10" data-ng-hide="signingKeyInfo.certificate">
- No client certificate configured
+ {{:: 'no-client-certificate-configured' | translate}}
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
- <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">Generate new keys and certificate</button>
- <button class="btn btn-default" type="submit" data-ng-click="importCertificate()">Import certificate</button>
+ <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'gen-new-keys-and-cert' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-click="importCertificate()">{{:: 'import-certificate' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
index 08c1dcd..7eb5924 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-export.html
@@ -1,18 +1,18 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">Credentials</a></li>
- <li class="active">Generate Client Private Key</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">{{:: 'credentials' | translate}}</a></li>
+ <li class="active">{{:: 'gen-client-private-key' | translate}}</li>
</ol>
- <h1>Generate Private Key</h1>
+ <h1>{{:: 'generate-private-key' | translate}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="form-group col-sm-10">
<div class="form-group">
- <label class="col-md-2 control-label" for="downloadKeyFormat">Archive Format</label>
+ <label class="col-md-2 control-label" for="downloadKeyFormat">{{:: 'archive-format' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="downloadKeyFormat"
@@ -21,33 +21,33 @@
</select>
</div>
</div>
- <kc-tooltip>Java keystore or PKCS12 archive format.</kc-tooltip>
+ <kc-tooltip>{{:: 'archive-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="keyAlias">Key Alias</label>
+ <label class="col-md-2 control-label" for="keyAlias">{{:: 'key-alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="keyAlias" name="keyAlias" data-ng-model="jks.keyAlias" autofocus required>
</div>
- <kc-tooltip>Archive alias for your private key and certificate.</kc-tooltip>
+ <kc-tooltip>{{:: 'key-alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="keyPassword">Key Password</label>
+ <label class="col-md-2 control-label" for="keyPassword">{{:: 'key-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
</div>
- <kc-tooltip>Password to access the private key in the archive</kc-tooltip>
+ <kc-tooltip>{{:: 'key-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storePassword">Store Password</label>
+ <label class="col-md-2 control-label" for="storePassword">{{:: 'store-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
</div>
- <kc-tooltip>Password to access the archive itself</kc-tooltip>
+ <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-primary" type="submit" data-ng-click="download()">Generate and Download</button>
- <button class="btn btn-default" type="submit" data-ng-click="cancel()">Cancel</button>
+ <button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'generate-and-download' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
index a3556ea..1544aea 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-jwt-key-import.html
@@ -1,18 +1,18 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">Credentials</a></li>
- <li class="active">Client Certificate Import</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">{{:: 'credentials' | translate}}</a></li>
+ <li class="active">{{:: 'client-certificate-import' | translate}}</li>
</ol>
- <h1>Import Client Certificate</h1>
+ <h1>{{:: 'import-client-certificate' | translate}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadKeyFormat">Archive Format</label>
+ <label class="col-md-2 control-label" for="uploadKeyFormat">{{:: 'archive-format' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="uploadKeyFormat"
@@ -21,27 +21,27 @@
</select>
</div>
</div>
- <kc-tooltip>Java keystore or PKCS12 archive format.</kc-tooltip>
+ <kc-tooltip>{{:: 'archive-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadKeyAlias">Key Alias</label>
+ <label class="col-md-2 control-label" for="uploadKeyAlias">{{:: 'key-alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="uploadKeyAlias" name="uploadKeyAlias" data-ng-model="uploadKeyAlias" autofocus required>
</div>
- <kc-tooltip>Archive alias for your certificate.</kc-tooltip>
+ <kc-tooltip>{{:: 'jwt-import.key-alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadStorePassword">Store Password</label>
+ <label class="col-md-2 control-label" for="uploadStorePassword">{{:: 'store-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="uploadStorePassword" name="uploadStorePassword" data-ng-model="uploadStorePassword" autofocus required>
</div>
- <kc-tooltip>Password to access the archive itself</kc-tooltip>
+ <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label">Import File </label>
+ <label class="col-md-2 control-label">{{:: 'import-file' | translate}} </label>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
- <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+ <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
@@ -51,8 +51,8 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
- <button type="submit" data-ng-click="uploadFile()" data-ng-disabled="files.length == 0" class="btn btn-primary">Import</button>
- <button type="submit" data-ng-click="cancel()" class="btn btn-default">Cancel</button>
+ <button type="submit" data-ng-click="uploadFile()" data-ng-disabled="files.length == 0" class="btn btn-primary">{{:: 'import' | translate}}</button>
+ <button type="submit" data-ng-click="cancel()" class="btn btn-default">{{:: 'cancel' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
index 3062e8f..2bd53db 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-credentials-secret.html
@@ -1,14 +1,14 @@
<div>
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageClients" data-ng-controller="ClientSecretCtrl">
<div class="form-group">
- <label class="col-md-2 control-label" for="secret">Secret</label>
+ <label class="col-md-2 control-label" for="secret">{{:: 'secret' | translate}}</label>
<div class="col-sm-6">
<div class="row">
<div class="col-sm-6">
<input readonly kc-select-action="click" class="form-control" type="text" id="secret" name="secret" data-ng-model="secret">
</div>
<div class="col-sm-6" data-ng-show="access.manageClients">
- <button type="submit" data-ng-click="changePassword()" class="btn btn-default">Regenerate Secret</button>
+ <button type="submit" data-ng-click="changePassword()" class="btn btn-default">{{:: 'regenerate-secret' | translate}}</button>
</div>
</div>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index cc4ea82..5d2b2dd 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -1,8 +1,8 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
- <li data-ng-show="create">Add Client</li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li data-ng-show="create">{{:: 'add-client' | translate}}</li>
<li data-ng-hide="create">{{client.clientId}}</li>
</ol>
@@ -11,56 +11,56 @@
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group" data-ng-show="create">
- <label for="name" class="col-sm-2 control-label">Import</label>
+ <label for="name" class="col-sm-2 control-label">{{:: 'import' | translate}}</label>
<div class="col-md-6" data-ng-hide="importing">
- <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+ <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" kc-on-read-file="importFile($fileContent)">
</div>
<div class="col-md-6" data-ng-show="importing">
- <button class="btn btn-default" data-ng-click="viewImportDetails()">View details</button>
- <button class="btn btn-default" data-ng-click="reset()">Clear import</button>
+ <button class="btn btn-default" data-ng-click="viewImportDetails()">{{:: 'view-details' | translate}}</button>
+ <button class="btn btn-default" data-ng-click="reset()">{{:: 'clear-import' | translate}}</button>
</div>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="clientId">Client ID <span class="required" data-ng-show="create">*</span></label>
+ <label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}} <span class="required" data-ng-show="create">*</span></label>
<div class="col-sm-6">
<input class="form-control" type="text" id="clientId" name="clientId" data-ng-model="client.clientId" autofocus required>
</div>
- <kc-tooltip>Specifies ID referenced in URI and tokens. For example 'my-client'</kc-tooltip>
+ <kc-tooltip>{{:: 'client-id.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="name">Name </label>
+ <label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} </label>
<div class="col-sm-6">
<input class="form-control" type="text" id="name" name="name" data-ng-model="client.name" autofocus>
</div>
- <kc-tooltip>Specifies display name of the client. For example 'My Client'. Supports keys for localized values as well. For example: ${my_client}</kc-tooltip>
+ <kc-tooltip>{{:: 'client.name.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block">
- <label class="col-md-2 control-label" for="enabled">Enabled</label>
+ <label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="client.enabled" name="enabled" id="enabled" onoffswitch />
+ <input ng-model="client.enabled" name="enabled" id="enabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Disabled clients cannot initiate a login or have obtain access tokens.</kc-tooltip>
+ <kc-tooltip>{{:: 'client.enabled.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block">
- <label class="col-md-2 control-label" for="consentRequired">Consent Required</label>
+ <label class="col-md-2 control-label" for="consentRequired">{{:: 'consent-required' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="client.consentRequired" name="consentRequired" id="consentRequired" onoffswitch />
+ <input ng-model="client.consentRequired" name="consentRequired" id="consentRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>If enabled users have to consent to client access.</kc-tooltip>
+ <kc-tooltip>{{:: 'consent-required.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block">
- <label class="col-md-2 control-label" for="directGrantsOnly">Direct Grants Only</label>
+ <label class="col-md-2 control-label" for="directGrantsOnly">{{:: 'direct-grants-only' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="client.directGrantsOnly" name="directGrantsOnly" id="directGrantsOnly" onoffswitch />
+ <input ng-model="client.directGrantsOnly" name="directGrantsOnly" id="directGrantsOnly" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>When enabled, client can only obtain grants from grant REST API.</kc-tooltip>
+ <kc-tooltip>{{:: 'direct-grants-only.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="protocol">Client Protocol</label>
+ <label class="col-md-2 control-label" for="protocol">{{:: 'client-protocol' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="protocol"
@@ -70,11 +70,10 @@
</select>
</div>
</div>
- <kc-tooltip>'OpenID connect' allows Clients to verify the identity of the End-User based on the authentication performed by an Authorization Server.
- 'SAML' enables web-based authentication and authorization scenarios including cross-domain single sign-on (SSO) and uses security tokens containing assertions to pass information.</kc-tooltip>
+ <kc-tooltip>{{:: 'client-protocol.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="protocol == 'openid-connect'">
- <label class="col-md-2 control-label" for="accessType">Access Type</label>
+ <label class="col-md-2 control-label" for="accessType">{{:: 'access-type' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="accessType"
@@ -84,38 +83,38 @@
</select>
</div>
</div>
- <kc-tooltip>'Confidential' clients require a secret to initiate login protocol. 'Public' clients do not require a secret. 'Bearer-only' clients are web services that never initiate a login.</kc-tooltip>
+ <kc-tooltip>{{:: 'access-type.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="protocol == 'openid-connect' && !client.publicClient && !client.bearerOnly">
- <label class="col-md-2 control-label" for="serviceAccountsEnabled">Service Accounts Enabled</label>
- <kc-tooltip>Allows you to authenticate this client to Keycloak and retrieve access token dedicated to this client.</kc-tooltip>
+ <label class="col-md-2 control-label" for="serviceAccountsEnabled">{{:: 'service-accounts-enabled' | translate}}</label>
+ <kc-tooltip>{{:: 'service-accounts-enabled.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
- <input ng-model="client.serviceAccountsEnabled" name="serviceAccountsEnabled" id="serviceAccountsEnabled" onoffswitch />
+ <input ng-model="client.serviceAccountsEnabled" name="serviceAccountsEnabled" id="serviceAccountsEnabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlServerSignature">Include AuthnStatement</label>
+ <label class="col-md-2 control-label" for="samlServerSignature">{{:: 'include-authnstatement' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlAuthnStatement" ng-click="switchChange()" name="samlAuthnStatement" id="samlAuthnStatement" onoffswitch />
+ <input ng-model="samlAuthnStatement" ng-click="switchChange()" name="samlAuthnStatement" id="samlAuthnStatement" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Should a statement specifying the method and timestamp be included in login responses?</kc-tooltip>
+ <kc-tooltip>{{:: 'include-authnstatement.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlServerSignature">Sign Documents</label>
+ <label class="col-md-2 control-label" for="samlServerSignature">{{:: 'sign-documents' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlServerSignature" ng-click="switchChange()" name="samlServerSignature" id="samlServerSignature" onoffswitch />
+ <input ng-model="samlServerSignature" ng-click="switchChange()" name="samlServerSignature" id="samlServerSignature" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Should SAML documents be signed by the realm?</kc-tooltip>
+ <kc-tooltip>{{:: 'sign-documents.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlAssertionSignature">Sign Assertions</label>
+ <label class="col-md-2 control-label" for="samlAssertionSignature">{{:: 'sign-assertions' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlAssertionSignature" ng-click="switchChange()" name="samlAssertionSignature" id="samlAssertionSignature" onoffswitch />
+ <input ng-model="samlAssertionSignature" ng-click="switchChange()" name="samlAssertionSignature" id="samlAssertionSignature" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Should assertions inside SAML documents be signed? This setting isn't needed if document is already being signed.</kc-tooltip>
+ <kc-tooltip>{{:: 'sign-assertions.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
- <label class="col-md-2 control-label" for="signatureAlgorithm">Signature Algorithm</label>
+ <label class="col-md-2 control-label" for="signatureAlgorithm">{{:: 'signature-algorithm' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="signatureAlgorithm"
@@ -125,10 +124,10 @@
</select>
</div>
</div>
- <kc-tooltip>The signature algorithm to use to sign documents.</kc-tooltip>
+ <kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="(samlAssertionSignature || samlServerSignature) && protocol == 'saml'">
- <label class="col-md-2 control-label" for="canonicalization">Canonicalization Method</label>
+ <label class="col-md-2 control-label" for="canonicalization">{{:: 'canonicalization-method' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="canonicalization"
@@ -137,45 +136,45 @@
</select>
</div>
</div>
- <kc-tooltip>Canonicalization Method for XML signatures.</kc-tooltip>
+ <kc-tooltip>{{:: 'canonicalization-method.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlEncrypt">Encrypt Assertions</label>
+ <label class="col-md-2 control-label" for="samlEncrypt">{{:: 'encrypt-assertions' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlEncrypt" ng-click="switchChange()" name="samlEncrypt" id="samlEncrypt" onoffswitch />
+ <input ng-model="samlEncrypt" ng-click="switchChange()" name="samlEncrypt" id="samlEncrypt" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Should SAML assertions be encrypted with client's public key using AES?</kc-tooltip>
+ <kc-tooltip>{{:: 'encrypt-assertions.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlClientSignature">Client Signature Required</label>
+ <label class="col-md-2 control-label" for="samlClientSignature">{{:: 'client-signature-required' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlClientSignature" ng-click="switchChange()" name="samlClientSignature" id="samlClientSignature" onoffswitch />
+ <input ng-model="samlClientSignature" ng-click="switchChange()" name="samlClientSignature" id="samlClientSignature" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Will the client sign their saml requests and responses? And should they be validated?</kc-tooltip>
+ <kc-tooltip>{{:: 'client-signature-required.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlForcePostBinding">Force POST Binding</label>
+ <label class="col-md-2 control-label" for="samlForcePostBinding">{{:: 'force-post-binding' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlForcePostBinding" ng-click="switchChange()" name="samlForcePostBinding" id="samlForcePostBinding" onoffswitch />
+ <input ng-model="samlForcePostBinding" ng-click="switchChange()" name="samlForcePostBinding" id="samlForcePostBinding" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Always use POST binding for responses.</kc-tooltip>
+ <kc-tooltip>{{:: 'force-post-binding.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="frontchannelLogout">Front Channel Logout</label>
+ <label class="col-md-2 control-label" for="frontchannelLogout">{{:: 'front-channel-logout' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="client.frontchannelLogout" name="frontchannelLogout" id="frontchannelLogout" onoffswitch />
+ <input ng-model="client.frontchannelLogout" name="frontchannelLogout" id="frontchannelLogout" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>When true, logout requires a browser redirect to client. When false, server performs a background invocation for logout.</kc-tooltip>
+ <kc-tooltip>{{:: 'front-channel-logout.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlForceNameIdFormat">Force Name ID Format</label>
+ <label class="col-md-2 control-label" for="samlForceNameIdFormat">{{:: 'force-name-id-format' | translate}}</label>
<div class="col-sm-6">
- <input ng-model="samlForceNameIdFormat" ng-click="switchChange()" name="samlForceNameIdFormat" id="samlForceNameIdFormat" onoffswitch />
+ <input ng-model="samlForceNameIdFormat" ng-click="switchChange()" name="samlForceNameIdFormat" id="samlForceNameIdFormat" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
- <kc-tooltip>Ignore requested NameID subject format and use admin console configured one.</kc-tooltip>
+ <kc-tooltip>{{:: 'force-name-id-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="samlNameIdFormat">Name ID Format</label>
+ <label class="col-md-2 control-label" for="samlNameIdFormat">{{:: 'name-id-format' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="samlNameIdFormat"
@@ -185,19 +184,19 @@
</select>
</div>
</div>
- <kc-tooltip>The name ID format to use for the subject.</kc-tooltip>
+ <kc-tooltip>{{:: 'name-id-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="!client.bearerOnly">
- <label class="col-md-2 control-label" for="rootUrl">Root URL</label>
+ <label class="col-md-2 control-label" for="rootUrl">{{:: 'root-url' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" name="rootUrl" id="rootUrl" data-ng-model="client.rootUrl">
</div>
- <kc-tooltip>Root URL appended to relative URLs</kc-tooltip>
+ <kc-tooltip>{{:: 'root-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-hide="client.bearerOnly || client.directGrantsOnly">
- <label class="col-md-2 control-label" for="newRedirectUri"><span class="required" data-ng-show="protocol != 'saml'">*</span> Valid Redirect URIs</label>
+ <label class="col-md-2 control-label" for="newRedirectUri"><span class="required" data-ng-show="protocol != 'saml'">*</span> {{:: 'valid-redirect-uris' | translate}}</label>
<div class="col-sm-6">
<div class="input-group" ng-repeat="(i, redirectUri) in client.redirectUris track by $index">
@@ -215,48 +214,48 @@
</div>
</div>
- <kc-tooltip>Valid URI pattern a browser can redirect to after a successful login or logout. Simple wildcards are allowed i.e. 'http://example.com/*'. Relative path can be specified too i.e. /my/relative/path/*. Relative paths will generate a redirect URI using the request's host and port. For SAML, you must set valid URI patterns if you are relying on the consumer service URL embedded with the login request.</kc-tooltip>
+ <kc-tooltip>{{:: 'valid-redirect-uris.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="!client.bearerOnly && !create">
- <label class="col-md-2 control-label" for="baseUrl">Base URL</label>
+ <label class="col-md-2 control-label" for="baseUrl">{{:: 'base-url' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" name="baseUrl" id="baseUrl" data-ng-model="client.baseUrl">
</div>
- <kc-tooltip>Default URL to use when the auth server needs to redirect or link back to the client.</kc-tooltip>
+ <kc-tooltip>{{:: 'base-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-hide="create || protocol == 'saml'">
- <label class="col-md-2 control-label" for="adminUrl">Admin URL</label>
+ <label class="col-md-2 control-label" for="adminUrl">{{:: 'admin-url' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" name="adminUrl" id="adminUrl"
data-ng-model="client.adminUrl">
</div>
- <kc-tooltip>URL to the admin interface of the client. Set this if the client supports the adapter REST API. This REST API allows the auth server to push revocation policies and other adminstrative tasks. Usually this is set to the base URL of the client.</kc-tooltip>
+ <kc-tooltip>{{:: 'admin-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="masterSamlUrl">Master SAML Processing URL</label>
+ <label class="col-md-2 control-label" for="masterSamlUrl">{{:: 'master-saml-processing-url' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" type="text" name="masterSamlUrl" id="masterSamlUrl"
data-ng-model="client.adminUrl">
</div>
- <kc-tooltip>If configured, this URL will be used for every binding to both the SP's Assertion Consumer and Single Logout Services. This can be individually overiden for each binding and service in the Fine Grain SAML Endpoint Configuration.</kc-tooltip>
+ <kc-tooltip>{{:: 'master-saml-processing-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="urlReferenceName">IDP Initiated SSO URL Name</label>
+ <label class="col-md-2 control-label" for="urlReferenceName">{{:: 'idp-sso-url-ref' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_idp_initiated_sso_url_name" class="form-control" type="text" name="urlReferenceName" id="urlReferenceName" />
</div>
- <kc-tooltip>URL fragment name to reference client when you want to do IDP Initiated SSO. Leaving this empty will disable IDP Initiated SSO. The URL you will reference from your browser will be: {server-root}/realms/{realm}/protocol/saml/clients/{client-url-name}</kc-tooltip>
+ <kc-tooltip>{{:: 'idp-sso-url-ref.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="idpInitiatedRelayState">IDP Initiated SSO Relay State</label>
+ <label class="col-md-2 control-label" for="idpInitiatedRelayState">{{:: 'idp-sso-relay-state' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_idp_initiated_sso_relay_state" class="form-control" type="text" name="idpInitiatedRelayState" id="idpInitiatedRelayState" />
</div>
- <kc-tooltip>Relay state you want to send with SAML request when you want to do IDP Initiated SSO.</kc-tooltip>
+ <kc-tooltip>{{:: 'idp-sso-relay-state.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="!client.bearerOnly && !create && protocol == 'openid-connect'">
- <label class="col-md-2 control-label" for="newWebOrigin">Web Origins</label>
+ <label class="col-md-2 control-label" for="newWebOrigin">{{:: 'web-origins' | translate}}</label>
<div class="col-sm-6">
<div class="input-group" ng-repeat="(i, webOrigin) in client.webOrigins track by $index">
@@ -274,49 +273,49 @@
</div>
</div>
- <kc-tooltip>Allowed CORS origins. To permit all origins of Valid Redirect URIs add '+'. To permit all origins add '*'.</kc-tooltip>
+ <kc-tooltip>{{:: 'web-origins.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset data-ng-show="protocol == 'saml'">
- <legend collapsed><span class="text">Fine Grain SAML Endpoint Configuration</span> <kc-tooltip>Expand this section to configure exact URLs for Assertion Consumer and Single Logout Service.</kc-tooltip></legend>
+ <legend collapsed><span class="text">{{:: 'fine-saml-endpoint-conf' | translate}}</span> <kc-tooltip>{{:: 'fine-saml-endpoint-conf.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="consumerServicePost">Assertion Consumer Service POST Binding URL</label>
+ <label class="col-md-2 control-label" for="consumerServicePost">{{:: 'assertion-consumer-post-binding-url' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_assertion_consumer_url_post" class="form-control" type="text" name="consumerServicePost" id="consumerServicePost" />
</div>
- <kc-tooltip>SAML POST Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.</kc-tooltip>
+ <kc-tooltip>{{:: 'assertion-consumer-post-binding-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="consumerServiceRedirect">Assertion Consumer Service Redirect Binding URL</label>
+ <label class="col-md-2 control-label" for="consumerServiceRedirect">{{:: 'assertion-consumer-redirect-binding-url' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_assertion_consumer_url_redirect" class="form-control" type="text" name="consumerServiceRedirect" id="consumerServiceRedirect" />
</div>
- <kc-tooltip>SAML Redirect Binding URL for the client's assertion consumer service (login responses). You can leave this blank if you do not have a URL for this binding.</kc-tooltip>
+ <kc-tooltip>{{:: 'assertion-consumer-redirect-binding-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="logoutPostBinding">Logout Service POST Binding URL</label>
+ <label class="col-md-2 control-label" for="logoutPostBinding">{{:: 'logout-service-post-binding-url' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_single_logout_service_url_post" class="form-control" type="text" name="logoutPostBinding" id="logoutPostBinding" />
</div>
- <kc-tooltip>SAML POST Binding URL for the client's single logout service. You can leave this blank if you are using a different binding</kc-tooltip>
+ <kc-tooltip>{{:: 'logout-service-post-binding-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix block" data-ng-show="protocol == 'saml'">
- <label class="col-md-2 control-label" for="logoutPostBinding">Logout Service Redirect Binding URL</label>
+ <label class="col-md-2 control-label" for="logoutPostBinding">{{:: 'logout-service-redir-binding-url' | translate}}</label>
<div class="col-sm-6">
<input ng-model="client.attributes.saml_single_logout_service_url_redirect" class="form-control" type="text" name="logoutRedirectBinding" id="logoutRedirectBinding" />
</div>
- <kc-tooltip>SAML Redirect Binding URL for the client's single logout service. You can leave this blank if you are using a different binding.</kc-tooltip>
+ <kc-tooltip>{{:: 'logout-service-redir-binding-url.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-import.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-import.html
index 910f1ce..18ec93c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-import.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-import.html
@@ -1,31 +1,31 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
- <li>Import Client</li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+ <li>{{:: 'import-client' | translate}}</li>
</ol>
- <h1>Import Client</h1>
+ <h1>{{:: 'import-client' | translate}}</h1>
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset class="border-top">
<div class="form-group input-select">
- <label class="col-md-2 control-label" for="configFormats">Format Option</label>
+ <label class="col-md-2 control-label" for="configFormats">{{:: 'format-option' | translate}}</label>
<div class="col-md-6">
<div class="input-group">
<div>
<select class="form-control" id="configFormats" name="configFormats" ng-model="configFormat" ng-options="format.name for format in configFormats">
- <option value="" selected> Select a Format </option>
+ <option value="" selected> {{:: 'select-format' | translate}} </option>
</select>
</div>
</div>
</div>
</div>
<div class="form-group">
- <label class="col-md-2 control-label">Import File </label>
+ <label class="col-md-2 control-label">{{:: 'import-file' | translate}} </label>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
- <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+ <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
@@ -35,8 +35,8 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="files.length > 0">
- <button type="submit" data-ng-click="uploadFile()" class="btn btn-primary">Import</button>
- <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-default">Cancel</button>
+ <button type="submit" data-ng-click="uploadFile()" class="btn btn-primary">{{:: 'import' | translate}}</button>
+ <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-default">{{:: 'cancel' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
index 375343a..2e3c578 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-installation.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -10,12 +10,12 @@
<form class="form" name="realmForm" novalidate>
<fieldset>
<div class="form-group input-select">
- <label class="col-md-1 control-label" for="configFormats">Format Option</label>
+ <label class="col-md-1 control-label" for="configFormats">{{:: 'format-option' | translate}}</label>
<div class="col-md-6">
<div class="input-group">
<div>
<select class="form-control" id="configFormats" name="configFormats" ng-change="changeFormat()" ng-model="configFormat" ng-options="a for a in configFormats">
- <option value="" selected> Select a Format </option>
+ <option value="" selected> {{:: 'select-a-format' | translate}} </option>
</select>
</div>
</div>
@@ -25,7 +25,7 @@
<fieldset class="margin-top">
<div class="form-group" ng-show="installation">
<div class="col-sm-12">
- <a class="btn btn-primary btn-lg" data-ng-click="download()" type="submit" ng-show="installation">Download</a>
+ <a class="btn btn-primary btn-lg" data-ng-click="download()" type="submit" ng-show="installation">{{:: 'download' | translate}}</a>
<textarea class="form-control" rows="20" kc-select-action="click">{{installation}}</textarea>
</div>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
index bd1faa3..6f7e125 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-keys.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
index dfa3e00..9f08776 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-list.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>
- <span>Clients</span>
- <kc-tooltip>Clients are trusted browser apps and web services in a realm. These clients can request a login. You can also define client specific roles.</kc-tooltip>
+ <span>{{:: 'clients' | translate}}</span>
+ <kc-tooltip>{{:: 'clients.tooltip' | translate}}</kc-tooltip>
</h1>
<table class="table table-striped table-bordered">
@@ -11,7 +11,7 @@
<div class="form-inline">
<div class="form-group">
<div class="input-group">
- <input type="text" placeholder="Search..." data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+ <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.clientId" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
@@ -19,37 +19,37 @@
</div>
<div class="pull-right" data-ng-show="access.manageClients">
- <a id="createClient" class="btn btn-default" href="#/create/client/{{realm.realm}}">Create</a>
- <a id="importClient" class="btn btn-default" href="#/import/client/{{realm.realm}}" data-ng-show="importButton">Import</a>
+ <a id="createClient" class="btn btn-default" href="#/create/client/{{realm.realm}}">{{:: 'create' | translate}}</a>
+ <a id="importClient" class="btn btn-default" href="#/import/client/{{realm.realm}}" data-ng-show="importButton">{{:: 'import' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr data-ng-hide="clients.length == 0">
- <th>Client ID</th>
- <th>Enabled</th>
- <th>Base URL</th>
- <th colspan="2">Actions</th>
+ <th>{{:: 'client-id' | translate}}</th>
+ <th>{{:: 'enabled' | translate}}</th>
+ <th>{{:: 'base-url' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="client in clients | filter:search | orderBy:'clientId'">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></td>
- <td>{{client.enabled}}</td>
+ <td translate="{{client.enabled}}"></td>
<td ng-class="{'text-muted': !client.baseUrl}">
<a href="{{client.rootUrl}}{{client.baseUrl}}" target="_blank" data-ng-show="client.baseUrl">{{client.rootUrl}}{{client.baseUrl}}</a>
- <span data-ng-hide="client.baseUrl">Not defined</span>
+ <span data-ng-hide="client.baseUrl">{{:: 'not-defined' | translate}}</span>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">Edit</button>
+ <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'edit' | translate}}</button>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" data-ng-click="removeClient(client)">Delete</button>
+ <button class="btn btn-default btn-block btn-sm" data-ng-click="removeClient(client)">{{:: 'delete' | translate}}</button>
</td>
</tr>
<tr data-ng-show="(clients | filter:search).length == 0">
- <td class="text-muted" colspan="3" data-ng-show="search.clientId">No results</td>
- <td class="text-muted" colspan="3" data-ng-hide="search.clientId">No clients available</td>
+ <td class="text-muted" colspan="3" data-ng-show="search.clientId">{{:: 'no-results' | translate}}</td>
+ <td class="text-muted" colspan="3" data-ng-hide="search.clientId">{{:: 'no-clients-available' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
index 3ef2ed3..23f7f97 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -14,7 +14,7 @@
<div class="form-inline">
<div class="form-group">
<div class="input-group">
- <input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+ <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
@@ -22,17 +22,17 @@
</div>
<div class="pull-right" data-ng-show="access.manageClients">
- <a class="btn btn-default" href="#/create/client/{{realm.realm}}/{{client.id}}/mappers">Create</a>
- <a class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/add-mappers">Add Builtin</a>
+ <a class="btn btn-default" href="#/create/client/{{realm.realm}}/{{client.id}}/mappers">{{:: 'create' | translate}}</a>
+ <a class="btn btn-default" href="#/realms/{{realm.realm}}/clients/{{client.id}}/add-mappers">{{:: 'add-builtin' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
- <th>Name</th>
- <th>Category</th>
- <th>Type</th>
- <th colspan="2">Actions</th>
+ <th>{{:: 'name' | translate}}</th>
+ <th>{{:: 'category' | translate}}</th>
+ <th>{{:: 'type' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -41,14 +41,14 @@
<td>{{mapperTypes[mapper.protocolMapper].category}}</td>
<td>{{mapperTypes[mapper.protocolMapper].name}}</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/mappers/{{mapper.id}}">Edit</button>
+ <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/mappers/{{mapper.id}}">{{:: 'edit' | translate}}</button>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" data-ng-click="removeMapper(mapper)">Delete</button>
+ <button class="btn btn-default btn-block btn-sm" data-ng-click="removeMapper(mapper)">{{:: 'delete' | translate}}</button>
</td>
</tr>
<tr data-ng-show="mappers.length == 0">
- <td>No mappers available</td>
+ <td>{{:: 'no-mappers-available' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
index f00ca3b..4237f7d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-mappers-add.html
@@ -1,13 +1,13 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">Mappers</a></li>
- <li class="active">Add Builtin Protocol Mappers</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">{{:: 'mappers' | translate}}</a></li>
+ <li class="active">{{:: 'add-builtin-protocol-mappers' | translate}}</li>
</ol>
- <h1>Add Builtin Protocol Mapper</h1>
+ <h1>{{:: 'add-builtin-protocol-mapper' | translate}}</h1>
<table class="table table-striped table-bordered">
<thead>
@@ -16,7 +16,7 @@
<div class="form-inline">
<div class="form-group">
<div class="input-group">
- <input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+ <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
@@ -26,10 +26,10 @@
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
- <th>Name</th>
- <th>Category</th>
- <th>Type</th>
- <th>Add</th>
+ <th>{{:: 'name' | translate}}</th>
+ <th>{{:: 'category' | translate}}</th>
+ <th>{{:: 'type' | translate}}</th>
+ <th>{{:: 'add' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -40,13 +40,13 @@
<td><input type="checkbox" ng-model="mapper.isChecked"></td>
</tr>
<tr data-ng-show="mappers.length == 0">
- <td>No mappers available</td>
+ <td>{{:: 'no-mappers-available' | translate}}</td>
</tr>
</tbody>
</table>
<div data-ng-show="access.manageRealm">
- <button class="btn btn-primary" data-ng-click="add()">Add Selected</button>
+ <button class="btn btn-primary" data-ng-click="add()">{{:: 'add-selected' | translate}}</button>
</div>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html
index a5df7b6..86f574b 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-offline-sessions.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -10,11 +10,11 @@
<form class="form-horizontal" name="sessionStats">
<fieldset class="border-top">
<div class="form-group">
- <label class="col-md-2 control-label" for="activeSessions">Offline Tokens</label>
+ <label class="col-md-2 control-label" for="activeSessions">{{:: 'offline-tokens' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="count" ng-disabled="true">
</div>
- <kc-tooltip>Total number of offline tokens for this client.</kc-tooltip>
+ <kc-tooltip>{{:: 'offline-tokens.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
</form>
@@ -23,23 +23,23 @@
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
- <a class="btn btn-default" ng-click="loadUsers()" tooltip-placement="left" tooltip-trigger="mouseover mouseout" tooltip="Warning, this is a potentially expensive operation depending on number of offline tokens.">Show Offline Tokens</a>
+ <a class="btn btn-default" ng-click="loadUsers()" tooltip-placement="left" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'show-offline-tokens.tooltip' | translate}}">{{:: 'show-offline-tokens' | translate}}</a>
</div>
</th>
</tr>
<tr data-ng-show="sessions">
- <th>User</th>
- <th>From IP</th>
- <th>Token Issued</th>
+ <th>{{:: 'user' | translate}}</th>
+ <th>{{:: 'from-ip' | translate}}</th>
+ <th>{{:: 'token-issued' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="sessions && (sessions.length >= 5 || query.first != 0)">
<tr>
<td colspan="7">
<div class="table-nav">
- <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
- <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
- <button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">Next page</button>
+ <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+ <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+ <button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">{{:: 'next-page' | translate}}</button>
</div>
</td>
</tr>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
index 16ac50d..37dde5c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-revocation.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -10,18 +10,18 @@
<form class="form-horizontal" name="credentialForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="border-top">
<div class="form-group">
- <label class="col-md-2 control-label" for="notBefore">Not Before</label>
+ <label class="col-md-2 control-label" for="notBefore">{{:: 'not-before' | translate}}</label>
<div class="col-md-6">
<input ng-disabled="true" class="form-control" type="text" id="notBefore" name="notBefore" data-ng-model="notBefore" autofocus>
</div>
- <kc-tooltip>Revoke any tokens issued before this date for this client.</kc-tooltip>
+ <kc-tooltip>{{:: 'client-revoke.not-before.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
- <button type="submit" data-ng-click="clear()" class="btn btn-default">Clear</button>
- <button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-default">Set To Now</button>
- <button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary" tooltip-trigger="mouseover mouseout" tooltip="If admin URL is configured for this client, push this policy to that client." tooltip-placement="bottom">Push</button>
+ <button type="submit" data-ng-click="clear()" class="btn btn-default">{{:: 'clear' | translate}}</button>
+ <button type="submit" data-ng-click="setNotBeforeNow()" class="btn btn-default">{{:: 'set-to-now' | translate}}</button>
+ <button type="submit" data-ng-click="pushRevocation()" class="btn btn-primary" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'client-revoke.push.tooltip' | translate}}" tooltip-placement="bottom">{{:: 'push' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
index f93c96e..7162151 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html
@@ -1,14 +1,14 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">Roles</a></li>
- <li data-ng-show="create">Add role</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
+ <li data-ng-show="create">{{:: 'add-role' | translate}}</li>
<li data-ng-hide="create">{{role.name}}</li>
</ol>
- <h1 data-ng-show="create">Add Role</h1>
+ <h1 data-ng-show="create">{{:: 'add-role' | translate}}</h1>
<h1 data-ng-hide="create">{{role.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageClients"
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
@@ -16,7 +16,7 @@
<fieldset class="border-top">
<div class="form-group">
- <label class="col-md-2 control-label" for="name">Role name <span class="required" data-ng-show="create">*</span></label>
+ <label class="col-md-2 control-label" for="name">{{:: 'role-name' | translate}} <span class="required" data-ng-show="create">*</span></label>
<div class="col-md-6">
<input class="form-control" type="text" id="name" name="name" data-ng-model="role.name" autofocus
@@ -24,7 +24,7 @@
</div>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="description">Description </label>
+ <label class="col-md-2 control-label" for="description">{{:: 'description' | translate}} </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="role.description"></textarea>
@@ -33,64 +33,64 @@
</div>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="scopeParamRequired">Scope Param Required </label>
- <kc-tooltip>This role will be granted just if scope parameter with role name is used during authorization/token request.</kc-tooltip>
+ <label class="col-md-2 control-label" for="scopeParamRequired">{{:: 'scope-param-required' | translate}} </label>
+ <kc-tooltip>{{:: 'scope-param-required.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
- <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch />
+ <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
</div>
<div class="form-group clearfix block" data-ng-hide="create">
- <label class="col-md-2 control-label" for="compositeSwitch" class="control-label">Composite Roles</label>
+ <label class="col-md-2 control-label" for="compositeSwitch" class="control-label">{{:: 'composite-roles' | translate}}</label>
<div class="col-md-6">
- <input ng-model="compositeSwitch" name="compositeSwitch" id="compositeSwitch" ng-disabled="compositeSwitchDisabled" onoffswitch />
+ <input ng-model="compositeSwitch" name="compositeSwitch" id="compositeSwitch" ng-disabled="compositeSwitchDisabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>When this role is (un)assigned to a user any role associated with it will be (un)assigned implicitly.</kc-tooltip>
+ <kc-tooltip>{{:: 'composite-roles.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
- <button kc-save>Save</button>
- <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save>{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
<fieldset data-ng-show="!create && (compositeSwitch || role.composite)">
- <legend uncollapsed><span class="text">Composite Roles</span> </legend>
+ <legend uncollapsed><span class="text">{{:: 'composite-roles' | translate}}</span> </legend>
<div class="form-group">
- <label class="col-md-2 control-label" class="control-label">Realm Roles</label>
+ <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
<div class="row">
<div class="col-md-4">
- <label class="control-label" for="available">Available Roles</label>
- <kc-tooltip>Realm level roles associated with this composite role.</kc-tooltip>
+ <label class="control-label" for="available">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'composite.available-realm-roles.tooltip' | translate}}</kc-tooltip>
<select id="available" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedRealmRoles"
ng-options="r.name for r in realmRoles">
</select>
<button ng-disabled="selectedRealmRoles.length == 0" class="btn btn-default" type="submit" ng-click="addRealmRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-4">
- <label class="control-label" for="assigned">Associated Roles</label>
- <kc-tooltip>Realm level roles associated with this composite role.</kc-tooltip>
+ <label class="control-label" for="assigned">{{:: 'associated-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'composite.associated-realm-roles.tooltip' | translate}}</kc-tooltip>
<select id="assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedRealmMappings"
ng-options="r.name for r in realmMappings">
</select>
<button ng-disabled="selectedRealmMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
</div>
@@ -99,37 +99,37 @@
<div class="form-group" ng-show="clients.length > 0 && !create && (compositeSwitch || role.composite)">
<label class="col-md-2 control-label" class="control-label">
- <span>Client Roles</span>
+ <span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="compositeClient" ng-options="a.clientId for a in clients" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="compositeClient">
- <div class="col-md-4"><span class="text-muted">Select client to view roles for client</span></div>
+ <div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
</div>
<div class="row" data-ng-show="compositeClient">
<div class="col-md-4">
- <label class="control-label" for="available-client">Available Roles</label>
- <kc-tooltip>Roles from this client that you can associate to this composite role.</kc-tooltip>
+ <label class="control-label" for="available-client">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'available-roles.tooltip' | translate}}</kc-tooltip>
<select id="available-client" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedClientRoles"
ng-options="r.name for r in clientRoles">
</select>
<button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-4">
- <label class="control-label" for="assigned-client">Associated Roles</label>
- <kc-tooltip>Client roles associated with this composite role.</kc-tooltip>
+ <label class="control-label" for="assigned-client">{{:: 'associated-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client.associated-roles.tooltip' | translate}}</kc-tooltip>
<select id="assigned-client" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedClientMappings"
ng-options="r.name for r in clientMappings">
</select>
<button ng-disabled="selectedClientMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteClientRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
index f07d9e3..98e2acd 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-list.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -12,28 +12,28 @@
<tr>
<th class="kc-table-actions" colspan="4" data-ng-show="access.manageClients">
<div class="pull-right">
- <a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">Add Role</a>
+ <a class="btn btn-default" href="#/create/role/{{realm.realm}}/clients/{{client.id}}">{{:: 'add-role' | translate}}</a>
</div>
</th>
</tr>
<tr data-ng-hide="!roles || roles.length == 0">
- <th>Role Name</th>
- <th>Composite</th>
- <th>Description</th>
- <th>Actions</th>
+ <th>{{:: 'role-name' | translate}}</th>
+ <th>{{:: 'composite' | translate}}</th>
+ <th>{{:: 'description' | translate}}</th>
+ <th>{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="role in roles">
<td><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{role.name}}</a></td>
- <td>{{role.composite}}</td>
+ <td translate="{{role.composite}}"></td>
<td>{{role.description}}</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">Edit</button>
+ <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/clients/{{client.id}}/roles/{{role.id}}">{{:: 'edit' | translate}}</button>
</td>
</tr>
<tr data-ng-show="!roles || roles.length == 0">
- <td>No client roles available</td>
+ <td>{{:: 'no-client-roles-available' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
index 92e24fd..f6cb851 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-export.html
@@ -1,18 +1,18 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">SAML Keys</a></li>
- <li class="active">SAML {{keyType}} Key Export</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
+ <li class="active">SAML {{keyType}} {{:: 'key-export' | translate}}</li>
</ol>
- <h1>Export SAML Key {{client.clientId|capitalize}}</h1>
+ <h1>{{:: 'export-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="form-group col-sm-10">
<div class="form-group">
- <label class="col-md-2 control-label" for="downloadKeyFormat">Archive Format</label>
+ <label class="col-md-2 control-label" for="downloadKeyFormat">{{:: 'archive-format' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="downloadKeyFormat"
@@ -21,39 +21,39 @@
</select>
</div>
</div>
- <kc-tooltip>Java keystore or PKCS12 archive format.</kc-tooltip>
+ <kc-tooltip>{{:: 'archive-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="keyAlias">Key Alias</label>
+ <label class="col-md-2 control-label" for="keyAlias">{{:: 'key-alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="keyAlias" name="keyAlias" data-ng-model="jks.keyAlias" autofocus required>
</div>
- <kc-tooltip>Archive alias for your private key and certificate.</kc-tooltip>
+ <kc-tooltip>{{:: 'key-alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-hide="!keyInfo.privateKey">
- <label class="col-md-2 control-label" for="keyPassword">Key Password</label>
+ <label class="col-md-2 control-label" for="keyPassword">{{:: 'key-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="keyPassword" name="keyPassword" data-ng-model="jks.keyPassword" autofocus required>
</div>
- <kc-tooltip>Password to access the private key in the archive</kc-tooltip>
+ <kc-tooltip>{{:: 'key-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="realmAlias">Realm Certificate Alias</label>
+ <label class="col-md-2 control-label" for="realmAlias">{{:: 'realm-certificate-alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="realmAlias" name="realmAlias" data-ng-model="jks.realmAlias" autofocus required>
</div>
- <kc-tooltip>Realm certificate is stored in archive too. This is the alias to it.</kc-tooltip>
+ <kc-tooltip>{{:: 'realm-certificate-alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storePassword">Store Password</label>
+ <label class="col-md-2 control-label" for="storePassword">{{:: 'store-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="storePassword" name="storePassword" data-ng-model="jks.storePassword" autofocus required>
</div>
- <kc-tooltip>Password to access the archive itself</kc-tooltip>
+ <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-primary" type="submit" data-ng-click="download()">Download</button>
+ <button class="btn btn-primary" type="submit" data-ng-click="download()">{{:: 'download' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
index 124448a..78a143c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-key-import.html
@@ -1,18 +1,18 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">SAML Keys</a></li>
- <li class="active">SAML {{keyType}} Key Import</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
+ <li class="active">SAML {{keyType}} {{:: 'key-import' | translate}}</li>
</ol>
- <h1>Import SAML Key {{client.clientId|capitalize}}</h1>
+ <h1>{{:: 'import-saml-key' | translate}} {{client.clientId|capitalize}}</h1>
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadKeyFormat">Archive Format</label>
+ <label class="col-md-2 control-label" for="uploadKeyFormat">{{:: 'archive-format' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="uploadKeyFormat"
@@ -21,24 +21,24 @@
</select>
</div>
</div>
- <kc-tooltip>Java keystore or PKCS12 archive format.</kc-tooltip>
+ <kc-tooltip>{{:: 'archive-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadKeyAlias">Key Alias</label>
+ <label class="col-md-2 control-label" for="uploadKeyAlias">{{:: 'key-alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="uploadKeyAlias" name="uploadKeyAlias" data-ng-model="uploadKeyAlias" autofocus required>
</div>
- <kc-tooltip>Archive alias for your certificate.</kc-tooltip>
+ <kc-tooltip>{{:: 'key-alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="uploadStorePassword">Store Password</label>
+ <label class="col-md-2 control-label" for="uploadStorePassword">{{:: 'store-password' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="password" id="uploadStorePassword" name="uploadStorePassword" data-ng-model="uploadStorePassword" autofocus required>
</div>
- <kc-tooltip>Password to access the archive itself</kc-tooltip>
+ <kc-tooltip>{{:: 'store-password.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label">Import File </label>
+ <label class="col-md-2 control-label">{{:: 'import-file' | translate}} </label>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
<label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
@@ -51,8 +51,8 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="files.length > 0">
- <button type="submit" data-ng-click="uploadFile()" class="btn btn-primary">Import</button>
- <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-default">Cancel</button>
+ <button type="submit" data-ng-click="uploadFile()" class="btn btn-primary">{{:: 'import' | translate}}</button>
+ <button type="submit" data-ng-click="clearFileSelect()" class="btn btn-default">{{:: 'cancel' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
index cadfb49..52e5468 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-saml-keys.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -9,9 +9,9 @@
<form class="form-horizontal" name="keyForm" novalidate kc-read-only="!access.manageRealm">
<fieldset class="form-group col-sm-10" data-ng-show="client.attributes['saml.client.signature'] == 'true'">
- <legend uncollapsed><span class="text">Signing Key</span> <kc-tooltip>SAML Signing Key.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'signing-key' | translate}}</span> <kc-tooltip>{{:: 'saml-signing-key' | translate}}</kc-tooltip></legend>
<div class="form-group" data-ng-hide="!signingKeyInfo.privateKey">
- <label class="col-md-2 control-label" for="signingPrivateKey">Private key</label>
+ <label class="col-md-2 control-label" for="signingPrivateKey">{{:: 'private-key' | translate}}</label>
<div class="col-sm-10">
<textarea type="text" id="signingPrivateKey" name="signingPrivateKey" class="form-control" rows="5"
@@ -19,7 +19,7 @@
</div>
</div>
<div class="form-group" data-ng-hide="!signingKeyInfo.certificate">
- <label class="col-md-2 control-label" for="signingCert">Certificate</label>
+ <label class="col-md-2 control-label" for="signingCert">{{:: 'certificate' | translate}}</label>
<div class="col-sm-10">
<textarea type="text" id="signingCert" name="signingCert" class="form-control" rows="5"
@@ -28,16 +28,16 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">Generate new keys</button>
- <button class="btn btn-default" type="submit" data-ng-click="importSigningKey()">Import</button>
- <button class="btn btn-default" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">Export</button>
+ <button class="btn btn-default" type="submit" data-ng-click="generateSigningKey()">{{:: 'generate-new-keys' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-click="importSigningKey()">{{:: 'import' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-hide="!signingKeyInfo.certificate" data-ng-click="exportSigningKey()">{{:: 'export' | translate}}</button>
</div>
</div>
</fieldset>
<fieldset class="form-group col-sm-10" data-ng-show="client.attributes['saml.encrypt'] == 'true'">
- <legend uncollapsed><span class="text">Encryption Key</span> <kc-tooltip>SAML Encryption Key.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'encryption-key' | translate}}</span> <kc-tooltip>{{:: 'saml-encryption-key.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group" data-ng-hide="!encryptionKeyInfo.privateKey">
- <label class="col-md-2 control-label" for="encryptionPrivateKey">Private key</label>
+ <label class="col-md-2 control-label" for="encryptionPrivateKey">{{:: 'private-key' | translate}}</label>
<div class="col-sm-10">
<textarea type="text" id="encryptionPrivateKey" name="encryptionPrivateKey" class="form-control" rows="5"
@@ -45,7 +45,7 @@
</div>
</div>
<div class="form-group" data-ng-hide="!encryptionKeyInfo.certificate">
- <label class="col-md-2 control-label" for="encryptionCert">Certificate</label>
+ <label class="col-md-2 control-label" for="encryptionCert">{{:: 'certificate' | translate}}</label>
<div class="col-sm-10">
<textarea type="text" id="encryptionCert" name="encryptionCert" class="form-control" rows="5"
@@ -54,9 +54,9 @@
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="access.manageRealm">
- <button class="btn btn-default" type="submit" data-ng-click="generateEncryptionKey()">Generate new keys</button>
- <button class="btn btn-default" type="submit" data-ng-click="importEncryptionKey()">Import</button>
- <button class="btn btn-default" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">Export</button>
+ <button class="btn btn-default" type="submit" data-ng-click="generateEncryptionKey()">{{:: 'generate-new-keys' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-click="importEncryptionKey()">{{:: 'import' | translate}}</button>
+ <button class="btn btn-default" type="submit" data-ng-hide="!encryptionKeyInfo.certificate" data-ng-click="exportEncryptionKey()">{{:: 'export' | translate}}</button>
</div>
</div>
</fieldset>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
index 6f68ed8..6c48016 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-scope-mappings.html
@@ -1,21 +1,21 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
<kc-tabs-client></kc-tabs-client>
- <h2><span>{{client.clientId}}</span> Scope Mappings </h2>
+ <h2><span>{{client.clientId}}</span> {{:: 'scope-mappings' | translate}} </h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="allowScope" novalidate kc-read-only="!access.manageClients">
<fieldset class="border-top">
<div class="form-group">
- <label class="col-md-2 control-label" for="fullScopeAllowed">Full Scope Allowed</label>
- <kc-tooltip>Allows you to disable all restrictions.</kc-tooltip>
+ <label class="col-md-2 control-label" for="fullScopeAllowed">{{:: 'full-scope-allowed' | translate}}</label>
+ <kc-tooltip>{{:: 'full-scope-allowed.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
- <input ng-model="client.fullScopeAllowed" ng-click="changeFullScopeAllowed()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch />
+ <input ng-model="client.fullScopeAllowed" ng-click="changeFullScopeAllowed()" name="fullScopeAllowed" id="fullScopeAllowed" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
</div>
</fieldset>
@@ -23,12 +23,12 @@
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.fullScopeAllowed">
<div class="form-group">
- <label class="col-md-2 control-label" class="control-label">Realm Roles</label>
+ <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
<div class="row">
<div class="col-md-3">
- <label class="control-label" for="available">Available Roles</label>
- <kc-tooltip>Realm level roles that can be assigned to scope.</kc-tooltip>
+ <label class="control-label" for="available">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'scope.available-roles.tooltip' | translate}}</kc-tooltip>
<select id="available" class="form-control" multiple size="5"
ng-multiple="true"
@@ -36,24 +36,24 @@
ng-options="r.name for r in realmRoles">
</select>
<button ng-disabled="selectedRealmRoles.length == 0" class="btn btn-default" type="submit" ng-click="addRealmRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="assigned">Assigned Roles</label>
- <kc-tooltip>Realm level roles assigned to scope.</kc-tooltip>
+ <label class="control-label" for="assigned">{{:: 'assigned-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'assigned-roles.tooltip' | translate}}</kc-tooltip>
<select id="assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedRealmMappings"
ng-options="r.name for r in realmMappings">
</select>
<button ng-disabled="selectedRealmMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="realm-composite">Effective Roles </label>
- <kc-tooltip>Assigned realm level roles that may have been inherited from a composite role.</kc-tooltip>
+ <label class="control-label" for="realm-composite">{{:: 'effective-roles' | translate}} </label>
+ <kc-tooltip>{{:: 'realm.effective-roles.tooltip' | translate}}</kc-tooltip>
<select id="realm-composite" class="form-control" multiple size=5
disabled="true"
ng-model="dummymodel"
@@ -66,42 +66,42 @@
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
- <span>Client Roles</span>
+ <span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
- <div class="col-md-4"><span class="text-muted">Select client to view roles for client</span></div>
+ <div class="col-md-4"><span class="text-muted">{{:: 'select-client-roles.tooltip' | translate}}</span></div>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-3">
- <label class="control-label" for="client-available">Available Roles</label>
- <kc-tooltip>Client roles available to be assigned.</kc-tooltip>
+ <label class="control-label" for="client-available">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-available" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedClientRoles"
ng-options="r.name for r in clientRoles">
</select>
<button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="client-assigned">Assigned Roles</label>
- <kc-tooltip>Assigned client roles.</kc-tooltip>
+ <label class="control-label" for="client-assigned">{{:: 'assigned-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client.assigned-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedClientMappings"
ng-options="r.name for r in clientMappings">
</select>
<button ng-disabled="selectedClientMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteClientRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="client-composite">Effective Roles</label>
- <kc-tooltip>Assigned client roles that may have been inherited from a composite role.</kc-tooltip>
+ <label class="control-label" for="client-composite">{{:: 'effective-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client.effective-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-composite" class="form-control" multiple size=5
disabled="true"
ng-model="dummymodel"
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
index 29bb4ab..bb38c6a 100644
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-service-account-roles.html
@@ -1,23 +1,23 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
<kc-tabs-client></kc-tabs-client>
- <h2><span>{{client.clientId}}</span> Service Accounts </h2>
+ <h2><span>{{client.clientId}}</span> {{:: 'service-accounts' | translate}} </h2>
<p class="subtitle"></p>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="client.serviceAccountsEnabled">
<div class="form-group">
- <label class="col-md-2 control-label" class="control-label">Realm Roles</label>
+ <label class="col-md-2 control-label" class="control-label">{{:: 'realm-roles' | translate}}</label>
<div class="col-md-10">
<div class="row">
<div class="col-md-3">
- <label class="control-label" for="available">Available Roles</label>
- <kc-tooltip>Realm level roles that can be assigned to service account.</kc-tooltip>
+ <label class="control-label" for="available">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'service-account.available-roles.tooltip' | translate}}</kc-tooltip>
<select id="available" class="form-control" multiple size="5"
ng-multiple="true"
@@ -25,24 +25,24 @@
ng-options="r.name for r in realmRoles">
</select>
<button ng-disabled="selectedRealmRoles.length == 0" class="btn btn-default" type="submit" ng-click="addRealmRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="assigned">Assigned Roles</label>
- <kc-tooltip>Realm level roles assigned to service account.</kc-tooltip>
+ <label class="control-label" for="assigned">{{:: 'assigned-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'service-account.assigned-roles.tooltip' | translate}}</kc-tooltip>
<select id="assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedRealmMappings"
ng-options="r.name for r in realmMappings">
</select>
<button ng-disabled="selectedRealmMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteRealmRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="realm-composite">Effective Roles </label>
- <kc-tooltip>Assigned realm level roles that may have been inherited from a composite role.</kc-tooltip>
+ <label class="control-label" for="realm-composite">{{:: 'effective-roles' | translate}} </label>
+ <kc-tooltip>{{:: 'realm.effective-roles.tooltip' | translate}}</kc-tooltip>
<select id="realm-composite" class="form-control" multiple size=5
disabled="true"
ng-model="dummymodel"
@@ -55,42 +55,42 @@
<div class="form-group">
<label class="col-md-2 control-label" class="control-label">
- <span>Client Roles</span>
+ <span>{{:: 'client-roles' | translate}}</span>
<select class="form-control" id="clients" name="clients" ng-change="changeClient()" ng-model="targetClient" ng-options="a.clientId for a in clients" ng-disabled="false"></select>
</label>
<div class="col-md-10">
<div class="row" data-ng-hide="targetClient">
- <div class="col-md-4"><span class="text-muted">Select client to view roles for client</span></div>
+ <div class="col-md-4"><span class="text-muted">{{:: 'select-client-to-view-roles' | translate}}</span></div>
</div>
<div class="row" data-ng-show="targetClient">
<div class="col-md-3">
- <label class="control-label" for="client-available">Available Roles</label>
- <kc-tooltip>Client roles available to be assigned.</kc-tooltip>
+ <label class="control-label" for="client-available">{{:: 'available-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'assign.available-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-available" class="form-control" multiple size="5"
ng-multiple="true"
ng-model="selectedClientRoles"
ng-options="r.name for r in clientRoles">
</select>
<button ng-disabled="selectedClientRoles.length == 0" class="btn btn-default" type="submit" ng-click="addClientRole()">
- Add selected <i class="fa fa-angle-double-right"></i>
+ {{:: 'add-selected' | translate}} <i class="fa fa-angle-double-right"></i>
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="client-assigned">Assigned Roles</label>
- <kc-tooltip>Assigned client roles.</kc-tooltip>
+ <label class="control-label" for="client-assigned">{{:: 'assigned-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client.assigned-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-assigned" class="form-control" multiple size=5
ng-multiple="true"
ng-model="selectedClientMappings"
ng-options="r.name for r in clientMappings">
</select>
<button ng-disabled="selectedClientMappings.length == 0" class="btn btn-default" type="submit" ng-click="deleteClientRole()">
- <i class="fa fa-angle-double-left"></i> Remove selected
+ <i class="fa fa-angle-double-left"></i> {{:: 'remove-selected' | translate}}
</button>
</div>
<div class="col-md-3">
- <label class="control-label" for="client-composite">Effective Roles</label>
- <kc-tooltip>Assigned client roles that may have been inherited from a composite role.</kc-tooltip>
+ <label class="control-label" for="client-composite">{{:: 'effective-roles' | translate}}</label>
+ <kc-tooltip>{{:: 'client.effective-roles.tooltip' | translate}}</kc-tooltip>
<select id="client-composite" class="form-control" multiple size=5
disabled="true"
ng-model="dummymodel"
@@ -103,7 +103,7 @@
</form>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageClients" data-ng-show="!client.serviceAccountsEnabled">
- <legend><span class="text">Service account is not enabled for {{client.clientId}}.</span></legend>
+ <legend><span class="text" translate="service-account-is-not-enabled-for" translate-values="{client: client.clientId}"></span></legend>
</form>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-sessions.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-sessions.html
index a22dcc6..5dbb2af 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-sessions.html
@@ -1,7 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li>{{client.clientId}}</li>
</ol>
@@ -10,11 +10,11 @@
<form class="form-horizontal" name="sessionStats">
<fieldset class="border-top">
<div class="form-group">
- <label class="col-md-2 control-label" for="activeSessions">Active Sessions</label>
+ <label class="col-md-2 control-label" for="activeSessions">{{:: 'active-sessions' | translate}}</label>
<div class="col-md-6">
<input class="form-control" type="text" id="activeSessions" name="activeSessions" data-ng-model="count" ng-disabled="true">
</div>
- <kc-tooltip>Total number of active user sessions for this client.</kc-tooltip>
+ <kc-tooltip>{{:: 'active-sessions.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
</form>
@@ -23,23 +23,23 @@
<tr>
<th class="kc-table-actions" colspan="3">
<div class="pull-right">
- <a class="btn btn-default" ng-click="loadUsers()" tooltip-placement="left" tooltip-trigger="mouseover mouseout" tooltip="Warning, this is a potentially expensive operation depending on number of active sessions.">Show Sessions</a>
+ <a class="btn btn-default" ng-click="loadUsers()" tooltip-placement="left" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'show-sessions.tooltip' | translate}}">{{:: 'show-sessions' | translate}}</a>
</div>
</th>
</tr>
<tr data-ng-show="sessions">
- <th>User</th>
- <th>From IP</th>
- <th>Session Start</th>
+ <th>{{:: 'user' | translate}}</th>
+ <th>{{:: 'from-ip' | translate}}</th>
+ <th>{{:: 'session-start' | translate}}</th>
</tr>
</thead>
<tfoot data-ng-show="sessions && (sessions.length >= 5 || query.first != 0)">
<tr>
<td colspan="7">
<div class="table-nav">
- <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">First page</button>
- <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">Previous page</button>
- <button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">Next page</button>
+ <button data-ng-click="firstPage()" class="first" ng-disabled="query.first == 0">{{:: 'first-page' | translate}}</button>
+ <button data-ng-click="previousPage()" class="prev" ng-disabled="query.first == 0">{{:: 'previous-page' | translate}}</button>
+ <button data-ng-click="nextPage()" class="next" ng-disabled="sessions.length < query.max">{{:: 'next-page' | translate}}</button>
</div>
</td>
</tr>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
index 2c389d7..5852634 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mapper-detail.html
@@ -1,33 +1,33 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">Identity Provider Mappers</a></li>
- <li class="active" data-ng-show="create">Create IdentityProvider Mapper</li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-mappers/{{identityProvider.alias}}/mappers">{{:: 'identity-provider-mappers' | translate}}</a></li>
+ <li class="active" data-ng-show="create">{{:: 'create-identity-provider-mapper' | translate}}</li>
<li class="active" data-ng-hide="create">{{mapper.name|capitalize}}</li>
</ol>
<h1 data-ng-hide="create">{{mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageRealm"
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
- <h1 data-ng-show="create">Add Identity Provider Mapper</h1>
+ <h1 data-ng-show="create">{{:: 'add-identity-provider-mapper' | translate}}</h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group clearfix" data-ng-show="!create">
- <label class="col-md-2 control-label" for="mapperId">ID </label>
+ <label class="col-md-2 control-label" for="mapperId">{{:: 'id' | translate}} </label>
<div class="col-md-6">
<input class="form-control" id="mapperId" type="text" ng-model="mapper.id" readonly>
</div>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="name">Name <span class="required">*</span></label>
+ <label class="col-md-2 control-label" for="name">{{:: 'name' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
<input class="form-control" id="name" type="text" ng-model="mapper.name" data-ng-readonly="!create" required>
</div>
- <kc-tooltip>Name of the mapper.</kc-tooltip>
+ <kc-tooltip>{{:: 'mapper.name.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="create">
- <label class="col-md-2 control-label" for="mapperTypeCreate">Mapper Type</label>
+ <label class="col-md-2 control-label" for="mapperTypeCreate">{{:: 'mapper-type' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="mapperTypeCreate"
@@ -39,7 +39,7 @@
<kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-hide="create">
- <label class="col-md-2 control-label" for="mapperType">Mapper Type</label>
+ <label class="col-md-2 control-label" for="mapperType">{{:: 'mapper-type' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="mapperType" type="text" ng-model="mapperType.name" data-ng-readonly="true">
</div>
@@ -50,15 +50,15 @@
<div class="form-group" data-ng-show="create && access.manageRealm">
<div class="col-md-10 col-md-offset-2">
- <button kc-save>Save</button>
- <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save>{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
<div class="form-group" data-ng-show="!create && access.manageRealm">
<div class="col-md-10 col-md-offset-2">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
index 676a116..7950d6f 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/identity-provider-mappers.html
@@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li>{{identityProvider.alias}}</li>
</ol>
@@ -13,22 +13,22 @@
<div class="form-inline">
<div class="form-group">
<div class="input-group">
- <input type="text" placeholder="Search..." data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
+ <input type="text" placeholder="{{:: 'search.placeholder' | translate}}" data-ng-model="search.name" class="form-control search" onkeyup="if(event.keyCode == 13){$(this).next('I').click();}">
<div class="input-group-addon">
<i class="fa fa-search" type="submit"></i>
</div>
</div>
</div>
<div class="pull-right">
- <a class="btn btn-primary" href="#/create/identity-provider-mappers/{{realm.realm}}/{{identityProvider.alias}}">Create</a>
+ <a class="btn btn-primary" href="#/create/identity-provider-mappers/{{realm.realm}}/{{identityProvider.alias}}">{{:: 'create' | translate}}</a>
</div>
</div>
</th>
</tr>
<tr data-ng-hide="mappers.length == 0">
- <th>Name</th>
- <th>Category</th>
- <th>Type</th>
+ <th>{{:: 'name' | translate}}</th>
+ <th>{{:: 'category' | translate}}</th>
+ <th>{{:: 'type' | translate}}</th>
</tr>
</thead>
<tbody>
@@ -38,7 +38,7 @@
<td>{{mapperTypes[mapper.identityProviderMapper].name}}</td>
</tr>
<tr data-ng-show="mappers.length == 0">
- <td>No mappers available</td>
+ <td>{{:: 'no-mappers-available' | translate}}</td>
</tr>
</tbody>
</table>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/notfound.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/notfound.html
index 20286c4..67b11e0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/notfound.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/notfound.html
@@ -2,6 +2,6 @@
<div class="error-container">
<h2>Resource <strong>not found</strong>...</h2>
<p class="instruction">We could not find the resource you are looking for. Please make sure the URL you entered is correct.</p>
- <a href="#" class="link-right">Go to the home page »</a>
+ <a href="#/" class="link-right">Go to the home page »</a>
</div>
</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
index 6f08e96..85ad20b 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/otp-policy.html
@@ -59,6 +59,15 @@
<kc-tooltip>What should the initial counter value be?</kc-tooltip>
</div>
+ <div class="form-group" data-ng-show="realm.otpPolicyType == 'totp'">
+ <label class="col-md-2 control-label" for="counter">OTP Token Period</label>
+ <div class="col-md-6">
+ <input class="form-control" type="text" id="period" name="period" data-ng-model="realm.otpPolicyPeriod">
+ </div>
+ <kc-tooltip>How many seconds should an OTP token be valid? Defaults to 30 seconds.</kc-tooltip>
+ </div>
+
+
<div class="form-group" data-ng-show="access.manageRealm">
<div class="col-md-10 col-md-offset-2">
<button kc-save data-ng-disabled="!changed">Save</button>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
index 5bee1b5..4e4224a 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/protocol-mapper-detail.html
@@ -1,14 +1,14 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/clients">Clients</a></li>
+ <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
<li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{client.clientId}}</a></li>
- <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">Mappers</a></li>
- <li class="active" data-ng-show="create">Create Protocol Mappers</li>
+ <li><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">{{:: 'mappers' | translate}}</a></li>
+ <li class="active" data-ng-show="create">{{:: 'create-protocol-mappers' | translate}}</li>
<li class="active" data-ng-hide="create">{{mapper.name}}</li>
</ol>
- <h1 data-ng-show="create">Create Protocol Mapper</h1>
+ <h1 data-ng-show="create">{{:: 'create-protocol-mapper' | translate}}</h1>
<h1 data-ng-hide="create">{{mapper.name|capitalize}}<i class="pficon pficon-delete clickable" data-ng-show="!create && access.manageRealm"
data-ng-hide="changed" data-ng-click="remove()"></i></h1>
@@ -16,42 +16,42 @@
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="protocol">Protocol</label>
+ <label class="col-md-2 control-label" for="protocol">{{:: 'protocol' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="protocol" type="text" ng-model="protocol" readonly>
</div>
- <kc-tooltip>Protocol.</kc-tooltip>
+ <kc-tooltip>{{:: 'protocol.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-show="!create">
- <label class="col-md-2 control-label" for="mapperId">ID </label>
+ <label class="col-md-2 control-label" for="mapperId">{{:: 'id' | translate}} </label>
<div class="col-md-6">
<input class="form-control" id="mapperId" type="text" ng-model="mapper.id" readonly>
</div>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="name">Name</label>
+ <label class="col-md-2 control-label" for="name">{{:: 'name' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="name" type="text" ng-model="mapper.name" data-ng-readonly="!create">
</div>
- <kc-tooltip>Name of the mapper.</kc-tooltip>
+ <kc-tooltip>{{:: 'mapper.name.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label for="consentRequired" class="col-sm-2 control-label">Consent required</label>
+ <label for="consentRequired" class="col-sm-2 control-label">{{:: 'consent-required' | translate}}</label>
<div class="col-md-6">
- <input ng-model="mapper.consentRequired" name="consentRequired" id="consentRequired" onoffswitch />
+ <input ng-model="mapper.consentRequired" name="consentRequired" id="consentRequired" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>When granting temporary access, must the user consent to providing this data to the client?</kc-tooltip>
+ <kc-tooltip>{{:: 'mapper.consent-required.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="mapper.consentRequired">
- <label class="col-md-2 control-label" for="consentText">Consent Text </label>
+ <label class="col-md-2 control-label" for="consentText">{{:: 'consent-text' | translate}} </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="consentText" name="consentText" data-ng-model="mapper.consentText"></textarea>
</div>
- <kc-tooltip>Text to display on consent page</kc-tooltip>
+ <kc-tooltip>{{:: 'consent-text.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="create">
- <label class="col-md-2 control-label" for="mapperTypeCreate">Mapper Type</label>
+ <label class="col-md-2 control-label" for="mapperTypeCreate">{{:: 'mapper-type' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="mapperTypeCreate"
@@ -63,7 +63,7 @@
<kc-tooltip>{{mapperType.helpText}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-hide="create">
- <label class="col-md-2 control-label" for="mapperType">Mapper Type</label>
+ <label class="col-md-2 control-label" for="mapperType">{{:: 'mapper-type' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="mapperType" type="text" ng-model="mapperType.name" data-ng-readonly="true">
</div>
@@ -74,15 +74,15 @@
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageRealm">
- <button kc-save>Save</button>
- <button kc-cancel data-ng-click="cancel()">Cancel</button>
+ <button kc-save>{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-reset data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
index b3b8139..37256af 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider.html
@@ -1,11 +1,11 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
- <h1>Identity Providers</h1>
+ <h1>{{:: 'identity-providers' | translate}}</h1>
<form name="realmForm" novalidate class="form-horizontal">
<fieldset>
<div>
<table class="table table-striped table-bordered">
- <caption class="hidden">Table of identity providers</caption>
+ <caption class="hidden">{{:: 'table-of-identity-providers' | translate}}</caption>
<thead>
<tr>
<th colspan="6" class="kc-table-actions">
@@ -13,17 +13,17 @@
<select class="form-control" ng-model="provider"
ng-options="p.name group by p.groupName for p in allProviders track by p.id"
data-ng-change="addProvider(provider); provider = null">
- <option value="" disabled selected>Add provider...</option>
+ <option value="" disabled selected>{{:: 'add-provider.placeholder' | translate}}</option>
</select>
</div>
</th>
</tr>
<tr ng-show="configuredProviders.length > 0">
- <th>Name</th>
- <th>Provider</th>
- <th>Enabled</th>
- <th width="15%">GUI order</th>
- <th colspan="2">Actions</th>
+ <th>{{:: 'name' | translate}}</th>
+ <th>{{:: 'provider' | translate}}</th>
+ <th>{{:: 'enabled' | translate}}</th>
+ <th width="15%">{{:: 'gui-order' | translate}}</th>
+ <th colspan="2">{{:: 'actions' | translate}}</th>
</tr>
</thead>
<tbody ng-show="configuredProviders.length > 0">
@@ -32,13 +32,13 @@
<a href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{identityProvider.alias}}</a>
</td>
<td>{{identityProvider.providerId}}</td>
- <td>{{identityProvider.enabled}}</td>
+ <td translate="{{identityProvider.enabled}}"></td>
<td>{{identityProvider.config.guiOrder}}</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">Edit</button>
+ <button class="btn btn-default btn-block btn-sm" kc-open="/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.alias}}">{{:: 'edit' | translate}}</button>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" data-ng-click="removeIdentityProvider(identityProvider)">Delete</button>
+ <button class="btn btn-default btn-block btn-sm" data-ng-click="removeIdentityProvider(identityProvider)">{{:: 'delete' | translate}}</button>
</td>
</tr>
</tbody>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html
index e811dc9..3e483f7 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-export.html
@@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initProvider()">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li>{{identityProvider.alias}}</li>
</ol>
@@ -10,7 +10,7 @@
<fieldset class="border-top">
<div class="form-group">
<div class="col-sm-12">
- <a class="btn btn-primary" data-ng-click="download()" type="submit">Download</a>
+ <a class="btn btn-primary" data-ng-click="download()" type="submit">{{:: 'download' | translate}}</a>
<textarea class="form-control" rows="20" kc-select-action="click">{{exported}}</textarea>
</div>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
index f372622..170bfe6 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-oidc.html
@@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initProvider()">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li>{{identityProvider.alias}}</li>
</ol>
@@ -9,193 +9,193 @@
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="redirectUri">Redirect URI</label>
+ <label class="col-md-2 control-label" for="redirectUri">{{:: 'redirect-uri' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
- <kc-tooltip>The redirect uri to use when configuring the identity provider</kc-tooltip>
+ <kc-tooltip>{{:: 'redirect-uri.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="identifier"><span class="required">*</span> Alias</label>
+ <label class="col-md-2 control-label" for="identifier"><span class="required">*</span> {{:: 'alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="identifier" type="text" ng-model="identityProvider.alias" data-ng-readonly="!newIdentityProvider" required>
</div>
- <kc-tooltip>The alias unique identifies an identity provider and it is also used to build the redirect uri.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="enabled">Enabled</label>
+ <label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable this identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.enabled.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <label class="col-md-2 control-label" for="authenticateByDefault">{{:: 'authenticate-by-default' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Indicates if this provider should be tried by default for authentication even before displaying login screen</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.authenticate-by-default.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storeToken">Store Tokens</label>
+ <label class="col-md-2 control-label" for="storeToken">{{:: 'store-tokens' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
+ <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable if tokens must be stored after authenticating users.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.store-tokens.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storedTokensReadable">Stored Tokens Readable</label>
+ <label class="col-md-2 control-label" for="storedTokensReadable">{{:: 'stored-tokens-readable' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch />
+ <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable new users can read any stored tokens. This assigns the broker.read-token role.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
+ <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
- <option value="on">On</option>
- <option value="missing">On missing info</option>
- <option value="off">Off</option>
+ <option value="on">{{:: 'on' | translate}}</option>
+ <option value="missing">{{:: 'on-missing-info' | translate}}</option>
+ <option value="off">{{:: 'off' | translate}}</option>
</select>
</div>
</div>
- <kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
+ <kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="trustEmail">Trust email</label>
+ <label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
+ <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
+ <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="guiOrder">GUI order</label>
+ <label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
- <kc-tooltip>Number defining order of the provider in GUI (eg. on Login page).</kc-tooltip>
+ <kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset>
- <legend uncollapsed><span class="text">OpenID Connect Config</span> <kc-tooltip>OIDC SP and external IDP configuration.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'openid-connect-config' | translate}}</span> <kc-tooltip>{{:: 'openid-connect-config.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="authorizationUrl"><span class="required">*</span> Authorization Url</label>
+ <label class="col-md-2 control-label" for="authorizationUrl"><span class="required">*</span> {{:: 'authorization-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="authorizationUrl" type="text" ng-model="identityProvider.config.authorizationUrl" required>
</div>
- <kc-tooltip>The Authorization Url.</kc-tooltip>
+ <kc-tooltip>{{:: 'authorization-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="tokenUrl"><span class="required">*</span> Token Url</label>
+ <label class="col-md-2 control-label" for="tokenUrl"><span class="required">*</span> {{:: 'token-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="tokenUrl" type="text" ng-model="identityProvider.config.tokenUrl" required>
</div>
- <kc-tooltip>The Token Url.</kc-tooltip>
+ <kc-tooltip>{{:: 'token-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="userInfoUrl">Logout Url</label>
+ <label class="col-md-2 control-label" for="userInfoUrl">{{:: 'logout-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="logoutUrl" type="text" ng-model="identityProvider.config.logoutUrl">
</div>
- <kc-tooltip>End session endpoint to use to logout user from external IDP.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.logout-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-sm-2 control-label" for="backchannelSupported">Backchannel Logout</label>
+ <label class="col-sm-2 control-label" for="backchannelSupported">{{:: 'backchannel-logout' | translate}}</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.backchannelSupported" id="backchannelSupported" onoffswitchvalue />
+ <input ng-model="identityProvider.config.backchannelSupported" id="backchannelSupported" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="Does the external IDP support backchannel logout?" class="fa fa-info-circle"></span>
+ <span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="{{:: 'backchannel-logout.tooltip' | translate}}" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="userInfoUrl">User Info Url</label>
+ <label class="col-md-2 control-label" for="userInfoUrl">{{:: 'user-info-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="userInfoUrl" type="text" ng-model="identityProvider.config.userInfoUrl">
</div>
- <kc-tooltip>The User Info Url. This is optional.</kc-tooltip>
+ <kc-tooltip>{{:: 'user-info-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientId"><span class="required">*</span> Client ID</label>
+ <label class="col-md-2 control-label" for="clientId"><span class="required">*</span> {{:: 'client-id' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.clientId" required>
</div>
- <kc-tooltip>The client or client identifier registered withing the identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.client-id.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientSecret"><span class="required">*</span> Client Secret</label>
+ <label class="col-md-2 control-label" for="clientSecret"><span class="required">*</span> {{:: 'client-secret' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="clientSecret" type="password" ng-model="identityProvider.config.clientSecret" ng-show="hidePassword" required>
<input class="form-control" id="clientSecret" type="text" ng-model="identityProvider.config.clientSecret" ng-show="!hidePassword" required>
- <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">Show Secret</a>
- <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">Hide Secret</a>
+ <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">{{:: 'show-secret' | translate}}</a>
+ <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">{{:: 'hide-secret' | translate}}</a>
</div>
- <kc-tooltip>The client or client secret registered withing the identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'client-secret.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="issuer">Issuer </label>
+ <label class="col-md-2 control-label" for="issuer">{{:: 'issuer' | translate}} </label>
<div class="col-md-6">
<input class="form-control" id="issuer" type="text" ng-model="identityProvider.config.issuer">
</div>
- <kc-tooltip>The issuer identifier for the issuer of the response. If not provided, no validation will be performed.</kc-tooltip>
+ <kc-tooltip>{{:: 'issuer.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="defaultScope">Default Scopes </label>
+ <label class="col-md-2 control-label" for="defaultScope">{{:: 'default-scopes' | translate}} </label>
<div class="col-md-6">
<input class="form-control" id="defaultScope" type="text" ng-model="identityProvider.config.defaultScope">
</div>
- <kc-tooltip>The scopes to be sent when asking for authorization. It can be a space-separated list of scopes. Defaults to 'openid'.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.default-scopes.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="prompt">Prompt</label>
+ <label class="col-md-2 control-label" for="prompt">{{:: 'prompt' | translate}}</label>
<div class="col-md-6">
<div>
<select class="form-control" id="prompt" ng-model="identityProvider.config.prompt">
- <option value="">unspecified</option>
- <option value="none">none</option>
- <option>consent</option>
- <option>login</option>
- <option>select_account</option>
+ <option value="">{{:: 'unspecified.option' | translate}}</option>
+ <option value="none">{{:: 'none.option' | translate}}</option>
+ <option>{{:: 'consent.option' | translate}}</option>
+ <option>{{:: 'login.option' | translate}}</option>
+ <option>{{:: 'select-account.option' | translate}}</option>
</select>
</div>
</div>
- <kc-tooltip>Specifies whether the Authorization Server prompts the End-User for reauthentication and consent.</kc-tooltip>
+ <kc-tooltip>{{:: 'prompt.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="validateSignature">Validate Signatures</label>
+ <label class="col-md-2 control-label" for="validateSignature">{{:: 'validate-signatures' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitchvalue />
+ <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable signature validation of external IDP signatures.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.validate-signatures.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-show="identityProvider.config.validateSignature == 'true'">
- <label class="col-md-2 control-label" for="publicKeySignatureVerifier">Validating Public Key</label>
+ <label class="col-md-2 control-label" for="publicKeySignatureVerifier">{{:: 'validating-public-key' | translate}}</label>
<div class="col-md-6">
<textarea class="form-control" id="publicKeySignatureVerifier" ng-model="identityProvider.config.publicKeySignatureVerifier"/>
</div>
- <kc-tooltip>The public key in PEM format that must be used to verify external IDP signatures.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.validating-public-key.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset data-ng-show="newIdentityProvider">
- <legend uncollapsed><span class="text">Import External IDP Config</span> <kc-tooltip>Allows you to load external IDP metadata from a config file or to download it from a URL.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'import-external-idp-config' | translate}}</span> <kc-tooltip>{{:: 'import-external-idp-config.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group" data-ng-show="newIdentityProvider">
- <label class="col-md-2 control-label" for="fromUrl">Import From Url</label>
+ <label class="col-md-2 control-label" for="fromUrl">{{:: 'import-from-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="fromUrl" type="text" ng-model="fromUrl.data">
</div>
- <kc-tooltip>Import metadata from a remote IDP discovery descriptor.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.import-from-url.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6" data-ng-show="importUrl">
- <button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">Import</button>
+ <button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
</div>
</div>
<div class="form-group" data-ng-show="newIdentityProvider">
- <label class="col-md-2 control-label">Import From File</label>
- <kc-tooltip>Import metadata from a downloaded IDP discovery descriptor.</kc-tooltip>
+ <label class="col-md-2 control-label">{{:: 'import-from-file' | translate}}</label>
+ <kc-tooltip>{{:: 'identity-provider.import-from-file.tooltip' | translate}}</kc-tooltip>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
- <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+ <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
@@ -203,15 +203,15 @@
</span>
</div>
<div class="col-md-6" data-ng-show="importFile">
- <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">Import</button>
+ <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
</div>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
index 478137c..bb9726f 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html
@@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initSamlProvider()">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li>{{identityProvider.alias}}</li>
</ol>
@@ -9,134 +9,134 @@
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="redirectUri">Redirect URI</label>
+ <label class="col-md-2 control-label" for="redirectUri">{{:: 'redirect-uri' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
- <kc-tooltip>The redirect uri to use when configuring the identity provider</kc-tooltip>
+ <kc-tooltip>{{:: 'redirect-uri.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="identifier"><span class="required">*</span> Alias</label>
+ <label class="col-md-2 control-label" for="identifier"><span class="required">*</span> {{:: 'alias' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="identifier" type="text" ng-model="identityProvider.alias" data-ng-readonly="!newIdentityProvider" required>
</div>
- <kc-tooltip>The alias unique identifies an identity provider and it is also used to build the redirect uri.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.alias.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="enabled">Enabled</label>
+ <label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable this identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.enabled.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <label class="col-md-2 control-label" for="authenticateByDefault">{{:: 'authenticate-by-default' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Indicates if this provider should be tried by default for authentication even before displaying login screen</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.authenticate-by-default.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storeToken">Store Tokens</label>
+ <label class="col-md-2 control-label" for="storeToken">{{:: 'store-tokens' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
+ <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable if tokens must be stored after authenticating users.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.store-tokens.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storedTokensReadable">Stored Tokens Readable</label>
+ <label class="col-md-2 control-label" for="storedTokensReadable">{{:: 'stored-tokens-readable' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch />
+ <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable new users can read any stored tokens. This assigns the broker.read-token role.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
+ <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
- <option value="on">On</option>
- <option value="missing">On missing info</option>
- <option value="off">Off</option>
+ <option value="on">{{:: 'on' | translate}}</option>
+ <option value="missing">{{:: 'on-missing-info' | translate}}</option>
+ <option value="off">{{:: 'off' | translate}}</option>
</select>
</div>
</div>
- <kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
+ <kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="trustEmail">Trust email</label>
+ <label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
+ <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
+ <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="guiOrder">GUI order</label>
+ <label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
- <kc-tooltip>Number defining order of the provider in GUI (eg. on Login page).</kc-tooltip>
+ <kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset>
- <legend uncollapsed><span class="text">SAML Config</span> <kc-tooltip>SAML SP and external IDP configuration.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'saml-config' | translate}}</span> <kc-tooltip>{{:: 'identity-provider.saml-config.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="singleSignOnServiceUrl"><span class="required">*</span> Single Sign-On Service Url</label>
+ <label class="col-md-2 control-label" for="singleSignOnServiceUrl"><span class="required">*</span> {{:: 'single-signon-service-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="singleSignOnServiceUrl" type="text" ng-model="identityProvider.config.singleSignOnServiceUrl" required>
</div>
- <kc-tooltip>The Url that must be used to send authentication requests(SAML AuthnRequest).</kc-tooltip>
+ <kc-tooltip>{{:: 'saml.single-signon-service-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="singleSignOnServiceUrl">Single Logout Service Url</label>
+ <label class="col-md-2 control-label" for="singleSignOnServiceUrl">{{:: 'single-logout-service-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="singleLogoutServiceUrl" type="text" ng-model="identityProvider.config.singleLogoutServiceUrl">
</div>
- <kc-tooltip>The Url that must be used to send logout requests.</kc-tooltip>
+ <kc-tooltip>{{:: 'saml.single-logout-service-url.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-sm-2 control-label" for="backchannelSupported">Backchannel Logout</label>
+ <label class="col-sm-2 control-label" for="backchannelSupported">{{:: 'backchannel-logout' | translate}}</label>
<div class="col-sm-4">
- <input ng-model="identityProvider.config.backchannelSupported" id="backchannelSupported" onoffswitchvalue />
+ <input ng-model="identityProvider.config.backchannelSupported" id="backchannelSupported" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="Does the external IDP support backchannel logout?" class="fa fa-info-circle"></span>
+ <span tooltip-trigger="mouseover mouseout" tooltip-placement="right" tooltip="{{:: 'backchannel-logout.tooltip' | translate}}" class="fa fa-info-circle"></span>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label>
+ <label class="col-md-2 control-label" for="nameIDPolicyFormat">{{:: 'nameid-policy-format' | translate}}</label>
<div class="col-md-6">
<select id="nameIDPolicyFormat" ng-model="identityProvider.config.nameIDPolicyFormat"
ng-options="nameFormat.format as nameFormat.name for nameFormat in nameIdFormats">
</select>
<!-- <input class="form-control" id="nameIDPolicyFormat" type="text" ng-model="identityProvider.config.nameIDPolicyFormat"> -->
</div>
- <kc-tooltip>Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent.</kc-tooltip>
+ <kc-tooltip>{{:: 'nameid-policy-format.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
+ <label class="col-md-2 control-label" for="postBindingResponse">{{:: 'http-post-binding-response' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitchvalue />
+ <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Indicates whether to respond to requests using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.</kc-tooltip>
+ <kc-tooltip>{{:: 'http-post-binding-response.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
+ <label class="col-md-2 control-label" for="postBindingAuthnRequest">{{:: 'http-post-binding-for-authn-request' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitchvalue />
+ <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Indicates whether the AuthnRequest must be sent using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used.</kc-tooltip>
+ <kc-tooltip>{{:: 'http-post-binding-for-authn-request.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label>
+ <label class="col-md-2 control-label" for="wantAuthnRequestsSigned">{{:: 'want-authn-requests-signed' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" name="wantAuthnRequestsSigned" onoffswitchvalue />
+ <input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" name="wantAuthnRequestsSigned" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip> Indicates whether the identity provider expects signed a AuthnRequest.</kc-tooltip>
+ <kc-tooltip>{{:: 'want-authn-requests-signed.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group" data-ng-show="identityProvider.config.wantAuthnRequestsSigned == 'true'">
- <label class="col-md-2 control-label" for="signatureAlgorithm">Signature Algorithm</label>
+ <label class="col-md-2 control-label" for="signatureAlgorithm">{{:: 'signature-algorithm' | translate}}</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="signatureAlgorithm"
@@ -145,47 +145,47 @@
</select>
</div>
</div>
- <kc-tooltip>The signature algorithm to use to sign documents.</kc-tooltip>
+ <kc-tooltip>{{:: 'signature-algorithm.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="forceAuthn">Force Authentication</label>
+ <label class="col-md-2 control-label" for="forceAuthn">{{:: 'force-authentication' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" name="forceAuthn" onoffswitchvalue />
+ <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" name="forceAuthn" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip> Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.force-authentication.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="validateSignature">Validate Signature</label>
+ <label class="col-md-2 control-label" for="validateSignature">{{:: 'validate-signature' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitchvalue />
+ <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitchvalue on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable signature validation of SAML responses.</kc-tooltip>
+ <kc-tooltip>{{:: 'saml.validate-signature.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix" data-ng-show="identityProvider.config.validateSignature == 'true'">
- <label class="col-md-2 control-label" for="signingCertificate">Validating X509 Certificate</label>
+ <label class="col-md-2 control-label" for="signingCertificate">{{:: 'validating-x509-certificate' | translate}}</label>
<div class="col-md-6">
<textarea class="form-control" id="signingCertificate" ng-model="identityProvider.config.signingCertificate"/>
</div>
- <kc-tooltip>The certificate in PEM format that must be used to check for signatures.</kc-tooltip>
+ <kc-tooltip>{{:: 'validating-x509-certificate.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset data-ng-show="newIdentityProvider">
- <legend uncollapsed><span class="text">Import External IDP Config</span> <kc-tooltip>Allows you to load external IDP metadata from a config file or to download it from a URL.</kc-tooltip></legend>
+ <legend uncollapsed><span class="text">{{:: 'import-external-idp-config' | translate}}</span> <kc-tooltip>{{:: 'import-external-idp-config.tooltip' | translate}}</kc-tooltip></legend>
<div class="form-group" data-ng-show="newIdentityProvider">
- <label class="col-md-2 control-label" for="fromUrl">Import From Url</label>
+ <label class="col-md-2 control-label" for="fromUrl">{{:: 'import-from-url' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="fromUrl" type="text" ng-model="fromUrl.data">
</div>
- <kc-tooltip>Import metadata from a remote IDP SAML entity descriptor.</kc-tooltip>
+ <kc-tooltip>{{:: 'saml.import-from-url.tooltip' | translate}}</kc-tooltip>
<div class="col-sm-4" data-ng-show="importUrl">
- <button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">Import</button>
+ <button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-primary">{{:: 'import' | translate}}</button>
</div>
</div>
<div class="form-group" data-ng-show="newIdentityProvider">
- <label class="col-md-2 control-label">Import From File</label>
+ <label class="col-md-2 control-label">{{:: 'import-from-file' | translate}}</label>
<div class="col-md-6">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
- <label for="import-file" class="btn btn-default">Select file <i class="pficon pficon-import"></i></label>
+ <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
<input id="import-file" type="file" class="hidden" ng-file-select="onFileSelect($files)">
</div>
<span class="kc-uploaded-file" data-ng-show="files.length > 0">
@@ -193,15 +193,15 @@
</span>
</div>
<div class="col-sm-4" data-ng-show="importFile">
- <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">Import</button>
+ <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-primary">{{:: 'import' | translate}}</button>
</div>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
index 06f6c02..2f71d19 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-social.html
@@ -1,6 +1,6 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2" data-ng-init="initProvider()">
<ol class="breadcrumb">
- <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
+ <li><a href="#/realms/{{realm.realm}}/identity-provider-settings">{{:: 'identity-providers' | translate}}</a></li>
<li>{{identityProvider.alias}}</li>
</ol>
@@ -9,100 +9,100 @@
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="redirectUri">Redirect URI</label>
+ <label class="col-md-2 control-label" for="redirectUri">{{:: 'redirect-uri' | translate}}</label>
<div class="col-sm-6">
<input class="form-control" id="redirectUri" type="text" value="{{callbackUrl}}{{identityProvider.alias}}/endpoint" readonly kc-select-action="click">
</div>
- <kc-tooltip>The redirect uri to use when configuring the identity provider</kc-tooltip>
+ <kc-tooltip>{{:: 'redirect-uri.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<fieldset>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientId"><span class="required">*</span> Client ID</label>
+ <label class="col-md-2 control-label" for="clientId"><span class="required">*</span> {{:: 'client-id' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.clientId" required>
</div>
- <kc-tooltip>The client identifier registered withing the identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'social.client-id.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientSecret"><span class="required">*</span> Client Secret</label>
+ <label class="col-md-2 control-label" for="clientSecret"><span class="required">*</span> {{:: 'client-secret' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="clientSecret" type="password" ng-model="identityProvider.config.clientSecret" ng-show="hidePassword" required>
<input class="form-control" id="clientSecret" type="text" ng-model="identityProvider.config.clientSecret" ng-show="!hidePassword" required>
- <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">Show Secret</a>
- <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">Hide Secret</a>
+ <a href="" ng-click="showPassword(false)" class="link" ng-show="hidePassword">{{:: 'show-secret' | translate}}</a>
+ <a href="" ng-click="showPassword(true);" ng-show="!hidePassword">{{:: 'hide-secret' | translate}}</a>
</div>
- <kc-tooltip>The client secret registered withing the identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'social.client-secret.tooltip' | translate}}</kc-tooltip>
</div>
<div data-ng-include data-src="resourceUrl + '/partials/realm-identity-provider-' + identityProvider.providerId + '-ext.html'"></div>
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="defaultScope">Default Scopes </label>
+ <label class="col-md-2 control-label" for="defaultScope">{{:: 'default-scopes' | translate}} </label>
<div class="col-md-6">
<input class="form-control" id="defaultScope" type="text" ng-model="identityProvider.config.defaultScope">
</div>
- <kc-tooltip>The scopes to be sent when asking for authorization. See documentation for possible values, separator and default value'.</kc-tooltip>
+ <kc-tooltip>{{:: 'social.default-scopes.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="enabled">Store Tokens</label>
+ <label class="col-md-2 control-label" for="enabled">{{:: 'store-tokens' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch />
+ <input ng-model="identityProvider.storeToken" id="storeToken" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable if tokens must be stored after authenticating users.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.store-tokens.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="storedTokensReadable">Stored Tokens Readable</label>
+ <label class="col-md-2 control-label" for="storedTokensReadable">{{:: 'stored-tokens-readable' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch />
+ <input ng-model="identityProvider.addReadTokenRoleOnCreate" id="storedTokensReadable" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable new users can read any stored tokens. This assigns the broker.read-token role.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.stored-tokens-readable.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="enabled">Enabled</label>
+ <label class="col-md-2 control-label" for="enabled">{{:: 'enabled' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.enabled" id="enabled" onoffswitch />
+ <input ng-model="identityProvider.enabled" id="enabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Enable/disable this identity provider.</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.enabled.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">Update Profile on First Login</label>
+ <label class="col-md-2 control-label" for="updateProfileFirstLoginMode">{{:: 'update-profile-on-first-login' | translate}}</label>
<div class="col-md-2">
<div>
<select id="updateProfileFirstLoginMode" ng-model="identityProvider.updateProfileFirstLoginMode" class="form-control">
- <option value="on">On</option>
- <option value="missing">On missing info</option>
- <option value="off">Off</option>
+ <option value="on">{{:: 'on' | translate}}</option>
+ <option value="missing">{{:: 'on-missing-info' | translate}}</option>
+ <option value="off">{{:: 'off' | translate}}</option>
</select>
</div>
</div>
- <kc-tooltip>Define under which conditions must user update his profile right after the first login.</kc-tooltip>
+ <kc-tooltip>{{:: 'update-profile-on-first-login.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="trustEmail">Trust email</label>
+ <label class="col-md-2 control-label" for="trustEmail">{{:: 'trust-email' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch />
+ <input ng-model="identityProvider.trustEmail" name="identityProvider.trustEmail" id="trustEmail" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>If enabled then email provided by this provider is not verified even if verification is enabled for the realm.</kc-tooltip>
+ <kc-tooltip>{{:: 'trust-email.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="authenticateByDefault">Authenticate By Default</label>
+ <label class="col-md-2 control-label" for="authenticateByDefault">{{:: 'authenticate-by-default' | translate}}</label>
<div class="col-md-6">
- <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch />
+ <input ng-model="identityProvider.authenticateByDefault" name="identityProvider.authenticateByDefault" id="authenticateByDefault" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
</div>
- <kc-tooltip>Indicates if this provider should be tried by default for authentication even before displaying login screen</kc-tooltip>
+ <kc-tooltip>{{:: 'identity-provider.authenticate-by-default.tooltip' | translate}}</kc-tooltip>
</div>
<div class="form-group">
- <label class="col-md-2 control-label" for="guiOrder">GUI order</label>
+ <label class="col-md-2 control-label" for="guiOrder">{{:: 'gui-order' | translate}}</label>
<div class="col-md-6">
<input class="form-control" id="guiOrder" type="text" ng-model="identityProvider.config.guiOrder">
</div>
- <kc-tooltip>Number defining order of the provider in GUI (eg. on Login page).</kc-tooltip>
+ <kc-tooltip>{{:: 'gui-order.tooltip' | translate}}</kc-tooltip>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
- <button kc-save data-ng-disabled="!changed">Save</button>
- <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">Cancel</button>
+ <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+ <button kc-cancel data-ng-click="cancel()" data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
</div>
</div>
</form>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html
index af49b3b..5e478f6 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-stackoverflow-ext.html
@@ -1,7 +1,7 @@
<div class="form-group clearfix">
- <label class="col-md-2 control-label" for="clientId">Key <span class="required">*</span></label>
+ <label class="col-md-2 control-label" for="clientId">{{:: 'key' | translate}} <span class="required">*</span></label>
<div class="col-md-6">
<input class="form-control" id="clientId" type="text" ng-model="identityProvider.config.key" required>
</div>
- <kc-tooltip>The Key obtained from Stack Overflow client registration.</kc-tooltip>
+ <kc-tooltip>{{:: 'stackoverflow.key.tooltip' | translate}}</kc-tooltip>
</div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-list.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-list.html
index eba458a..099fe6d 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-list.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-list.html
@@ -1,11 +1,11 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
- <h1>Realms</h1>
+ <h1>{{:: 'realms' | translate}}</h1>
<table class="datatable table table-striped table-bordered">
<thead>
<tr>
- <th>Realm</th>
+ <th>{{:: 'realm' | translate}}</th>
</tr>
</thead>
<tbody>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
index a92c774..2375983 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/server-info.html
@@ -11,7 +11,7 @@
<table class="table table-striped table-bordered">
<tr>
- <td width="20%">Keycloak Version</td>
+ <td width="20%">Server Version</td>
<td>{{serverInfo.systemInfo.version}}</td>
</tr>
<tr>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
index b420f47..63669e0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/user-sessions.html
@@ -19,7 +19,7 @@
<th>IP Address</th>
<th>Started</th>
<th>Last Access</th>
- <th>Clients</th>
+ <th>{{:: 'clients' | translate}}</th>
<th data-ng-show="access.manageUsers">Action</th>
</tr>
</thead>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
index 08b76a8..54ebdae 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-provider-config.html
@@ -1,16 +1,16 @@
<div>
<div data-ng-repeat="option in properties" class="form-group" data-ng-controller="ProviderConfigCtrl">
- <label class="col-md-2 control-label">{{option.label}}</label>
+ <label class="col-md-2 control-label">{{:: option.label | translate}}</label>
<div class="col-sm-6" data-ng-hide="option.type == 'boolean' || option.type == 'List' || option.type == 'Role' || option.type == 'ClientList'">
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
</div>
<div class="col-sm-6" data-ng-show="option.type == 'boolean'">
- <input ng-model="config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchstring />
+ <input ng-model="config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchstring on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
</div>
<div class="col-sm-6" data-ng-show="option.type == 'List'">
<select ng-model="config[ option.name ]" ng-options="data for data in option.defaultValue">
- <option value="" selected> Select one... </option>
+ <option value="" selected> {{:: 'selectOne' | translate}} </option>
</select>
</div>
<div class="col-sm-6" data-ng-show="option.type == 'Role'">
@@ -19,16 +19,16 @@
<input class="form-control" type="text" data-ng-model="config[ option.name ]" >
</div>
<div class="col-sm-2">
- <button type="submit" data-ng-click="openRoleSelector(option.name, config)" class="btn btn-default" tooltip-placement="top" tooltip-trigger="mouseover mouseout" tooltip="Enter role in the textbox to the left, or click this button to browse and select the role you want">Select Role</button>
+ <button type="submit" data-ng-click="openRoleSelector(option.name, config)" class="btn btn-default" tooltip-placement="top" tooltip-trigger="mouseover mouseout" tooltip="{{:: 'selectRole.tooltip' | translate}}">{{:: 'selectRole.label' | translate}}</button>
</div>
</div>
</div>
<div class="col-sm-4" data-ng-show="option.type == 'ClientList'">
<select ng-model="config[ option.name ]" ng-options="client.clientId as client.clientId for client in clients">
- <option value="" selected> Select one... </option>
+ <option value="" selected> {{:: 'selectOne' | translate}} </option>
</select>
</div>
- <kc-tooltip>{{option.helpText}}</kc-tooltip>
+ <kc-tooltip>{{:: option.helpText | translate}}</kc-tooltip>
</div>
</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
index e16406d..6f56467 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html
@@ -1,48 +1,46 @@
<div data-ng-controller="ClientTabCtrl">
- <h1 data-ng-show="create">Add Client</h1>
+ <h1 data-ng-show="create">{{:: 'add-client' | translate}}</h1>
<h1 data-ng-hide="create">
{{client.clientId|capitalize}}
<i id="removeClient" class="pficon pficon-delete clickable" data-ng-show="access.manageClients" data-ng-click="removeClient()"></i>
</h1>
<ul class="nav nav-tabs nav-tabs-pf" data-ng-hide="create && !path[4]">
- <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">Settings</a></li>
- <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!client.publicClient && client.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">Credentials</a></li>
- <li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">SAML Keys</a></li>
- <li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">Roles</a></li>
+ <li ng-class="{active: !path[4]}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}">{{:: 'settings' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'credentials'}" data-ng-show="!client.publicClient && client.protocol != 'saml'"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/credentials">{{:: 'credentials' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'saml'}" data-ng-show="client.protocol == 'saml' && (client.attributes['saml.client.signature'] == 'true' || client.attributes['saml.encrypt'] == 'true')"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/saml/keys">{{:: 'saml-keys' | translate}}</a></li>
+ <li ng-class="{active: path[4] == 'roles'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/roles">{{:: 'roles' | translate}}</a></li>
<li ng-class="{active: path[4] == 'mappers'}" data-ng-show="!client.bearerOnly">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">Mappers</a>
- <kc-tooltip>Protocol mappers perform transformation on tokens and documents. They an do things like map user data into protocol claims, or just transform any requests going between the client and auth server.</kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/mappers">{{:: 'mappers' | translate}}</a>
+ <kc-tooltip>{{:: 'mappers.tooltip' | translate}}</kc-tooltip>
</li>
<li ng-class="{active: path[4] == 'scope-mappings'}" data-ng-show="!client.bearerOnly">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/scope-mappings">Scope</a>
- <kc-tooltip>Scope mappings allow you to restrict which user role mappings are included within the access token requested by the client.</kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/scope-mappings">{{:: 'scope' | translate}}</a>
+ <kc-tooltip>{{:: 'scope.tooltip' | translate}}</kc-tooltip>
</li>
- <li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">Revocation</a></li>
+ <li ng-class="{active: path[4] == 'revocation'}"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/revocation">{{:: 'revocation' | translate}}</a></li>
<!-- <li ng-class="{active: path[4] == 'identity-provider'}" data-ng-show="realm.identityFederationEnabled"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/identity-provider">Identity Provider</a></li> -->
<li ng-class="{active: path[4] == 'sessions'}" data-ng-show="!client.bearerOnly">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/sessions">Sessions</a>
- <kc-tooltip>View active sessions for this client. Allows you to see which users are active and when they logged in.</kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/sessions">{{:: 'sessions' | translate}}</a>
+ <kc-tooltip>{{:: 'sessions.tooltip' | translate}}</kc-tooltip>
</li>
<li ng-class="{active: path[4] == 'offline-access'}" data-ng-show="!client.bearerOnly">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/offline-access">Offline Access</a>
- <kc-tooltip>View offline sessions for this client. Allows you to see which users retrieve offline token and when they retrieve it.
- To revoke all tokens for the client, go to Revocation tab and set not before value to now.
- </kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/offline-access">{{:: 'offline-access' | translate}}</a>
+ <kc-tooltip>{{:: 'offline-access.tooltip' | translate}}</kc-tooltip>
</li>
- <li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">Clustering</a></li>
+ <li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li>
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">Installation</a>
- <kc-tooltip>Helper utility for generating various client adapter configuration formats which you can download or cut and paste to configure your clients.</kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">{{:: 'installation' | translate}}</a>
+ <kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip>
</li>
<li ng-class="{active: path[4] == 'service-account-roles'}" data-ng-show="client.serviceAccountsEnabled">
- <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/service-account-roles">Service Account Roles</a>
- <kc-tooltip>Allows you to authenticate role mappings for the service account dedicated to this client.</kc-tooltip>
+ <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/service-account-roles">{{:: 'service-account-roles' | translate}}</a>
+ <kc-tooltip>{{:: 'service-account-roles.tooltip' | translate}}</kc-tooltip>
</li>
</ul>
</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 94b0627..65ad002 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -89,6 +89,7 @@ personalInfo=Personal Info:
role_admin=Admin
role_realm-admin=Realm Admin
role_create-realm=Create realm
+role_create-client=Create client
role_view-realm=View realm
role_view-users=View users
role_view-applications=View applications
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
index 54395eb..c377b1f 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/BearerTokenRequestAuthenticator.java
@@ -115,6 +115,11 @@ public class BearerTokenRequestAuthenticator {
}
@Override
+ public int getResponseCode() {
+ return 0;
+ }
+
+ @Override
public boolean challenge(HttpFacade exchange) {
// do the same thing as client cert auth
return false;
@@ -140,6 +145,11 @@ public class BearerTokenRequestAuthenticator {
}
@Override
+ public int getResponseCode() {
+ return 401;
+ }
+
+ @Override
public boolean challenge(HttpFacade facade) {
facade.getResponse().setStatus(401);
facade.getResponse().addHeader("WWW-Authenticate", challenge);
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
index d077d7d..c6ffce5 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java
@@ -181,6 +181,11 @@ public class OAuthRequestAuthenticator {
public boolean errorPage() {
return true;
}
+
+ @Override
+ public int getResponseCode() {
+ return 403;
+ }
};
}
return new AuthChallenge() {
@@ -191,6 +196,11 @@ public class OAuthRequestAuthenticator {
}
@Override
+ public int getResponseCode() {
+ return 0;
+ }
+
+ @Override
public boolean challenge(HttpFacade exchange) {
tokenStore.saveRequest();
log.debug("Sending redirect to login page: " + redirect);
@@ -263,6 +273,11 @@ public class OAuthRequestAuthenticator {
}
@Override
+ public int getResponseCode() {
+ return code;
+ }
+
+ @Override
public boolean challenge(HttpFacade exchange) {
exchange.getResponse().setStatus(code);
return true;
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
index 94385f0..47c07c2 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/AuthChallenge.java
@@ -13,9 +13,17 @@ public interface AuthChallenge {
boolean challenge(HttpFacade exchange);
/**
- * Whether or not an error page should be displayed if possible
+ * Whether or not an error page should be displayed if possible along with the challenge
*
* @return
*/
boolean errorPage();
+
+ /**
+ * If errorPage is true, this is the response code the challenge will send. This is used by platforms
+ * that call HttpServletResponse.sendError() to forward to error page.
+ *
+ * @return
+ */
+ int getResponseCode();
}
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
index 4470c15..c4a8081 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/InMemorySessionIdMapper.java
@@ -18,6 +18,19 @@ public class InMemorySessionIdMapper implements SessionIdMapper {
ConcurrentHashMap<String, String> sessionToPrincipal = new ConcurrentHashMap<>();
@Override
+ public boolean hasSession(String id) {
+ return sessionToSso.containsKey(id) || sessionToPrincipal.containsKey(id);
+ }
+
+ @Override
+ public void clear() {
+ ssoToSession.clear();
+ sessionToSso.clear();
+ principalToSession.clear();
+ sessionToPrincipal.clear();
+ }
+
+ @Override
public Set<String> getUserSessions(String principal) {
Set<String> lookup = principalToSession.get(principal);
if (lookup == null) return null;
diff --git a/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
index 646f71b..0f87155 100755
--- a/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
+++ b/integration/adapter-spi/src/main/java/org/keycloak/adapters/SessionIdMapper.java
@@ -7,6 +7,10 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public interface SessionIdMapper {
+ boolean hasSession(String id);
+
+ void clear();
+
Set<String> getUserSessions(String principal);
String getSessionFromSSO(String sso);
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index 6d6c7be..431a646 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -9,6 +9,12 @@ import org.keycloak.admin.client.resource.RealmsResource;
import org.keycloak.admin.client.token.TokenManager;
/**
+ * Provides a Keycloak client. By default, this implementation uses a {@link ResteasyClient RESTEasy client} with the
+ * default {@link ResteasyClientBuilder} settings. To customize the underling client, use a {@link KeycloakBuilder} to
+ * create a Keycloak client.
+ *
+ * @see KeycloakBuilder
+ *
* @author rodrigo.sasaki@icarros.com.br
*/
public class Keycloak {
@@ -18,9 +24,9 @@ public class Keycloak {
private final ResteasyWebTarget target;
private final ResteasyClient client;
- private Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
+ Keycloak(String serverUrl, String realm, String username, String password, String clientId, String clientSecret, ResteasyClient resteasyClient){
config = new Config(serverUrl, realm, username, password, clientId, clientSecret);
- client = new ResteasyClientBuilder().build();
+ client = resteasyClient != null ? resteasyClient : new ResteasyClientBuilder().build();
tokenManager = new TokenManager(config, client);
@@ -30,11 +36,11 @@ public class Keycloak {
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId, String clientSecret){
- return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret);
+ return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, null);
}
public static Keycloak getInstance(String serverUrl, String realm, String username, String password, String clientId){
- return new Keycloak(serverUrl, realm, username, password, clientId, null);
+ return new Keycloak(serverUrl, realm, username, password, clientId, null, null);
}
public RealmsResource realms(){
@@ -49,6 +55,9 @@ public class Keycloak {
return tokenManager;
}
+ /**
+ * Closes the underlying client. After calling this method, this <code>Keycloak</code> instance cannot be reused.
+ */
public void close() {
client.close();
}
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
new file mode 100644
index 0000000..c4a1c33
--- /dev/null
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/KeycloakBuilder.java
@@ -0,0 +1,107 @@
+package org.keycloak.admin.client;
+
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+
+/**
+ * Provides a {@link Keycloak} client builder with the ability to customize the underlying
+ * {@link ResteasyClient RESTEasy client} used to communicate with the Keycloak server.
+ *
+ * <p>Example usage with a connection pool size of 20:</p>
+ *
+ * <pre>
+ * Keycloak keycloak = KeycloakBuilder.builder()
+ * .serverUrl("https:/sso.example.com/auth")
+ * .realm("realm")
+ * .username("user")
+ * .password("pass")
+ * .clientId("client")
+ * .clientSecret("secret")
+ * .resteasyClient(new ResteasyClientBuilder().connectionPoolSize(20).build())
+ * .build();
+ * </pre>
+ *
+ * @author Scott Rossillo
+ * @see ResteasyClientBuilder
+ */
+public class KeycloakBuilder {
+ private String serverUrl;
+ private String realm;
+ private String username;
+ private String password;
+ private String clientId;
+ private String clientSecret;
+ private ResteasyClient resteasyClient;
+
+ public KeycloakBuilder serverUrl(String serverUrl) {
+ this.serverUrl = serverUrl;
+ return this;
+ }
+
+ public KeycloakBuilder realm(String realm) {
+ this.realm = realm;
+ return this;
+ }
+
+ public KeycloakBuilder username(String username) {
+ this.username = username;
+ return this;
+ }
+
+ public KeycloakBuilder password(String password) {
+ this.password = password;
+ return this;
+ }
+
+ public KeycloakBuilder clientId(String clientId) {
+ this.clientId = clientId;
+ return this;
+ }
+
+ public KeycloakBuilder clientSecret(String clientSecret) {
+ this.clientSecret = clientSecret;
+ return this;
+ }
+
+ public KeycloakBuilder resteasyClient(ResteasyClient resteasyClient) {
+ this.resteasyClient = resteasyClient;
+ return this;
+ }
+
+ /**
+ * Builds a new Keycloak client from this builder.
+ */
+ public Keycloak build() {
+ if (serverUrl == null) {
+ throw new IllegalStateException("serverUrl required");
+ }
+
+ if (realm == null) {
+ throw new IllegalStateException("realm required");
+ }
+
+ if (username == null) {
+ throw new IllegalStateException("username required");
+ }
+
+ if (password == null) {
+ throw new IllegalStateException("password required");
+ }
+
+ if (clientId == null) {
+ throw new IllegalStateException("clientId required");
+ }
+
+ return new Keycloak(serverUrl, realm, username, password, clientId, clientSecret, resteasyClient);
+ }
+
+ private KeycloakBuilder() {
+ }
+
+ /**
+ * Returns a new Keycloak builder.
+ */
+ public static KeycloakBuilder builder() {
+ return new KeycloakBuilder();
+ }
+}
diff --git a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
index c11f30a..6b0233c 100755
--- a/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
+++ b/integration/jetty/jetty-core/src/main/java/org/keycloak/adapters/jetty/core/AbstractKeycloakJettyAuthenticator.java
@@ -262,16 +262,16 @@ public abstract class AbstractKeycloakJettyAuthenticator extends LoginAuthentica
}
AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
+ challenge.challenge(facade);
if (challenge.errorPage() && errorPage != null) {
Response response = (Response)res;
try {
- response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
+ response.sendRedirect(response.encodeRedirectURL(URIUtil.addPaths(request.getContextPath(), errorPage)));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
- challenge.challenge(facade);
}
return Authentication.SEND_CONTINUE;
}
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index f384e7b..d189a36 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -183,6 +183,18 @@
return url;
}
+ kc.register = function (options) {
+ return adapter.register(options);
+ }
+
+ kc.createRegisterUrl = function(options) {
+ if (!options) {
+ options = {};
+ }
+ options.action = 'register';
+ return kc.createLoginUrl(options);
+ }
+
kc.createAccountUrl = function(options) {
var url = getRealmUrl()
+ '/account'
@@ -760,6 +772,11 @@
return createPromise().promise;
},
+ register: function(options) {
+ window.location.href = kc.createRegisterUrl(options);
+ return createPromise().promise;
+ },
+
accountManagement : function() {
window.location.href = kc.createAccountUrl();
return createPromise().promise;
@@ -858,6 +875,16 @@
return promise.promise;
},
+ register : function() {
+ var registerUrl = kc.createRegisterUrl();
+ var ref = window.open(registerUrl, '_blank', 'location=no');
+ ref.addEventListener('loadstart', function(event) {
+ if (event.url.indexOf('http://localhost') == 0) {
+ ref.close();
+ }
+ });
+ },
+
accountManagement : function() {
var accountUrl = kc.createAccountUrl();
var ref = window.open(accountUrl, '_blank', 'location=no');
integration/pom.xml 2(+2 -0)
diff --git a/integration/pom.xml b/integration/pom.xml
index e4b7f27..6257c28 100755
--- a/integration/pom.xml
+++ b/integration/pom.xml
@@ -15,6 +15,7 @@
<modules>
<module>adapter-spi</module>
+ <module>servlet-adapter-spi</module>
<module>adapter-core</module>
<module>jaxrs-oauth-client</module>
<module>servlet-oauth-client</module>
@@ -25,6 +26,7 @@
<module>undertow-adapter-spi</module>
<module>undertow</module>
<module>wildfly</module>
+ <module>servlet-filter</module>
<module>js</module>
<module>installed</module>
<module>admin-client</module>
diff --git a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java
new file mode 100755
index 0000000..b6ddbc7
--- /dev/null
+++ b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/FilterSessionStore.java
@@ -0,0 +1,400 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.adapters.AdapterSessionStore;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
+import org.keycloak.util.Encode;
+import org.keycloak.util.MultivaluedHashMap;
+
+import javax.servlet.ServletException;
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.io.UnsupportedEncodingException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class FilterSessionStore implements AdapterSessionStore {
+ public static final String REDIRECT_URI = "__REDIRECT_URI";
+ public static final String SAVED_METHOD = "__SAVED_METHOD";
+ public static final String SAVED_HEADERS = "__SAVED_HEADERS";
+ public static final String SAVED_BODY = "__SAVED_BODY";
+ protected final HttpServletRequest request;
+ protected final HttpFacade facade;
+ protected final int maxBuffer;
+ protected byte[] restoredBuffer = null;
+ protected boolean needRequestRestore;
+
+ public FilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer) {
+ this.request = request;
+ this.facade = facade;
+ this.maxBuffer = maxBuffer;
+ }
+
+ public void clearSavedRequest(HttpSession session) {
+ session.removeAttribute(REDIRECT_URI);
+ session.removeAttribute(SAVED_METHOD);
+ session.removeAttribute(SAVED_HEADERS);
+ session.removeAttribute(SAVED_BODY);
+ }
+
+ public void servletRequestLogout() {
+
+ }
+
+ public static String getCharsetFromContentType(String contentType) {
+
+ if (contentType == null)
+ return (null);
+ int start = contentType.indexOf("charset=");
+ if (start < 0)
+ return (null);
+ String encoding = contentType.substring(start + 8);
+ int end = encoding.indexOf(';');
+ if (end >= 0)
+ encoding = encoding.substring(0, end);
+ encoding = encoding.trim();
+ if ((encoding.length() > 2) && (encoding.startsWith("\""))
+ && (encoding.endsWith("\"")))
+ encoding = encoding.substring(1, encoding.length() - 1);
+ return (encoding.trim());
+
+ }
+
+
+ public HttpServletRequestWrapper buildWrapper(HttpSession session, final KeycloakAccount account) {
+ if (needRequestRestore) {
+ final String method = (String)session.getAttribute(SAVED_METHOD);
+ final byte[] body = (byte[])session.getAttribute(SAVED_BODY);
+ final MultivaluedHashMap<String, String> headers = (MultivaluedHashMap<String, String>)session.getAttribute(SAVED_HEADERS);
+ clearSavedRequest(session);
+ HttpServletRequestWrapper wrapper = new HttpServletRequestWrapper(request) {
+ protected MultivaluedHashMap<String, String> parameters;
+
+ MultivaluedHashMap<String, String> getParams() {
+ if (parameters != null) return parameters;
+ String contentType = getContentType();
+ contentType = contentType.toLowerCase();
+ if (contentType.startsWith("application/x-www-form-urlencoded")) {
+ ByteArrayInputStream is = new ByteArrayInputStream(body);
+ try {
+ parameters = parseForm(is);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return parameters;
+
+ }
+ @Override
+ public boolean isUserInRole(String role) {
+ return account.getRoles().contains(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return account.getPrincipal();
+ }
+
+ @Override
+ public String getMethod() {
+ if (needRequestRestore) {
+ return method;
+ } else {
+ return super.getMethod();
+
+ }
+ }
+
+ @Override
+ public String getHeader(String name) {
+ if (needRequestRestore && headers != null) {
+ return headers.getFirst(name.toLowerCase());
+ }
+ return super.getHeader(name);
+ }
+
+ @Override
+ public Enumeration<String> getHeaders(String name) {
+ if (needRequestRestore && headers != null) {
+ List<String> values = headers.getList(name.toLowerCase());
+ if (values == null) return Collections.emptyEnumeration();
+ else return Collections.enumeration(values);
+ }
+ return super.getHeaders(name);
+ }
+
+ @Override
+ public Enumeration<String> getHeaderNames() {
+ if (needRequestRestore && headers != null) {
+ return Collections.enumeration(headers.keySet());
+ }
+ return super.getHeaderNames();
+ }
+
+ @Override
+ public ServletInputStream getInputStream() throws IOException {
+
+ if (needRequestRestore && body != null) {
+ final ByteArrayInputStream is = new ByteArrayInputStream(body);
+ return new ServletInputStream() {
+ @Override
+ public int read() throws IOException {
+ return is.read();
+ }
+ };
+ }
+ return super.getInputStream();
+ }
+
+ @Override
+ public void logout() throws ServletException {
+ servletRequestLogout();
+ }
+
+ @Override
+ public long getDateHeader(String name) {
+ if (!needRequestRestore) return super.getDateHeader(name);
+ throw new RuntimeException("This method is not supported in a restored authenticated request");
+ }
+
+ @Override
+ public int getIntHeader(String name) {
+ if (!needRequestRestore) return super.getIntHeader(name);
+ String value = getHeader(name);
+ if (value == null) return -1;
+ return Integer.valueOf(value);
+
+ }
+
+ @Override
+ public String[] getParameterValues(String name) {
+ if (!needRequestRestore) return super.getParameterValues(name);
+ MultivaluedHashMap<String, String> formParams = getParams();
+ if (formParams == null) {
+ return super.getParameterValues(name);
+ }
+ String[] values = request.getParameterValues(name);
+ List<String> list = new LinkedList<>();
+ if (values != null) {
+ for (String val : values) list.add(val);
+ }
+ List<String> vals = formParams.get(name);
+ if (vals != null) list.addAll(vals);
+ return list.toArray(new String[list.size()]);
+ }
+
+ @Override
+ public Enumeration<String> getParameterNames() {
+ if (!needRequestRestore) return super.getParameterNames();
+ MultivaluedHashMap<String, String> formParams = getParams();
+ if (formParams == null) {
+ return super.getParameterNames();
+ }
+ Set<String> names = new HashSet<>();
+ Enumeration<String> qnames = super.getParameterNames();
+ while (qnames.hasMoreElements()) names.add(qnames.nextElement());
+ names.addAll(formParams.keySet());
+ return Collections.enumeration(names);
+
+ }
+
+ @Override
+ public Map<String, String[]> getParameterMap() {
+ if (!needRequestRestore) return super.getParameterMap();
+ MultivaluedHashMap<String, String> formParams = getParams();
+ if (formParams == null) {
+ return super.getParameterMap();
+ }
+ Map<String, String[]> map = new HashMap<>();
+ Enumeration<String> names = getParameterNames();
+ while (names.hasMoreElements()) {
+ String name = names.nextElement();
+ String[] values = getParameterValues(name);
+ if (values != null) {
+ map.put(name, values);
+ }
+ }
+ return map;
+ }
+
+ @Override
+ public String getParameter(String name) {
+ if (!needRequestRestore) return super.getParameter(name);
+ String param = super.getParameter(name);
+ if (param != null) return param;
+ MultivaluedHashMap<String, String> formParams = getParams();
+ if (formParams == null) {
+ return null;
+ }
+ return formParams.getFirst(name);
+
+ }
+
+ @Override
+ public BufferedReader getReader() throws IOException {
+ if (!needRequestRestore) return super.getReader();
+ return new BufferedReader(new InputStreamReader(getInputStream()));
+ }
+
+ @Override
+ public int getContentLength() {
+ if (!needRequestRestore) return super.getContentLength();
+ String header = getHeader("content-length");
+ if (header == null) return -1;
+ return Integer.valueOf(header);
+ }
+
+ @Override
+ public String getContentType() {
+ if (!needRequestRestore) return super.getContentType();
+ return getHeader("content-type");
+ }
+
+ @Override
+ public String getCharacterEncoding() {
+ if (!needRequestRestore) return super.getCharacterEncoding();
+ return getCharsetFromContentType(getContentType());
+ }
+
+ };
+ return wrapper;
+ } else {
+ return new HttpServletRequestWrapper(request) {
+ @Override
+ public boolean isUserInRole(String role) {
+ return account.getRoles().contains(role);
+ }
+
+ @Override
+ public Principal getUserPrincipal() {
+ return account.getPrincipal();
+ }
+
+ @Override
+ public void logout() throws ServletException {
+ servletRequestLogout();
+ }
+
+
+ };
+ }
+ }
+
+ public String getRedirectUri() {
+ HttpSession session = request.getSession(true);
+ return (String)session.getAttribute(REDIRECT_URI);
+ }
+
+ @Override
+ public boolean restoreRequest() {
+ HttpSession session = request.getSession(false);
+ if (session == null) return false;
+ return session.getAttribute(REDIRECT_URI) != null;
+ }
+
+ public static MultivaluedHashMap<String, String> parseForm(InputStream entityStream)
+ throws IOException
+ {
+ char[] buffer = new char[100];
+ StringBuffer buf = new StringBuffer();
+ BufferedReader reader = new BufferedReader(new InputStreamReader(entityStream));
+
+ int wasRead = 0;
+ do
+ {
+ wasRead = reader.read(buffer, 0, 100);
+ if (wasRead > 0) buf.append(buffer, 0, wasRead);
+ } while (wasRead > -1);
+
+ String form = buf.toString();
+
+ MultivaluedHashMap<String, String> formData = new MultivaluedHashMap<String, String>();
+ if ("".equals(form)) return formData;
+
+ String[] params = form.split("&");
+
+ for (String param : params)
+ {
+ if (param.indexOf('=') >= 0)
+ {
+ String[] nv = param.split("=");
+ String val = nv.length > 1 ? nv[1] : "";
+ formData.add(Encode.decode(nv[0]), Encode.decode(val));
+ }
+ else
+ {
+ formData.add(Encode.decode(param), "");
+ }
+ }
+ return formData;
+ }
+
+
+
+ @Override
+ public void saveRequest() {
+ HttpSession session = request.getSession(true);
+ session.setAttribute(REDIRECT_URI, facade.getRequest().getURI());
+ session.setAttribute(SAVED_METHOD, request.getMethod());
+ MultivaluedHashMap<String, String> headers = new MultivaluedHashMap<>();
+ Enumeration<String> names = request.getHeaderNames();
+ while (names.hasMoreElements()) {
+ String name = names.nextElement();
+ Enumeration<String> values = request.getHeaders(name);
+ while (values.hasMoreElements()) {
+ headers.add(name.toLowerCase(), values.nextElement());
+ }
+ }
+ session.setAttribute(SAVED_HEADERS, headers);
+ if (request.getMethod().equalsIgnoreCase("GET")) {
+ return;
+ }
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+ byte[] buffer = new byte[4096];
+ int bytesRead;
+ int totalRead = 0;
+ try {
+ InputStream is = request.getInputStream();
+
+ while ( (bytesRead = is.read(buffer) ) >= 0) {
+ os.write(buffer);
+ totalRead += bytesRead;
+ if (totalRead > maxBuffer) {
+ throw new RuntimeException("max buffer reached on a saved request");
+ }
+
+ }
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ byte[] body = os.toByteArray();
+ // Only save the request body if there is something to save
+ if (body.length > 0) {
+ session.setAttribute(SAVED_BODY, body);
+ }
+
+
+ }
+
+}
diff --git a/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java
new file mode 100755
index 0000000..f12c9ca
--- /dev/null
+++ b/integration/servlet-adapter-spi/src/main/java/org/keycloak/adapters/servlet/ServletHttpFacade.java
@@ -0,0 +1,196 @@
+package org.keycloak.adapters.servlet;
+
+import org.bouncycastle.ocsp.Req;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.util.KeycloakUriBuilder;
+import org.keycloak.util.MultivaluedHashMap;
+import org.keycloak.util.ServerCookie;
+import org.keycloak.util.UriUtils;
+
+import javax.security.cert.X509Certificate;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.URI;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ServletHttpFacade implements HttpFacade {
+ protected final RequestFacade requestFacade = new RequestFacade();
+ protected final ResponseFacade responseFacade = new ResponseFacade();
+ protected HttpServletRequest request;
+ protected HttpServletResponse response;
+ protected MultivaluedHashMap<String, String> queryParameters;
+
+ public ServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
+ this.request = request;
+ this.response = response;
+ }
+
+ protected class RequestFacade implements Request {
+ @Override
+ public String getMethod() {
+ return request.getMethod();
+ }
+
+ @Override
+ public String getURI() {
+ StringBuffer buf = request.getRequestURL();
+ if (request.getQueryString() != null) {
+ buf.append('?').append(request.getQueryString());
+ }
+ return buf.toString();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return request.isSecure();
+ }
+
+ @Override
+ public String getFirstParam(String param) {
+ return request.getParameter(param);
+ }
+
+ @Override
+ public String getQueryParamValue(String param) {
+ if (queryParameters == null) {
+ queryParameters = UriUtils.decodeQueryString(request.getQueryString());
+ }
+ return queryParameters.getFirst(param);
+ }
+
+ public MultivaluedHashMap<String, String> getQueryParameters() {
+ if (queryParameters == null) {
+ queryParameters = UriUtils.decodeQueryString(request.getQueryString());
+ }
+ return queryParameters;
+ }
+
+ @Override
+ public Cookie getCookie(String cookieName) {
+ if (request.getCookies() == null) return null;
+ javax.servlet.http.Cookie cookie = null;
+ for (javax.servlet.http.Cookie c : request.getCookies()) {
+ if (c.getName().equals(cookieName)) {
+ cookie = c;
+ break;
+ }
+ }
+ if (cookie == null) return null;
+ return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return request.getHeader(name);
+ }
+
+ @Override
+ public List<String> getHeaders(String name) {
+ Enumeration<String> values = request.getHeaders(name);
+ List<String> list = new LinkedList<>();
+ while (values.hasMoreElements()) list.add(values.nextElement());
+ return list;
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ try {
+ return request.getInputStream();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ return request.getRemoteAddr();
+ }
+ }
+ public boolean isEnded() {
+ return responseFacade.isEnded();
+ }
+
+ protected class ResponseFacade implements Response {
+ protected boolean ended;
+
+ @Override
+ public void setStatus(int status) {
+ response.setStatus(status);
+ }
+
+ @Override
+ public void addHeader(String name, String value) {
+ response.addHeader(name, value);
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ response.setHeader(name, value);
+ }
+
+ @Override
+ public void resetCookie(String name, String path) {
+ setCookie(name, "", path, null, 0, false, false);
+ }
+
+ @Override
+ public void setCookie(String name, String value, String path, String domain, int maxAge, boolean secure, boolean httpOnly) {
+ StringBuffer cookieBuf = new StringBuffer();
+ ServerCookie.appendCookieValue(cookieBuf, 1, name, value, path, domain, null, maxAge, secure, httpOnly);
+ String cookie = cookieBuf.toString();
+ response.addHeader("Set-Cookie", cookie);
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ try {
+ return response.getOutputStream();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void sendError(int code, String message) {
+ try {
+ response.sendError(code, message);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void end() {
+ ended = true;
+ }
+
+ public boolean isEnded() {
+ return ended;
+ }
+ }
+
+
+ @Override
+ public Request getRequest() {
+ return requestFacade;
+ }
+
+ @Override
+ public Response getResponse() {
+ return responseFacade;
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain() {
+ throw new IllegalStateException("Not supported yet");
+ }
+}
integration/servlet-filter/pom.xml 83(+83 -0)
diff --git a/integration/servlet-filter/pom.xml b/integration/servlet-filter/pom.xml
new file mode 100755
index 0000000..8448d59
--- /dev/null
+++ b/integration/servlet-filter/pom.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-servlet-filter-adapter</artifactId>
+ <name>Keycloak Servlet Filter Adapter Integration</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-core-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-mapper-asl</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.codehaus.jackson</groupId>
+ <artifactId>jackson-xc</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java
new file mode 100755
index 0000000..70cb7e9
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/FilterRequestAuthenticator.java
@@ -0,0 +1,86 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.AdapterUtils;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+import java.security.Principal;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:ungarida@gmail.com">Davide Ungari</a>
+ * @version $Revision: 1 $
+ */
+public class FilterRequestAuthenticator extends RequestAuthenticator {
+ private static final Logger log = Logger.getLogger(""+FilterRequestAuthenticator.class);
+ protected HttpServletRequest request;
+
+ public FilterRequestAuthenticator(KeycloakDeployment deployment,
+ AdapterTokenStore tokenStore,
+ OIDCHttpFacade facade,
+ HttpServletRequest request,
+ int sslRedirectPort) {
+ super(facade, deployment, tokenStore, sslRedirectPort);
+ this.request = request;
+ }
+
+ @Override
+ protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+ return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
+ }
+
+ @Override
+ protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> skp) {
+ final RefreshableKeycloakSecurityContext securityContext = skp.getKeycloakSecurityContext();
+ final Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
+ OidcKeycloakAccount account = new OidcKeycloakAccount() {
+
+ @Override
+ public Principal getPrincipal() {
+ return skp;
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return roles;
+ }
+
+ @Override
+ public KeycloakSecurityContext getKeycloakSecurityContext() {
+ return securityContext;
+ }
+
+ };
+
+ request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+ this.tokenStore.saveAccountInfo(account);
+ }
+
+ @Override
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
+ RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
+ Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
+ if (log.isLoggable(Level.FINE)) {
+ log.fine("Completing bearer authentication. Bearer roles: " + roles);
+ }
+ request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+ }
+
+ @Override
+ protected String getHttpSessionId(boolean create) {
+ HttpSession session = request.getSession(create);
+ return session != null ? session.getId() : null;
+ }
+
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java
new file mode 100755
index 0000000..1dab8c3
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/KeycloakOIDCFilter.java
@@ -0,0 +1,162 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.AuthenticatedActionsHandler;
+import org.keycloak.adapters.InMemorySessionIdMapper;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.adapters.NodesRegistrationManagement;
+import org.keycloak.adapters.PreAuthActionsHandler;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.UserSessionManagement;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class KeycloakOIDCFilter implements Filter {
+ protected AdapterDeploymentContext deploymentContext;
+ protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
+ protected NodesRegistrationManagement nodesRegistrationManagement;
+ private final static Logger log = Logger.getLogger(""+KeycloakOIDCFilter.class);
+
+ @Override
+ public void init(final FilterConfig filterConfig) throws ServletException {
+ String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver");
+ if (configResolverClass != null) {
+ try {
+ KeycloakConfigResolver configResolver = (KeycloakConfigResolver) getClass().getClassLoader().loadClass(configResolverClass).newInstance();
+ deploymentContext = new AdapterDeploymentContext(configResolver);
+ log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
+ } catch (Exception ex) {
+ log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
+ deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ String fp = filterConfig.getInitParameter("keycloak.config.file");
+ InputStream is = null;
+ if (fp != null) {
+ try {
+ is = new FileInputStream(fp);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ String path = "/WEB-INF/keycloak.json";
+ String pathParam = filterConfig.getInitParameter("keycloak.config.path");
+ if (pathParam != null) path = pathParam;
+ is = filterConfig.getServletContext().getResourceAsStream(path);
+ }
+ KeycloakDeployment kd;
+ if (is == null) {
+ log.fine("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ kd = new KeycloakDeployment();
+ } else {
+ kd = KeycloakDeploymentBuilder.build(is);
+ }
+ deploymentContext = new AdapterDeploymentContext(kd);
+ log.fine("Keycloak is using a per-deployment configuration.");
+ }
+ filterConfig.getServletContext().setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
+ nodesRegistrationManagement = new NodesRegistrationManagement();
+ }
+
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+ OIDCServletHttpFacade facade = new OIDCServletHttpFacade(request, response);
+ KeycloakDeployment deployment = deploymentContext.resolveDeployment(facade);
+ if (deployment == null || !deployment.isConfigured()) {
+ response.sendError(403);
+ log.fine("deployment not configured");
+ return;
+ }
+
+ PreAuthActionsHandler preActions = new PreAuthActionsHandler(new UserSessionManagement() {
+ @Override
+ public void logoutAll() {
+ if (idMapper != null) {
+ idMapper.clear();
+ }
+ }
+
+ @Override
+ public void logoutHttpSessions(List<String> ids) {
+ for (String id : ids) {
+ idMapper.removeSession(id);
+ }
+
+ }
+ }, deploymentContext, facade);
+
+ if (preActions.handleRequest()) {
+ return;
+ }
+
+
+ nodesRegistrationManagement.tryRegister(deployment);
+ OIDCFilterSessionStore tokenStore = new OIDCFilterSessionStore(request, facade, 100000, deployment, idMapper);
+ tokenStore.checkCurrentToken();
+
+
+ FilterRequestAuthenticator authenticator = new FilterRequestAuthenticator(deployment, tokenStore, facade, request, 8443);
+ AuthOutcome outcome = authenticator.authenticate();
+ if (outcome == AuthOutcome.AUTHENTICATED) {
+ log.fine("AUTHENTICATED");
+ if (facade.isEnded()) {
+ return;
+ }
+ AuthenticatedActionsHandler actions = new AuthenticatedActionsHandler(deployment, facade);
+ if (actions.handledRequest()) {
+ return;
+ } else {
+ HttpServletRequestWrapper wrapper = tokenStore.buildWrapper();
+ chain.doFilter(wrapper, res);
+ return;
+ }
+ }
+ AuthChallenge challenge = authenticator.getChallenge();
+ if (challenge != null) {
+ log.fine("challenge");
+ challenge.challenge(facade);
+ if (challenge.errorPage()) {
+ response.sendError(challenge.getResponseCode());
+ return;
+ }
+ log.fine("sending challenge");
+ return;
+ }
+ response.sendError(403);
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java
new file mode 100755
index 0000000..bab5cbe
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCFilterSessionStore.java
@@ -0,0 +1,167 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.SessionIdMapper;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+import java.io.Serializable;
+import java.security.Principal;
+import java.util.Set;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCFilterSessionStore extends FilterSessionStore implements AdapterTokenStore {
+ protected final KeycloakDeployment deployment;
+ private static final Logger log = Logger.getLogger("" + OIDCFilterSessionStore.class);
+ protected final SessionIdMapper idMapper;
+
+ public OIDCFilterSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer, KeycloakDeployment deployment, SessionIdMapper idMapper) {
+ super(request, facade, maxBuffer);
+ this.deployment = deployment;
+ this.idMapper = idMapper;
+ }
+
+ public HttpServletRequestWrapper buildWrapper() {
+ HttpSession session = request.getSession();
+ KeycloakAccount account = (KeycloakAccount)session.getAttribute((KeycloakAccount.class.getName()));
+ return buildWrapper(session, account);
+ }
+
+ @Override
+ public void checkCurrentToken() {
+ HttpSession httpSession = request.getSession(false);
+ if (httpSession == null) return;
+ SerializableKeycloakAccount account = (SerializableKeycloakAccount)httpSession.getAttribute(KeycloakAccount.class.getName());
+ if (account == null) {
+ return;
+ }
+
+ RefreshableKeycloakSecurityContext session = account.getKeycloakSecurityContext();
+ if (session == null) return;
+
+ // just in case session got serialized
+ if (session.getDeployment() == null) session.setCurrentRequestInfo(deployment, this);
+
+ if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) return;
+
+ // FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
+ // not be updated
+ boolean success = session.refreshExpiredToken(false);
+ if (success && session.isActive()) return;
+
+ // Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
+ //log.fine("Cleanup and expire session " + httpSession.getId() + " after failed refresh");
+ cleanSession(httpSession);
+ httpSession.invalidate();
+ }
+
+ protected void cleanSession(HttpSession session) {
+ session.removeAttribute(KeycloakAccount.class.getName());
+ clearSavedRequest(session);
+ }
+
+ @Override
+ public boolean isCached(RequestAuthenticator authenticator) {
+ HttpSession httpSession = request.getSession(false);
+ if (httpSession == null) return false;
+ SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
+ if (account == null) {
+ return false;
+ }
+
+ log.fine("remote logged in already. Establish state from session");
+
+ RefreshableKeycloakSecurityContext securityContext = account.getKeycloakSecurityContext();
+
+ if (!deployment.getRealm().equals(securityContext.getRealm())) {
+ log.fine("Account from cookie is from a different realm than for the request.");
+ cleanSession(httpSession);
+ return false;
+ }
+
+ if (idMapper != null && !idMapper.hasSession(httpSession.getId())) {
+ cleanSession(httpSession);
+ return false;
+ }
+
+
+ securityContext.setCurrentRequestInfo(deployment, this);
+ request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
+ needRequestRestore = restoreRequest();
+ return true;
+ }
+
+ public static class SerializableKeycloakAccount implements OidcKeycloakAccount, Serializable {
+ protected Set<String> roles;
+ protected Principal principal;
+ protected RefreshableKeycloakSecurityContext securityContext;
+
+ public SerializableKeycloakAccount(Set<String> roles, Principal principal, RefreshableKeycloakSecurityContext securityContext) {
+ this.roles = roles;
+ this.principal = principal;
+ this.securityContext = securityContext;
+ }
+
+ @Override
+ public Principal getPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return roles;
+ }
+
+ @Override
+ public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
+ return securityContext;
+ }
+ }
+
+ @Override
+ public void saveAccountInfo(OidcKeycloakAccount account) {
+ RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) account.getKeycloakSecurityContext();
+ Set<String> roles = account.getRoles();
+
+ SerializableKeycloakAccount sAccount = new SerializableKeycloakAccount(roles, account.getPrincipal(), securityContext);
+ HttpSession httpSession = request.getSession();
+ httpSession.setAttribute(KeycloakAccount.class.getName(), sAccount);
+ if (idMapper != null) idMapper.map(account.getKeycloakSecurityContext().getToken().getClientSession(), account.getPrincipal().getName(), httpSession.getId());
+ //String username = securityContext.getToken().getSubject();
+ //log.fine("userSessionManagement.login: " + username);
+ }
+
+ @Override
+ public void logout() {
+ HttpSession httpSession = request.getSession(false);
+ if (httpSession != null) {
+ SerializableKeycloakAccount account = (SerializableKeycloakAccount) httpSession.getAttribute(KeycloakAccount.class.getName());
+ if (account != null) {
+ account.getKeycloakSecurityContext().logout(deployment);
+ }
+ cleanSession(httpSession);
+ }
+ }
+
+ @Override
+ public void servletRequestLogout() {
+ logout();
+ }
+
+ @Override
+ public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
+ // no-op
+ }
+}
diff --git a/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java
new file mode 100755
index 0000000..1232625
--- /dev/null
+++ b/integration/servlet-filter/src/main/java/org/keycloak/adapters/servlet/OIDCServletHttpFacade.java
@@ -0,0 +1,23 @@
+package org.keycloak.adapters.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.OIDCHttpFacade;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class OIDCServletHttpFacade extends ServletHttpFacade implements OIDCHttpFacade {
+
+ public OIDCServletHttpFacade(HttpServletRequest request, HttpServletResponse response) {
+ super(request, response);
+ }
+
+ @Override
+ public KeycloakSecurityContext getSecurityContext() {
+ return (KeycloakSecurityContext)request.getAttribute(KeycloakSecurityContext.class.getName());
+ }
+}
diff --git a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
index 3347978..51b89f8 100755
--- a/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
+++ b/integration/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/tomcat/AbstractKeycloakAuthenticatorValve.java
@@ -77,6 +77,13 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
request.setUserPrincipal(null);
}
+ protected void beforeStop() {
+ if (nodesRegistrationManagement != null) {
+ nodesRegistrationManagement.stop();
+ }
+ }
+
+
@SuppressWarnings("UseSpecificCatch")
public void keycloakInit() {
// Possible scenarios:
@@ -119,11 +126,6 @@ public abstract class AbstractKeycloakAuthenticatorValve extends FormAuthenticat
nodesRegistrationManagement = new NodesRegistrationManagement();
}
- protected void beforeStop() {
- if (nodesRegistrationManagement != null) {
- nodesRegistrationManagement.stop();
- }
- }
private static InputStream getJSONFromServletContext(ServletContext servletContext) {
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
index d668ae0..ca47f3e 100644
--- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
@@ -70,6 +70,15 @@ public class MigrateTo1_6_0 {
if ((adminConsoleClient != null) && !localeMapperAdded(adminConsoleClient)) {
adminConsoleClient.addProtocolMapper(localeMapper);
}
+
+ ClientModel client = realm.getMasterAdminClient();
+ if (client.getRole(AdminRoles.CREATE_CLIENT) == null) {
+ RoleModel role = client.addRole(AdminRoles.CREATE_CLIENT);
+ role.setDescription("${role_" + AdminRoles.CREATE_CLIENT + "}");
+ role.setScopeParamRequired(false);
+
+ realm.getRole(AdminRoles.ADMIN).addCompositeRole(role);
+ }
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/AdminRoles.java b/model/api/src/main/java/org/keycloak/models/AdminRoles.java
index c067a1d..2aa91df 100755
--- a/model/api/src/main/java/org/keycloak/models/AdminRoles.java
+++ b/model/api/src/main/java/org/keycloak/models/AdminRoles.java
@@ -13,6 +13,7 @@ public class AdminRoles {
public static String REALM_ADMIN = "realm-admin";
public static String CREATE_REALM = "create-realm";
+ public static String CREATE_CLIENT = "create-client";
public static String VIEW_REALM = "view-realm";
public static String VIEW_USERS = "view-users";
@@ -26,6 +27,6 @@ public class AdminRoles {
public static String MANAGE_CLIENTS = "manage-clients";
public static String MANAGE_EVENTS = "manage-events";
- public static String[] ALL_REALM_ROLES = {VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
+ public static String[] ALL_REALM_ROLES = {CREATE_CLIENT, VIEW_REALM, VIEW_USERS, VIEW_CLIENTS, VIEW_EVENTS, VIEW_IDENTITY_PROVIDERS, MANAGE_REALM, MANAGE_USERS, MANAGE_CLIENTS, MANAGE_EVENTS, MANAGE_IDENTITY_PROVIDERS};
}
pom.xml 77(+75 -2)
diff --git a/pom.xml b/pom.xml
index 3ef78c3..65968cb 100755
--- a/pom.xml
+++ b/pom.xml
@@ -136,7 +136,7 @@
<modules>
<module>common</module>
<module>core</module>
- <module>core-jaxrs</module>
+ <module>client-api</module>
<module>connections</module>
<module>dependencies</module>
<module>events</module>
@@ -652,7 +652,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
+ <artifactId>keycloak-client-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@@ -764,6 +764,11 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-adapter-spi</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-adapter-core</artifactId>
<version>${project.version}</version>
</dependency>
@@ -889,11 +894,31 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-filter-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-tomcat6-adapter</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-subsystem</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-tomcat7-adapter</artifactId>
<version>${project.version}</version>
</dependency>
@@ -1100,6 +1125,12 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-modules</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-as7-modules</artifactId>
<version>${project.version}</version>
<type>zip</type>
@@ -1255,6 +1286,48 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat6-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat7-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat8-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty81-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-jetty92-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-eap6-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-testsuite-integration</artifactId>
<version>${project.version}</version>
</dependency>
saml/client-adapter/as7-eap6/adapter/pom.xml 101(+101 -0)
diff --git a/saml/client-adapter/as7-eap6/adapter/pom.xml b/saml/client-adapter/as7-eap6/adapter/pom.xml
new file mode 100755
index 0000000..0683343
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/adapter/pom.xml
@@ -0,0 +1,101 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-as7-adapter</artifactId>
+ <name>Keycloak SAML AS7 Integration</name>
+ <description/>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.web</groupId>
+ <artifactId>jbossweb</artifactId>
+ <version>7.0.17.Final</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-web</artifactId>
+ <version>7.1.2.Final</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-tomcat-adapter-core</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-servlet-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat-catalina</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>catalina</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java b/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java
new file mode 100755
index 0000000..17ad74b
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java
@@ -0,0 +1,57 @@
+package org.keycloak.adapters.saml.jbossweb;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.keycloak.adapters.jbossweb.JBossWebPrincipalFactory;
+import org.keycloak.adapters.saml.AbstractSamlAuthenticatorValve;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Keycloak authentication valve
+ *
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlAuthenticatorValve extends AbstractSamlAuthenticatorValve {
+ public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws java.io.IOException {
+ return authenticateInternal(request, response, config);
+ }
+
+ @Override
+ protected boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
+ if (loginConfig == null) return false;
+ LoginConfig config = (LoginConfig)loginConfig;
+ if (config.getErrorPage() == null) return false;
+ forwardToErrorPage(request, (Response)response, config);
+ return true;
+ }
+
+ @Override
+ protected void forwardToLogoutPage(Request request, HttpServletResponse response, SamlDeployment deployment) {
+ super.forwardToLogoutPage(request, response, deployment);
+ }
+
+ @Override
+ public void start() throws LifecycleException {
+ StandardContext standardContext = (StandardContext) context;
+ standardContext.addLifecycleListener(this);
+ super.start();
+ }
+
+
+ public void logout(Request request) {
+ logoutInternal(request);
+ }
+
+ @Override
+ protected GenericPrincipalFactory createPrincipalFactory() {
+ return new JBossWebPrincipalFactory();
+ }
+}
saml/client-adapter/as7-eap6/pom.xml 20(+20 -0)
diff --git a/saml/client-adapter/as7-eap6/pom.xml b/saml/client-adapter/as7-eap6/pom.xml
new file mode 100755
index 0000000..c6bca52
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/pom.xml
@@ -0,0 +1,20 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <name>Keycloak SAML EAP Integration</name>
+ <description/>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-eap-integration-pom</artifactId>
+ <packaging>pom</packaging>
+
+ <modules>
+ <module>adapter</module>
+ <module>subsystem</module>
+ </modules>
+</project>
saml/client-adapter/as7-eap6/subsystem/pom.xml 115(+115 -0)
diff --git a/saml/client-adapter/as7-eap6/subsystem/pom.xml b/saml/client-adapter/as7-eap6/subsystem/pom.xml
new file mode 100755
index 0000000..198c360
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/pom.xml
@@ -0,0 +1,115 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+~ Copyright 2013 JBoss Inc
+~
+~ Licensed under the Apache License, Version 2.0 (the "License");
+~ you may not use this file except in compliance with the License.
+~ You may obtain a copy of the License at
+~
+~ http://www.apache.org/licenses/LICENSE-2.0
+~
+~ Unless required by applicable law or agreed to in writing, software
+~ distributed under the License is distributed on an "AS IS" BASIS,
+~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+~ See the License for the specific language governing permissions and
+~ limitations under the License.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <parent>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-parent</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+
+ <artifactId>keycloak-saml-as7-subsystem</artifactId>
+ <name>Keycloak SAML AS7 Subsystem</name>
+ <description/>
+ <packaging>jar</packaging>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.8.1</version>
+ <configuration>
+ <redirectTestOutputToFile>false</redirectTestOutputToFile>
+ <enableAssertions>true</enableAssertions>
+ <argLine>-Xmx512m</argLine>
+ <systemProperties>
+ <property>
+ <name>jboss.home</name>
+ <value>${jboss.home}</value>
+ </property>
+ </systemProperties>
+ <includes>
+ <include>**/*TestCase.java</include>
+ </includes>
+ <forkMode>once</forkMode>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-as7-adapter</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-naming</artifactId>
+ <version>${jboss.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-server</artifactId>
+ <version>${jboss.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-ee</artifactId>
+ <version>${jboss.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-web</artifactId>
+ <version>${jboss.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>3.1.0.GA</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging-processor</artifactId>
+ <!-- This is a compile-time dependency of this project, but is not needed at compile or runtime by other
+projects that depend on this project.-->
+ <scope>provided</scope>
+ <optional>true</optional>
+ <version>1.0.0.Final</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.msc</groupId>
+ <artifactId>jboss-msc</artifactId>
+ <version>1.0.2.GA</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+</project>
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java
new file mode 100755
index 0000000..31008d4
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.web.deployment.WarMetaData;
+import org.jboss.logging.Logger;
+import org.jboss.metadata.javaee.spec.ParamValueMetaData;
+import org.jboss.metadata.web.jboss.JBossWebMetaData;
+import org.jboss.metadata.web.jboss.ValveMetaData;
+import org.jboss.metadata.web.spec.LoginConfigMetaData;
+import org.keycloak.adapters.saml.jbossweb.SamlAuthenticatorValve;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
+ */
+public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
+ protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class);
+
+ // This param name is defined again in Keycloak Undertow Integration class
+ // org.keycloak.adapters.undertow.KeycloakServletExtension. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
+ public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.saml.adapterConfig";
+
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+ String deploymentName = deploymentUnit.getName();
+
+ // if it's not a web-app there's nothing to secure
+ WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+ if (warMetaData == null) {
+ return;
+ }
+ JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+ if (webMetaData == null) {
+ webMetaData = new JBossWebMetaData();
+ warMetaData.setMergedJBossWebMetaData(webMetaData);
+ }
+
+ // otherwise
+ LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+
+ boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod());
+
+ if (webRequiresKC) {
+ log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName);
+ addValve(webMetaData);
+ }
+ }
+
+ private void addValve(JBossWebMetaData webMetaData) {
+ List<ValveMetaData> valves = webMetaData.getValves();
+ if (valves == null) {
+ valves = new ArrayList<ValveMetaData>(1);
+ webMetaData.setValves(valves);
+ }
+ ValveMetaData valve = new ValveMetaData();
+ valve.setValveClass(SamlAuthenticatorValve.class.getName());
+ valve.setModule("org.keycloak.keycloak-saml-as7-adapter");
+ //log.info("******* adding Keycloak valve to: " + deploymentName);
+ valves.add(valve);
+ }
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java
new file mode 100755
index 0000000..c214774
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.server.deployment.Attachments;
+import org.jboss.as.server.deployment.DeploymentPhaseContext;
+import org.jboss.as.server.deployment.DeploymentUnit;
+import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
+import org.jboss.as.server.deployment.DeploymentUnitProcessor;
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
+
+ private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
+ private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-core");
+ private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+ // Next phase, need to detect if this is a Keycloak deployment. If not, don't add the modules.
+
+ final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
+ final ModuleLoader moduleLoader = Module.getBootModuleLoader();
+ addCommonModules(moduleSpecification, moduleLoader);
+ addPlatformSpecificModules(moduleSpecification, moduleLoader);
+ }
+
+ private void addCommonModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+ // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
+ }
+
+ abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java
new file mode 100755
index 0000000..700fc82
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java
@@ -0,0 +1,19 @@
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ * @author <a href="mailto:marko.strukelj@gmail.com">Marko Strukelj</a>
+ */
+public class KeycloakDependencyProcessorAS7 extends KeycloakDependencyProcessor {
+
+ private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-as7-adapter");
+
+ @Override
+ protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+ // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_AS7_ADAPTER, false, false, true, false));
+ }}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java
new file mode 100755
index 0000000..c52f2b5
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.controller.Extension;
+import org.jboss.as.controller.ExtensionContext;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ResourceDefinition;
+import org.jboss.as.controller.SubsystemRegistration;
+import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
+import org.jboss.as.controller.parsing.ExtensionParsingContext;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+
+
+/**
+ * Main Extension class for the subsystem.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSamlExtension implements Extension {
+
+ public static final String SUBSYSTEM_NAME = "keycloak-saml";
+ public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6";
+ private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
+ static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions";
+ private static final int MGMT_API_VERSION_MAJOR = 1;
+ private static final int MGMT_API_VERSION_MINOR = 1;
+
+ static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
+
+ public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
+ StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
+ for (String kp : keyPrefix) {
+ prefix.append('.').append(kp);
+ }
+ return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, KeycloakSamlExtension.class.getClassLoader(), true, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initializeParsers(final ExtensionParsingContext context) {
+ context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE, PARSER);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initialize(final ExtensionContext context) {
+ final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION_MAJOR, MGMT_API_VERSION_MINOR);
+
+ ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+
+ subsystem.registerXMLElementWriter(PARSER);
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java
new file mode 100755
index 0000000..2a7fd55
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.saml.as7;
+
+
+import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.ServiceVerificationHandler;
+import org.jboss.as.server.AbstractDeploymentChainStep;
+import org.jboss.as.server.DeploymentProcessorTarget;
+import org.jboss.as.server.deployment.Phase;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+
+import java.util.List;
+
+/**
+ * The Keycloak subsystem add update handler.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
+
+ static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
+
+ @Override
+ protected void performBoottime(final OperationContext context, ModelNode operation, final ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) {
+ context.addStep(new AbstractDeploymentChainStep() {
+ @Override
+ protected void execute(DeploymentProcessorTarget processorTarget) {
+ processorTarget.addDeploymentProcessor(Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessorAS7());
+ processorTarget.addDeploymentProcessor(
+ Phase.POST_MODULE, // PHASE
+ Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
+ new KeycloakAdapterConfigDeploymentProcessor());
+ }
+ }, OperationContext.Stage.RUNTIME);
+ }
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ model.setEmptyObject();
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java
new file mode 100755
index 0000000..400822e
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+
+/**
+ * Definition of subsystem=keycloak.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
+ protected KeycloakSubsystemDefinition() {
+ super(KeycloakSamlExtension.SUBSYSTEM_PATH,
+ KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
+ KeycloakSubsystemAdd.INSTANCE,
+ ReloadRequiredRemoveStepHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(ModelDescriptionConstants.DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java
new file mode 100755
index 0000000..14899e1
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
+ * as indicated by the @author tags. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
+import org.jboss.dmr.ModelNode;
+import org.jboss.staxmapper.XMLElementReader;
+import org.jboss.staxmapper.XMLElementWriter;
+import org.jboss.staxmapper.XMLExtendedStreamReader;
+import org.jboss.staxmapper.XMLExtendedStreamWriter;
+
+import javax.xml.stream.XMLStreamConstants;
+import javax.xml.stream.XMLStreamException;
+import java.util.List;
+
+/**
+ * The subsystem parser, which uses stax to read and write to and from xml
+ */
+class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
+ // Require no attributes
+ ParseUtils.requireNoAttributes(reader);
+ ModelNode addKeycloakSub = org.jboss.as.controller.operations.common.Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM));
+ list.add(addKeycloakSub);
+
+ while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
+ }
+ }
+
+ // used for debugging
+ private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
+ return reader.nextTag();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
+ context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false);
+ writer.writeEndElement();
+ }
+
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java
new file mode 100755
index 0000000..e73f338
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java
@@ -0,0 +1,42 @@
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class Util {
+ public static ModelNode createAddOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.ADD, address);
+ }
+
+ public static ModelNode createAddOperation() {
+ return createEmptyOperation(ModelDescriptionConstants.ADD, null);
+ }
+
+ public static ModelNode createRemoveOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.REMOVE, address);
+ }
+
+ public static ModelNode createOperation(final String operationName, final PathAddress address) {
+ return createEmptyOperation(operationName, address);
+ }
+
+ public static ModelNode createEmptyOperation(String operationName, final PathAddress address) {
+ ModelNode op = new ModelNode();
+ op.get(OP).set(operationName);
+ if (address != null) {
+ op.get(OP_ADDR).set(address.toModelNode());
+ } else {
+ // Just establish the standard structure; caller can fill in address later
+ op.get(OP_ADDR);
+ }
+ return op;
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
new file mode 100755
index 0000000..2fd653a
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
@@ -0,0 +1 @@
+org.keycloak.subsystem.saml.as7.KeycloakSamlExtension
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index fdb8284..404fc58 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -37,7 +37,7 @@ public class ConfigXmlConstants {
public static final String ATTRIBUTE_ATTR = "attribute";
- public static final String ROLE_MAPPING_ELEMENT = "RoleMapping";
+ public static final String ROLE_IDENTIFIERS_ELEMENT = "RoleIdentifiers";
public static final String ATTRIBUTE_ELEMENT = "Attribute";
public static final String NAME_ATTR = "name";
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index 0421fda..0e92bb7 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -20,6 +20,8 @@ import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
+import java.util.HashSet;
+import java.util.Set;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -52,6 +54,11 @@ public class DeploymentBuilder {
deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName());
}
deployment.setRoleAttributeNames(sp.getRoleAttributes());
+ if (sp.getRoleAttributes() == null) {
+ Set<String> roles = new HashSet<>();
+ roles.add("Role");
+ deployment.setRoleAttributeNames(roles);
+ }
if (sp.getSslPolicy() != null) {
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
deployment.setSslRequired(ssl);
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
index fc08e44..d1aaea9 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -24,16 +24,16 @@ public class IDPXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
IDP idp = new IDP();
- String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ String entityID = SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on IDP");
}
idp.setEntityID(entityID);
- boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
- idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
- idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
+ boolean signaturesRequired = SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
+ idp.setSignatureCanonicalizationMethod(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ idp.setSignatureAlgorithm(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -73,25 +73,25 @@ public class IDPXmlParser extends AbstractParser {
protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
- slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
- slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
- slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
- slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
- slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
- slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
- slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
+ slo.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ slo.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+ slo.setValidateRequestSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
+ slo.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ slo.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ slo.setSignResponse(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
+ slo.setPostBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
+ slo.setRedirectBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
return slo;
}
protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
- sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
- sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
- sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
- sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+ sso.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ sso.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+ sso.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ sso.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ sso.setBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
return sso;
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
index 6fbd8d0..0308a56 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
@@ -7,8 +7,6 @@ import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
@@ -24,8 +22,8 @@ public class KeyXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
Key key = new Key();
- key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
- key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
+ key.setSigning(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
+ key.setEncryption(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -46,13 +44,13 @@ public class KeyXmlParser extends AbstractParser {
key.setKeystore(parseKeyStore(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setCertificatePem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setPublicKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setPrivateKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
@@ -65,14 +63,14 @@ public class KeyXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
- keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
- keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
- keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
- keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
+ keyStore.setType(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
+ keyStore.setAlias(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setFile(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
+ keyStore.setResource(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
if (keyStore.getFile() == null && keyStore.getResource() == null) {
throw new ParsingException("KeyStore element must have the url or classpath attribute set");
}
- keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
+ keyStore.setPassword(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPassword() == null) {
throw new ParsingException("KeyStore element must have the password attribute set");
}
@@ -97,19 +95,19 @@ public class KeyXmlParser extends AbstractParser {
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getCertificateAlias() == null) {
throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
}
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setPrivateKeyAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getPrivateKeyAlias() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
}
- keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
+ keyStore.setPrivateKeyPassword(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPrivateKeyPassword() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
index f010470..ef67c45 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -6,6 +6,7 @@ import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
+import org.keycloak.util.StringPropertyReplacer;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
@@ -13,7 +14,6 @@ import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -23,21 +23,44 @@ import java.util.Set;
*/
public class SPXmlParser extends AbstractParser {
+ public static String getAttributeValue(StartElement startElement, String tag) {
+ String str = StaxParserUtil.getAttributeValue(startElement, tag);
+ if (str != null) return StringPropertyReplacer.replaceProperties(str);
+ else return str;
+ }
+
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
+ String result = getAttributeValue(startElement, tag);
+ if (result == null) return defaultValue;
+ return Boolean.valueOf(result);
+ }
+
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
+ return getBooleanAttributeValue(startElement, tag, false);
+ }
+
+ public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException {
+ String result = StaxParserUtil.getElementText(xmlEventReader);
+ if (result != null) result = StringPropertyReplacer.replaceProperties(result);
+ return result;
+ }
+
+
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT);
SP sp = new SP();
- String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on SP");
}
sp.setEntityID(entityID);
- sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
- sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
- sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
- sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
+ sp.setSslPolicy(getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
+ sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
+ sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
+ sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -60,18 +83,18 @@ public class SPXmlParser extends AbstractParser {
sp.setKeys(keys);
} else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
+ String policy = getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
if (policy == null) {
throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
}
- String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
+ String attribute = getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
mapping.setPolicy(policy);
mapping.setAttributeName(attribute);
sp.setPrincipalNameMapping(mapping);
- } else if (tag.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT)) {
+ } else if (tag.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) {
parseRoleMapping(xmlEventReader, sp);
} else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
IDPXmlParser parser = new IDPXmlParser();
@@ -87,7 +110,7 @@ public class SPXmlParser extends AbstractParser {
protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
- StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT);
Set<String> roleAttributes = new HashSet<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
@@ -96,7 +119,7 @@ public class SPXmlParser extends AbstractParser {
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
- if (endElementName.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT))
+ if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
break;
else
continue;
@@ -107,7 +130,7 @@ public class SPXmlParser extends AbstractParser {
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
if (attributeValue == null) {
throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
index bcba3d0..c0f8dc4 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/InitiateLogin.java
@@ -32,6 +32,11 @@ public class InitiateLogin implements AuthChallenge {
}
@Override
+ public int getResponseCode() {
+ return 0;
+ }
+
+ @Override
public boolean challenge(HttpFacade httpFacade) {
try {
String issuerURL = deployment.getEntityID();
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index bebb506..b404f9b 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -88,6 +88,9 @@ public abstract class SamlAuthenticator {
protected AuthOutcome globalLogout() {
SamlSession account = sessionStore.getAccount();
+ if (account == null) {
+ return AuthOutcome.NOT_ATTEMPTED;
+ }
SAML2LogoutRequestBuilder logoutBuilder = new SAML2LogoutRequestBuilder()
.assertionExpiration(30)
.issuer(deployment.getEntityID())
@@ -292,15 +295,14 @@ public abstract class SamlAuthenticator {
}
}
}
- if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE_NAME) {
+ if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
if (deployment.getPrincipalAttributeName() != null) {
String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
if (attribute != null) principalName = attribute;
- }
- } else if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_FRIENDLY_ATTRIBUTE_NAME) {
- if (deployment.getPrincipalAttributeName() != null) {
- String attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
- if (attribute != null) principalName = attribute;
+ else {
+ attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
+ if (attribute != null) principalName = attribute;
+ }
}
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 681e405..258f14b 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -67,8 +67,7 @@ public interface SamlDeployment {
enum PrincipalNamePolicy {
FROM_NAME_ID,
- FROM_ATTRIBUTE_NAME,
- FROM_FRIENDLY_ATTRIBUTE_NAME
+ FROM_ATTRIBUTE
}
PrincipalNamePolicy getPrincipalNamePolicy();
String getPrincipalAttributeName();
diff --git a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
index b9e0799..534c9ae 100755
--- a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
+++ b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
@@ -25,7 +25,7 @@
<xs:all>
<xs:element name="Keys" type="keys-type" minOccurs="0" maxOccurs="1"/>
<xs:element name="PrincipalNameMapping" type="principal-name-mapping-type" minOccurs="0" maxOccurs="1"/>
- <xs:element name="RoleMapping" type="role-mapping-type" minOccurs="0" maxOccurs="1"/>
+ <xs:element name="RoleIdentifiers" type="role-identifiers-type" minOccurs="0" maxOccurs="1"/>
<xs:element name="IDP" type="idp-type" minOccurs="1" maxOccurs="1"/>
</xs:all>
<xs:attribute name="entityID" type="xs:string" use="required"/>
@@ -70,7 +70,7 @@
<xs:attribute name="policy" type="xs:string" use="required"/>
<xs:attribute name="attribute" type="xs:string" use="optional"/>
</xs:complexType>
- <xs:complexType name="role-mapping-type">
+ <xs:complexType name="role-identifiers-type">
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="Attribute" maxOccurs="unbounded" minOccurs="0" type="attribute-type"/>
</xs:choice>
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
index 5f88197..ef910dc 100755
--- a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -20,9 +20,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="policy" attribute="attribute"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="member"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp"
signatureAlgorithm="RSA"
signatureCanonicalizationMethod="canon"
diff --git a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
index a63b934..ec97fc9 100755
--- a/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
+++ b/saml/client-adapter/jetty/jetty-core/src/main/java/org/keycloak/adapters/saml/jetty/AbstractSamlAuthenticator.java
@@ -259,6 +259,7 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
AuthChallenge challenge = authenticator.getChallenge();
if (challenge != null) {
+ challenge.challenge(facade);
if (challenge.errorPage() && errorPage != null) {
Response response = (Response)res;
try {
@@ -268,7 +269,6 @@ public abstract class AbstractSamlAuthenticator extends LoginAuthenticator {
}
}
- challenge.challenge(facade);
}
return Authentication.SEND_CONTINUE;
}
saml/client-adapter/pom.xml 2(+2 -0)
diff --git a/saml/client-adapter/pom.xml b/saml/client-adapter/pom.xml
index e101055..1fa9028 100755
--- a/saml/client-adapter/pom.xml
+++ b/saml/client-adapter/pom.xml
@@ -19,5 +19,7 @@
<module>tomcat</module>
<module>jetty</module>
<module>wildfly</module>
+ <module>as7-eap6</module>
+ <module>servlet-filter</module>
</modules>
</project>
saml/client-adapter/servlet-filter/pom.xml 69(+69 -0)
diff --git a/saml/client-adapter/servlet-filter/pom.xml b/saml/client-adapter/servlet-filter/pom.xml
new file mode 100755
index 0000000..c2de715
--- /dev/null
+++ b/saml/client-adapter/servlet-filter/pom.xml
@@ -0,0 +1,69 @@
+<?xml version="1.0"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+ <name>Keycloak SAML Servlet Filter</name>
+ <description />
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-common</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>1.6</source>
+ <target>1.6</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
new file mode 100755
index 0000000..2d3a7fd
--- /dev/null
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/FilterSamlSessionStore.java
@@ -0,0 +1,134 @@
+package org.keycloak.adapters.saml.servlet;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakAccount;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.servlet.FilterSessionStore;
+import org.keycloak.util.MultivaluedHashMap;
+
+import javax.servlet.ServletInputStream;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpSession;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.security.Principal;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class FilterSamlSessionStore extends FilterSessionStore implements SamlSessionStore {
+ protected static Logger log = Logger.getLogger(SamlSessionStore.class);
+ protected final SessionIdMapper idMapper;
+
+ public FilterSamlSessionStore(HttpServletRequest request, HttpFacade facade, int maxBuffer, SessionIdMapper idMapper) {
+ super(request, facade, maxBuffer);
+ this.idMapper = idMapper;
+ }
+
+ @Override
+ public void logoutAccount() {
+ HttpSession session = request.getSession(false);
+ if (session == null) return;
+ if (session != null) {
+ if (idMapper != null) idMapper.removeSession(session.getId());
+ SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
+ if (samlSession != null) {
+ session.removeAttribute(SamlSession.class.getName());
+ }
+ clearSavedRequest(session);
+ }
+ }
+
+ @Override
+ public void logoutByPrincipal(String principal) {
+ SamlSession account = getAccount();
+ if (account != null && account.getPrincipal().getSamlSubject().equals(principal)) {
+ logoutAccount();
+ }
+ if (idMapper != null) {
+ Set<String> sessions = idMapper.getUserSessions(principal);
+ if (sessions != null) {
+ List<String> ids = new LinkedList<String>();
+ ids.addAll(sessions);
+ for (String id : ids) {
+ idMapper.removeSession(id);
+ }
+ }
+ }
+
+ }
+
+ @Override
+ public void logoutBySsoId(List<String> ssoIds) {
+ SamlSession account = getAccount();
+ for (String ssoId : ssoIds) {
+ if (account != null && account.getSessionIndex().equals(ssoId)) {
+ logoutAccount();
+ } else if (idMapper != null) {
+ String sessionId = idMapper.getSessionFromSSO(ssoId);
+ idMapper.removeSession(sessionId);
+ }
+ }
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ HttpSession session = request.getSession(false);
+ if (session == null) return false;
+ if (session == null) {
+ log.debug("session was null, returning null");
+ return false;
+ }
+ final SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
+ if (samlSession == null) {
+ log.debug("SamlSession was not in session, returning null");
+ return false;
+ }
+ if (idMapper != null && !idMapper.hasSession(session.getId())) {
+ logoutAccount();
+ return false;
+ }
+
+ needRequestRestore = restoreRequest();
+ return true;
+ }
+
+ public HttpServletRequestWrapper getWrap() {
+ HttpSession session = request.getSession(true);
+ final SamlSession samlSession = (SamlSession)session.getAttribute(SamlSession.class.getName());
+ final KeycloakAccount account = samlSession;
+ return buildWrapper(session, account);
+ }
+
+ @Override
+ public void saveAccount(SamlSession account) {
+ HttpSession session = request.getSession(true);
+ session.setAttribute(SamlSession.class.getName(), account);
+ if (idMapper != null) idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), session.getId());
+ }
+
+ @Override
+ public SamlSession getAccount() {
+ HttpSession session = request.getSession(false);
+ if (session == null) return null;
+ return (SamlSession)session.getAttribute(SamlSession.class.getName());
+ }
+
+ @Override
+ public String getRedirectUri() {
+ HttpSession session = request.getSession(false);
+ if (session == null) return null;
+ return (String)session.getAttribute(REDIRECT_URI);
+ }
+
+}
diff --git a/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
new file mode 100755
index 0000000..8188066
--- /dev/null
+++ b/saml/client-adapter/servlet-filter/src/main/java/org/keycloak/adapters/saml/servlet/SamlFilter.java
@@ -0,0 +1,157 @@
+package org.keycloak.adapters.saml.servlet;
+
+import org.keycloak.adapters.AuthChallenge;
+import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.InMemorySessionIdMapper;
+import org.keycloak.adapters.SessionIdMapper;
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
+import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
+import org.keycloak.adapters.servlet.ServletHttpFacade;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.RequestDispatcher;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import javax.servlet.http.HttpServletResponse;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlFilter implements Filter {
+ protected SamlDeploymentContext deploymentContext;
+ protected SessionIdMapper idMapper = new InMemorySessionIdMapper();
+ private final static Logger log = Logger.getLogger(""+SamlFilter.class);
+
+ @Override
+ public void init(final FilterConfig filterConfig) throws ServletException {
+ String configResolverClass = filterConfig.getInitParameter("keycloak.config.resolver");
+ if (configResolverClass != null) {
+ try {
+ throw new RuntimeException("Not implemented yet");
+ //KeycloakConfigResolver configResolver = (KeycloakConfigResolver) context.getLoader().getClassLoader().loadClass(configResolverClass).newInstance();
+ //deploymentContext = new SamlDeploymentContext(configResolver);
+ //log.log(Level.INFO, "Using {0} to resolve Keycloak configuration on a per-request basis.", configResolverClass);
+ } catch (Exception ex) {
+ log.log(Level.FINE, "The specified resolver {0} could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: {1}", new Object[]{configResolverClass, ex.getMessage()});
+ //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ String fp = filterConfig.getInitParameter("keycloak.config.file");
+ InputStream is = null;
+ if (fp != null) {
+ try {
+ is = new FileInputStream(fp);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ } else {
+ String path = "/WEB-INF/keycloak-saml.xml";
+ String pathParam = filterConfig.getInitParameter("keycloak.config.path");
+ if (pathParam != null) path = pathParam;
+ is = filterConfig.getServletContext().getResourceAsStream(path);
+ }
+ final SamlDeployment deployment;
+ if (is == null) {
+ log.info("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ deployment = new DefaultSamlDeployment();
+ } else {
+ try {
+ ResourceLoader loader = new ResourceLoader() {
+ @Override
+ public InputStream getResourceAsStream(String resource) {
+ return filterConfig.getServletContext().getResourceAsStream(resource);
+ }
+ };
+ deployment = new DeploymentBuilder().build(is, loader);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ deploymentContext = new SamlDeploymentContext(deployment);
+ log.fine("Keycloak is using a per-deployment configuration.");
+ }
+ filterConfig.getServletContext().setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
+
+ }
+
+ @Override
+ public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
+ HttpServletRequest request = (HttpServletRequest) req;
+ HttpServletResponse response = (HttpServletResponse) res;
+ ServletHttpFacade facade = new ServletHttpFacade(request, response);
+ SamlDeployment deployment = deploymentContext.resolveDeployment(facade);
+ if (deployment == null || !deployment.isConfigured()) {
+ response.sendError(403);
+ log.fine("deployment not configured");
+ return;
+ }
+ FilterSamlSessionStore tokenStore = new FilterSamlSessionStore(request, facade, 100000, idMapper);
+
+
+ SamlAuthenticator authenticator = new SamlAuthenticator(facade, deployment, tokenStore) {
+ @Override
+ protected void completeAuthentication(SamlSession account) {
+
+ }
+ };
+ AuthOutcome outcome = authenticator.authenticate();
+ if (outcome == AuthOutcome.AUTHENTICATED) {
+ log.fine("AUTHENTICATED");
+ if (facade.isEnded()) {
+ return;
+ }
+ HttpServletRequestWrapper wrapper = tokenStore.getWrap();
+ chain.doFilter(wrapper, res);
+ return;
+ }
+ if (outcome == AuthOutcome.LOGGED_OUT) {
+ tokenStore.logoutAccount();
+ if (deployment.getLogoutPage() != null) {
+ RequestDispatcher disp = req.getRequestDispatcher(deployment.getLogoutPage());
+ disp.forward(req, res);
+ return;
+ }
+ chain.doFilter(req, res);
+ return;
+ }
+
+ AuthChallenge challenge = authenticator.getChallenge();
+ if (challenge != null) {
+ log.fine("challenge");
+ challenge.challenge(facade);
+ if (challenge.errorPage()) {
+ response.sendError(challenge.getResponseCode());
+ return;
+ }
+ log.fine("sending challenge");
+ return;
+ }
+ if (!facade.isEnded()) {
+ response.sendError(403);
+ }
+
+ }
+
+ @Override
+ public void destroy() {
+
+ }
+}
diff --git a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
index 4dad973..cfc21a0 100755
--- a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
+++ b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
@@ -167,7 +167,7 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
try {
- disp.forward(request, response);
+ disp.forward(request.getRequest(), response);
} catch (ServletException e) {
throw new RuntimeException(e);
} catch (IOException e) {
@@ -212,14 +212,12 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
if (loginConfig == null) {
loginConfig = request.getContext().getLoginConfig();
}
+ challenge.challenge(facade);
if (challenge.errorPage()) {
log.fine("error page");
if (forwardToErrorPageInternal(request, response, loginConfig))return false;
}
- log.fine("sending challenge");
- challenge.challenge(facade);
}
- log.fine("No challenge, but failed authentication");
return false;
}
diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
index d0a50eb..2dad307 100755
--- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
+++ b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
@@ -36,7 +36,6 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-core");
- private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
@Override
@@ -56,7 +55,6 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
- moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
}
abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
services/pom.xml 4(+0 -4)
diff --git a/services/pom.xml b/services/pom.xml
index 1e2e84e..0cbe349 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -32,10 +32,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-http-client</artifactId>
</dependency>
<dependency>
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
index bb17291..e476280 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/ClientIdAndSecretAuthenticator.java
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.Map;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response;
@@ -44,7 +45,11 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
String clientSecret = null;
String authorizationHeader = context.getHttpRequest().getHttpHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
- MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();
+
+ MediaType mediaType = context.getHttpRequest().getHttpHeaders().getMediaType();
+ boolean hasFormData = mediaType != null && mediaType.isCompatible(MediaType.APPLICATION_FORM_URLENCODED_TYPE);
+
+ MultivaluedMap<String, String> formData = hasFormData ? context.getHttpRequest().getDecodedFormParameters() : null;
if (authorizationHeader != null) {
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
@@ -54,7 +59,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
} else {
// Don't send 401 if client_id parameter was sent in request. For example IE may automatically send "Authorization: Negotiate" in XHR requests even for public clients
- if (!formData.containsKey(OAuth2Constants.CLIENT_ID)) {
+ if (formData != null && !formData.containsKey(OAuth2Constants.CLIENT_ID)) {
Response challengeResponse = Response.status(Response.Status.UNAUTHORIZED).header(HttpHeaders.WWW_AUTHENTICATE, "Basic realm=\"" + context.getRealm().getName() + "\"").build();
context.challenge(challengeResponse);
return;
@@ -62,7 +67,7 @@ public class ClientIdAndSecretAuthenticator extends AbstractClientAuthenticator
}
}
- if (client_id == null) {
+ if (formData != null && client_id == null) {
client_id = formData.getFirst(OAuth2Constants.CLIENT_ID);
clientSecret = formData.getFirst("client_secret");
}
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
index fda09ee..23c943e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/AuthorizationEndpoint.java
@@ -46,7 +46,7 @@ public class AuthorizationEndpoint {
public static final String CODE_AUTH_TYPE = "code";
private enum Action {
- REGISTER, CODE
+ REGISTER, CODE, FORGOT_CREDENTIALS
}
@Context
@@ -118,6 +118,8 @@ public class AuthorizationEndpoint {
switch (action) {
case REGISTER:
return buildRegister();
+ case FORGOT_CREDENTIALS:
+ return buildForgotCredential();
case CODE:
return buildAuthorizationCodeAuthorizationResponse();
}
@@ -145,6 +147,17 @@ public class AuthorizationEndpoint {
return this;
}
+ public AuthorizationEndpoint forgotCredentials() {
+ event.event(EventType.RESET_PASSWORD);
+ action = Action.FORGOT_CREDENTIALS;
+
+ if (!realm.isResetPasswordAllowed()) {
+ throw new ErrorPageException(session, Messages.RESET_CREDENTIAL_NOT_ALLOWED);
+ }
+
+ return this;
+ }
+
private void checkSsl() {
if (!uriInfo.getBaseUri().getScheme().equals("https") && realm.getSslRequired().isRequired(clientConnection)) {
event.error(Errors.SSL_REQUIRED);
@@ -266,17 +279,7 @@ public class AuthorizationEndpoint {
AuthenticationFlowModel flow = realm.getBrowserFlow();
String flowId = flow.getId();
- AuthenticationProcessor processor = new AuthenticationProcessor();
- processor.setClientSession(clientSession)
- .setFlowPath(LoginActionsService.AUTHENTICATE_PATH)
- .setFlowId(flowId)
- .setConnection(clientConnection)
- .setEventBuilder(event)
- .setProtector(authManager.getProtector())
- .setRealm(realm)
- .setSession(session)
- .setUriInfo(uriInfo)
- .setRequest(request);
+ AuthenticationProcessor processor = createProcessor(flowId, LoginActionsService.AUTHENTICATE_PATH);
Response challenge = null;
try {
@@ -312,6 +315,32 @@ public class AuthorizationEndpoint {
.createRegistration();
}
+ private Response buildForgotCredential() {
+ authManager.expireIdentityCookie(realm, uriInfo, clientConnection);
+
+ AuthenticationFlowModel flow = realm.getResetCredentialsFlow();
+ String flowId = flow.getId();
+
+ AuthenticationProcessor processor = createProcessor(flowId, LoginActionsService.RESET_CREDENTIALS_PATH);
+
+ return processor.authenticate();
+ }
+
+ private AuthenticationProcessor createProcessor(String flowId, String flowPath) {
+ AuthenticationProcessor processor = new AuthenticationProcessor();
+ processor.setClientSession(clientSession)
+ .setFlowPath(flowPath)
+ .setFlowId(flowId)
+ .setConnection(clientConnection)
+ .setEventBuilder(event)
+ .setProtector(authManager.getProtector())
+ .setRealm(realm)
+ .setSession(session)
+ .setUriInfo(uriInfo)
+ .setRequest(request);
+ return processor;
+ }
+
private Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
logger.debug("Automatically redirect to identity provider: " + providerId);
return Response.temporaryRedirect(
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
index ed83068..227bd0c 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/OIDCAttributeMapperHelper.java
@@ -2,12 +2,9 @@ package org.keycloak.protocol.oidc.mappers;
import org.jboss.logging.Logger;
import org.keycloak.models.ProtocolMapperModel;
-import org.keycloak.models.RealmModel;
-import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.protocol.ProtocolMapperUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
-import org.keycloak.representations.AccessToken;
import org.keycloak.representations.IDToken;
import java.util.ArrayList;
@@ -23,14 +20,16 @@ public class OIDCAttributeMapperHelper {
private static final Logger logger = Logger.getLogger(OIDCAttributeMapperHelper.class);
public static final String TOKEN_CLAIM_NAME = "claim.name";
- public static final String TOKEN_CLAIM_NAME_LABEL = "Token Claim Name";
- public static final String JSON_TYPE = "Claim JSON Type";
+ public static final String TOKEN_CLAIM_NAME_LABEL = "tokenClaimName.label";
+ public static final String TOKEN_CLAIM_NAME_TOOLTIP = "tokenClaimName.tooltip";
+ public static final String JSON_TYPE = "jsonType.label";
+ public static final String JSON_TYPE_TOOLTIP = "jsonType.tooltip";
public static final String INCLUDE_IN_ACCESS_TOKEN = "access.token.claim";
- public static final String INCLUDE_IN_ACCESS_TOKEN_LABEL = "Add to access token";
- public static final String INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT = "Should the claim be added to the access token?";
+ public static final String INCLUDE_IN_ACCESS_TOKEN_LABEL = "includeInAccessToken.label";
+ public static final String INCLUDE_IN_ACCESS_TOKEN_HELP_TEXT = "includeInAccessToken.tooltip";
public static final String INCLUDE_IN_ID_TOKEN = "id.token.claim";
- public static final String INCLUDE_IN_ID_TOKEN_LABEL = "Add to ID token";
- public static final String INCLUDE_IN_ID_TOKEN_HELP_TEXT = "Should the claim be added to the ID token?";
+ public static final String INCLUDE_IN_ID_TOKEN_LABEL = "includeInIdToken.label";
+ public static final String INCLUDE_IN_ID_TOKEN_HELP_TEXT = "includeInIdToken.tooltip";
public static Object mapAttributeValue(ProtocolMapperModel mappingModel, Object attributeValue) {
if (attributeValue == null) return null;
@@ -139,7 +138,7 @@ public class OIDCAttributeMapperHelper {
property.setName(TOKEN_CLAIM_NAME);
property.setLabel(TOKEN_CLAIM_NAME_LABEL);
property.setType(ProviderConfigProperty.STRING_TYPE);
- property.setHelpText("Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created.");
+ property.setHelpText(TOKEN_CLAIM_NAME_TOOLTIP);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(JSON_TYPE);
@@ -151,7 +150,7 @@ public class OIDCAttributeMapperHelper {
types.add("boolean");
property.setType(ProviderConfigProperty.LIST_TYPE);
property.setDefaultValue(types);
- property.setHelpText("JSON type that should be used to populate the json claim in the token. long, int, boolean, and String are valid values.");
+ property.setHelpText(JSON_TYPE_TOOLTIP);
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(INCLUDE_IN_ID_TOKEN);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
index 18c78c4..3d7e93c 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
@@ -121,6 +121,16 @@ public class OIDCLoginProtocolService {
}
/**
+ * Forgot-Credentials endpoint
+ */
+ @Path("forgot-credentials")
+ public Object forgotCredentialsPage() {
+ AuthorizationEndpoint endpoint = new AuthorizationEndpoint(authManager, realm, event);
+ ResteasyProviderFactory.getInstance().injectProperties(endpoint);
+ return endpoint.forgotCredentials();
+ }
+
+ /**
* Token endpoint
*/
@Path("token")
diff --git a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
index c1a9938..2b86773 100755
--- a/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
+++ b/services/src/main/java/org/keycloak/protocol/ProtocolMapperUtils.java
@@ -17,14 +17,14 @@ public class ProtocolMapperUtils {
public static final String USER_ATTRIBUTE = "user.attribute";
public static final String USER_SESSION_NOTE = "user.session.note";
public static final String MULTIVALUED = "multivalued";
- public static final String USER_MODEL_PROPERTY_LABEL = "User Property";
- public static final String USER_MODEL_PROPERTY_HELP_TEXT = "Name of the property method in the UserModel interface. For example, a value of 'email' would reference the UserModel.getEmail() method.";
- public static final String USER_MODEL_ATTRIBUTE_LABEL = "User Attribute";
- public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "Name of stored user attribute which is the name of an attribute within the UserModel.attribute map.";
- public static final String USER_SESSION_MODEL_NOTE_LABEL = "User Session Note";
- public static final String USER_SESSION_MODEL_NOTE_HELP_TEXT = "Name of stored user session note within the UserSessionModel.note map.";
- public static final String MULTIVALUED_LABEL = "Multivalued";
- public static final String MULTIVALUED_HELP_TEXT = "Indicates if attribute supports multiple values. If true, then the list of all values of this attribute will be set as claim. If false, then just first value will be set as claim";
+ public static final String USER_MODEL_PROPERTY_LABEL = "usermodel.prop.label";
+ public static final String USER_MODEL_PROPERTY_HELP_TEXT = "usermodel.prop.tooltip";
+ public static final String USER_MODEL_ATTRIBUTE_LABEL = "usermodel.attr.label";
+ public static final String USER_MODEL_ATTRIBUTE_HELP_TEXT = "usermodel.attr.tooltip";
+ public static final String USER_SESSION_MODEL_NOTE_LABEL = "userSession.modelNote.label";
+ public static final String USER_SESSION_MODEL_NOTE_HELP_TEXT = "userSession.modelNote.tooltip";
+ public static final String MULTIVALUED_LABEL = "multivalued.label";
+ public static final String MULTIVALUED_HELP_TEXT = "multivalued.tooltip";
public static String getUserModelValue(UserModel user, String propertyName) {
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
new file mode 100644
index 0000000..d1d6648
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProvider.java
@@ -0,0 +1,16 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.RealmModel;
+import org.keycloak.provider.Provider;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ClientRegistrationProvider extends Provider {
+
+ void setRealm(RealmModel realm);
+
+ void setEvent(EventBuilder event);
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProviderFactory.java
new file mode 100644
index 0000000..d9be240
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationProviderFactory.java
@@ -0,0 +1,9 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.provider.ProviderFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public interface ClientRegistrationProviderFactory extends ProviderFactory<ClientRegistrationProvider> {
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
new file mode 100644
index 0000000..8b215fa
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationService.java
@@ -0,0 +1,37 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.AppAuthManager;
+
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.core.Context;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegistrationService {
+
+ private RealmModel realm;
+
+ private EventBuilder event;
+
+ @Context
+ private KeycloakSession session;
+
+ public ClientRegistrationService(RealmModel realm, EventBuilder event) {
+ this.realm = realm;
+ this.event = event;
+ }
+
+ @Path("{provider}")
+ public Object getProvider(@PathParam("provider") String providerId) {
+ ClientRegistrationProvider provider = session.getProvider(ClientRegistrationProvider.class, providerId);
+ provider.setRealm(realm);
+ provider.setEvent(event);
+ return provider;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java
new file mode 100644
index 0000000..3672f7f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/ClientRegistrationSpi.java
@@ -0,0 +1,30 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.provider.Provider;
+import org.keycloak.provider.ProviderFactory;
+import org.keycloak.provider.Spi;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegistrationSpi implements Spi {
+ @Override
+ public boolean isInternal() {
+ return false;
+ }
+
+ @Override
+ public String getName() {
+ return "client-registration";
+ }
+
+ @Override
+ public Class<? extends Provider> getProviderClass() {
+ return ClientRegistrationProvider.class;
+ }
+
+ @Override
+ public Class<? extends ProviderFactory> getProviderFactoryClass() {
+ return ClientRegistrationProviderFactory.class;
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
new file mode 100644
index 0000000..04cb46a
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProvider.java
@@ -0,0 +1,155 @@
+package org.keycloak.services.clientregistration;
+
+import org.jboss.logging.Logger;
+import org.keycloak.events.Errors;
+import org.keycloak.events.EventBuilder;
+import org.keycloak.events.EventType;
+import org.keycloak.models.*;
+import org.keycloak.models.utils.ModelToRepresentation;
+import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.services.ErrorResponse;
+import org.keycloak.services.ForbiddenException;
+import org.keycloak.services.managers.AppAuthManager;
+import org.keycloak.services.managers.AuthenticationManager;
+
+import javax.ws.rs.*;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultClientRegistrationProvider implements ClientRegistrationProvider {
+
+ private static final Logger logger = Logger.getLogger(DefaultClientRegistrationProvider.class);
+
+ private KeycloakSession session;
+ private EventBuilder event;
+ private RealmModel realm;
+
+ public DefaultClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+
+ @POST
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response create(ClientRepresentation client) {
+ event.event(EventType.CLIENT_REGISTER);
+
+ authenticate(true, null);
+
+ try {
+ ClientModel clientModel = RepresentationToModel.createClient(session, realm, client, true);
+ client = ModelToRepresentation.toRepresentation(clientModel);
+ URI uri = session.getContext().getUri().getAbsolutePathBuilder().path(clientModel.getId()).build();
+
+ logger.infov("Created client {0}", client.getClientId());
+
+ event.client(client.getClientId()).success();
+ return Response.created(uri).entity(client).build();
+ } catch (ModelDuplicateException e) {
+ return ErrorResponse.exists("Client " + client.getClientId() + " already exists");
+ }
+ }
+
+ @GET
+ @Path("{clientId}")
+ @Produces(MediaType.APPLICATION_JSON)
+ public Response get(@PathParam("clientId") String clientId) {
+ event.event(EventType.CLIENT_INFO);
+
+ ClientModel client = authenticate(false, clientId);
+ if (client == null) {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ return Response.ok(ModelToRepresentation.toRepresentation(client)).build();
+ }
+
+ @PUT
+ @Path("{clientId}")
+ @Consumes(MediaType.APPLICATION_JSON)
+ public Response update(@PathParam("clientId") String clientId, ClientRepresentation rep) {
+ event.event(EventType.CLIENT_UPDATE).client(clientId);
+
+ ClientModel client = authenticate(false, clientId);
+ RepresentationToModel.updateClient(rep, client);
+
+ logger.infov("Updated client {0}", rep.getClientId());
+
+ event.success();
+ return Response.status(Response.Status.OK).build();
+ }
+
+ @DELETE
+ @Path("{clientId}")
+ public Response delete(@PathParam("clientId") String clientId) {
+ event.event(EventType.CLIENT_DELETE).client(clientId);
+
+ ClientModel client = authenticate(false, clientId);
+ if (realm.removeClient(client.getId())) {
+ event.success();
+ return Response.ok().build();
+ } else {
+ return Response.status(Response.Status.NOT_FOUND).build();
+ }
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+
+ private ClientModel authenticate(boolean create, String clientId) {
+ String authorizationHeader = session.getContext().getRequestHeaders().getRequestHeaders().getFirst(HttpHeaders.AUTHORIZATION);
+
+ boolean bearer = authorizationHeader != null && authorizationHeader.split(" ")[0].equalsIgnoreCase("Bearer");
+
+ if (bearer) {
+ AuthenticationManager.AuthResult authResult = new AppAuthManager().authenticateBearerToken(session, realm);
+ AccessToken.Access realmAccess = authResult.getToken().getResourceAccess(Constants.REALM_MANAGEMENT_CLIENT_ID);
+ if (realmAccess != null) {
+ if (realmAccess.isUserInRole(AdminRoles.MANAGE_CLIENTS)) {
+ return create ? null : realm.getClientByClientId(clientId);
+ }
+
+ if (create && realmAccess.isUserInRole(AdminRoles.CREATE_CLIENT)) {
+ return create ? null : realm.getClientByClientId(clientId);
+ }
+ }
+ } else if (!create) {
+ ClientModel client;
+
+ try {
+ AuthorizeClientUtil.ClientAuthResult clientAuth = AuthorizeClientUtil.authorizeClient(session, event, realm);
+ client = clientAuth.getClient();
+
+ if (client != null && !client.isPublicClient() && client.getClientId().equals(clientId)) {
+ return client;
+ }
+ } catch (Throwable t) {
+ }
+ }
+
+ event.error(Errors.NOT_ALLOWED);
+
+ throw new ForbiddenException();
+ }
+
+ @Override
+ public void setRealm(RealmModel realm) {
+this.realm = realm;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProviderFactory.java
new file mode 100644
index 0000000..ec99652
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/DefaultClientRegistrationProviderFactory.java
@@ -0,0 +1,34 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class DefaultClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
+
+ @Override
+ public ClientRegistrationProvider create(KeycloakSession session) {
+ return new DefaultClientRegistrationProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "default";
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java
new file mode 100644
index 0000000..baf9df1
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProvider.java
@@ -0,0 +1,34 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.events.EventBuilder;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRegistrationProvider implements ClientRegistrationProvider {
+
+ private KeycloakSession session;
+ private RealmModel realm;
+ private EventBuilder event;
+
+ public OIDCClientRegistrationProvider(KeycloakSession session) {
+ this.session = session;
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public void setRealm(RealmModel realm) {
+ this.realm = realm;
+ }
+
+ @Override
+ public void setEvent(EventBuilder event) {
+ this.event = event;
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java b/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java
new file mode 100644
index 0000000..a3ba000
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/clientregistration/OIDCClientRegistrationProviderFactory.java
@@ -0,0 +1,34 @@
+package org.keycloak.services.clientregistration;
+
+import org.keycloak.Config;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.KeycloakSessionFactory;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OIDCClientRegistrationProviderFactory implements ClientRegistrationProviderFactory {
+
+ @Override
+ public ClientRegistrationProvider create(KeycloakSession session) {
+ return new OIDCClientRegistrationProvider(session);
+ }
+
+ @Override
+ public void init(Config.Scope config) {
+ }
+
+ @Override
+ public void postInit(KeycloakSessionFactory factory) {
+ }
+
+ @Override
+ public void close() {
+ }
+
+ @Override
+ public String getId() {
+ return "openid-connect";
+ }
+
+}
diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
index ae55eee..431421e 100755
--- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
+++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java
@@ -27,6 +27,8 @@ public class KeycloakSessionServletFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+ servletRequest.setCharacterEncoding("UTF-8");
+
final HttpServletRequest request = (HttpServletRequest)servletRequest;
KeycloakSessionFactory sessionFactory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName());
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index 0156fb6..3865027 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -3,6 +3,7 @@ package org.keycloak.services.managers;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.UnauthorizedException;
import org.keycloak.ClientConnection;
+import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
@@ -39,6 +40,11 @@ public class AppAuthManager extends AuthenticationManager {
return tokenString;
}
+ public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm) {
+ KeycloakContext ctx = session.getContext();
+ return authenticateBearerToken(session, realm, ctx.getUri(), ctx.getConnection(), ctx.getRequestHeaders());
+ }
+
public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
String tokenString = extractAuthorizationHeaderToken(headers);
if (tokenString == null) return null;
diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
index 28818d8..6ea2ecb 100755
--- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
+++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java
@@ -6,12 +6,12 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
-import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.exportimport.ExportImportManager;
import org.keycloak.migration.MigrationModelManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
+import org.keycloak.offlineconfig.AdminRecovery;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.DefaultKeycloakSessionFactory;
import org.keycloak.services.managers.ApplianceBootstrap;
@@ -23,6 +23,7 @@ import org.keycloak.services.scheduled.ClearExpiredEvents;
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
import org.keycloak.services.scheduled.ScheduledTaskRunner;
import org.keycloak.services.util.JsonConfigProvider;
+import org.keycloak.services.util.ObjectMapperResolver;
import org.keycloak.timer.TimerProvider;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.SystemEnvProperties;
@@ -31,18 +32,13 @@ import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.IOException;
-import java.io.InputStream;
+import java.io.*;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import java.util.StringTokenizer;
-import org.keycloak.offlineconfig.AdminRecovery;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -77,12 +73,12 @@ public class KeycloakApplication extends Application {
singletons.add(new RealmsResource());
singletons.add(new AdminRoot());
singletons.add(new ModelExceptionMapper());
- classes.add(SkeletonKeyContextResolver.class);
classes.add(QRCodeResource.class);
classes.add(ThemeResource.class);
classes.add(JsResource.class);
classes.add(WelcomeResource.class);
+ singletons.add(new ObjectMapperResolver(Boolean.parseBoolean(System.getProperty("keycloak.jsonPrettyPrint", "false"))));
setupDefaultRealm(context.getContextPath());
diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
index 52f49df..73aaca6 100755
--- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java
@@ -13,6 +13,7 @@ import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.LoginProtocolFactory;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.services.clientregistration.ClientRegistrationService;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.RealmManager;
@@ -112,14 +113,14 @@ public class RealmsResource {
return service;
}
-// @Path("{realm}/client-registration")
-// public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
-// RealmModel realm = init(name);
-// EventBuilder event = new EventBuilder(realm, session, clientConnection);
-// ClientRegistrationService service = new ClientRegistrationService(realm, event);
-// ResteasyProviderFactory.getInstance().injectProperties(service);
-// return service;
-// }
+ @Path("{realm}/client-registration")
+ public ClientRegistrationService getClientsService(final @PathParam("realm") String name) {
+ RealmModel realm = init(name);
+ EventBuilder event = new EventBuilder(realm, session, clientConnection);
+ ClientRegistrationService service = new ClientRegistrationService(realm, event);
+ ResteasyProviderFactory.getInstance().injectProperties(service);
+ return service;
+ }
@Path("{realm}/clients-managements")
public ClientsManagementService getClientsManagementService(final @PathParam("realm") String name) {
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
index 6d88f97..5119548 100755
--- a/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
+++ b/services/src/main/resources/META-INF/services/org.keycloak.provider.Spi
@@ -8,3 +8,4 @@ org.keycloak.authentication.ClientAuthenticatorSpi
org.keycloak.authentication.RequiredActionSpi
org.keycloak.authentication.FormAuthenticatorSpi
org.keycloak.authentication.FormActionSpi
+org.keycloak.services.clientregistration.ClientRegistrationSpi
\ No newline at end of file
diff --git a/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
new file mode 100644
index 0000000..3e8773a
--- /dev/null
+++ b/services/src/main/resources/META-INF/services/org.keycloak.services.clientregistration.ClientRegistrationProviderFactory
@@ -0,0 +1 @@
+org.keycloak.services.clientregistration.DefaultClientRegistrationProviderFactory
\ No newline at end of file
testsuite/integration/pom.xml 8(+8 -0)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 979d65e..dad4114 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -107,6 +107,14 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-servlet-filter-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-servlet-filter-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-undertow-adapter</artifactId>
</dependency>
<dependency>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index a63132f..d5f9072 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -604,6 +604,7 @@ public class AdapterTestStrategy extends ExternalResource {
// logout sessions in account management
accountSessionsPage.realm("demo");
accountSessionsPage.open();
+ Assert.assertTrue(accountSessionsPage.isCurrent());
accountSessionsPage.logoutAll();
// Assert I need to login again (logout was propagated to the app)
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java
new file mode 100755
index 0000000..39c0368
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/FilterAdapterTest.java
@@ -0,0 +1,214 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.adapter;
+
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+import java.net.URL;
+import java.security.PublicKey;
+
+/**
+ * Tests Undertow Adapter
+ *
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+public class FilterAdapterTest {
+
+ public static PublicKey realmPublicKey;
+ @ClassRule
+ public static AbstractKeycloakRule keycloakRule = new AbstractKeycloakRule() {
+ @Override
+ protected void configure(KeycloakSession session, RealmManager manager, RealmModel adminRealm) {
+ RealmModel realm = AdapterTestStrategy.baseAdapterTestInitialization(session, manager, adminRealm, getClass());
+ realmPublicKey = realm.getPublicKey();
+
+ URL url = getClass().getResource("/adapter-test/cust-app-keycloak.json");
+ createApplicationDeployment()
+ .name("customer-portal").contextPath("/customer-portal")
+ .servletClass(CustomerServlet.class).adapterConfigPath(url.getPath())
+ .role("user").deployApplicationWithFilter();
+
+ url = getClass().getResource("/adapter-test/secure-portal-keycloak.json");
+ createApplicationDeployment()
+ .name("secure-portal").contextPath("/secure-portal")
+ .servletClass(CallAuthenticatedServlet.class).adapterConfigPath(url.getPath())
+ .role("user")
+ .isConstrained(false).deployApplicationWithFilter();
+
+ url = getClass().getResource("/adapter-test/customer-db-keycloak.json");
+ createApplicationDeployment()
+ .name("customer-db").contextPath("/customer-db")
+ .servletClass(CustomerDatabaseServlet.class).adapterConfigPath(url.getPath())
+ .role("user")
+ .errorPage(null).deployApplicationWithFilter();
+
+ createApplicationDeployment()
+ .name("customer-db-error-page").contextPath("/customer-db-error-page")
+ .servletClass(CustomerDatabaseServlet.class).adapterConfigPath(url.getPath())
+ .role("user").deployApplicationWithFilter();
+
+ url = getClass().getResource("/adapter-test/product-keycloak.json");
+ createApplicationDeployment()
+ .name("product-portal").contextPath("/product-portal")
+ .servletClass(ProductServlet.class).adapterConfigPath(url.getPath())
+ .role("user").deployApplicationWithFilter();
+
+ // Test that replacing system properties works for adapters
+ System.setProperty("app.server.base.url", "http://localhost:8081");
+ System.setProperty("my.host.name", "localhost");
+ url = getClass().getResource("/adapter-test/session-keycloak.json");
+ createApplicationDeployment()
+ .name("session-portal").contextPath("/session-portal")
+ .servletClass(SessionServlet.class).adapterConfigPath(url.getPath())
+ .role("user").deployApplicationWithFilter();
+
+ url = getClass().getResource("/adapter-test/input-keycloak.json");
+ createApplicationDeployment()
+ .name("input-portal").contextPath("/input-portal")
+ .servletClass(InputServlet.class).adapterConfigPath(url.getPath())
+ .role("user").constraintUrl("/secured/*").deployApplicationWithFilter();
+ }
+ };
+
+ @Rule
+ public AdapterTestStrategy testStrategy = new AdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule);
+
+ @Test
+ public void testLoginSSOAndLogout() throws Exception {
+ testStrategy.testLoginSSOAndLogout();
+ }
+
+ @Test
+ public void testSavedPostRequest() throws Exception {
+ testStrategy.testSavedPostRequest();
+ }
+
+ @Test
+ public void testServletRequestLogout() throws Exception {
+ testStrategy.testServletRequestLogout();
+ }
+
+ @Test
+ public void testLoginSSOIdle() throws Exception {
+ testStrategy.testLoginSSOIdle();
+
+ }
+
+ @Test
+ public void testLoginSSOIdleRemoveExpiredUserSessions() throws Exception {
+ testStrategy.testLoginSSOIdleRemoveExpiredUserSessions();
+ }
+
+ @Test
+ public void testLoginSSOMax() throws Exception {
+ testStrategy.testLoginSSOMax();
+ }
+
+ /**
+ * KEYCLOAK-518
+ * @throws Exception
+ */
+ @Test
+ public void testNullBearerToken() throws Exception {
+ testStrategy.testNullBearerToken();
+ }
+
+ /**
+ * KEYCLOAK-1368
+ * @throws Exception
+ */
+ /*
+ can't test because of the way filter works
+ @Test
+ public void testNullBearerTokenCustomErrorPage() throws Exception {
+ testStrategy.testNullBearerTokenCustomErrorPage();
+ }
+ */
+
+ /**
+ * KEYCLOAK-518
+ * @throws Exception
+ */
+ @Test
+ public void testBadUser() throws Exception {
+ testStrategy.testBadUser();
+ }
+
+ @Test
+ public void testVersion() throws Exception {
+ testStrategy.testVersion();
+ }
+
+ /*
+ Don't need to test this because HttpServletRequest.authenticate doesn't make sense with filter implementation
+
+ @Test
+ public void testAuthenticated() throws Exception {
+ testStrategy.testAuthenticated();
+ }
+ */
+
+ /**
+ * KEYCLOAK-732
+ *
+ * @throws Throwable
+ */
+ @Test
+ public void testSingleSessionInvalidated() throws Throwable {
+ testStrategy.testSingleSessionInvalidated();
+ }
+
+ /**
+ * KEYCLOAK-741
+ */
+ @Test
+ public void testSessionInvalidatedAfterFailedRefresh() throws Throwable {
+ testStrategy.testSessionInvalidatedAfterFailedRefresh();
+
+ }
+
+ /**
+ * KEYCLOAK-942
+ */
+ @Test
+ public void testAdminApplicationLogout() throws Throwable {
+ testStrategy.testAdminApplicationLogout();
+ }
+
+ /**
+ * KEYCLOAK-1216
+ */
+ /*
+ Can't test this because backchannel logout for filter does not invalidate the session
+ @Test
+ public void testAccountManagementSessionsLogout() throws Throwable {
+ testStrategy.testAccountManagementSessionsLogout();
+ }
+ */
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SendUsernameServlet.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SendUsernameServlet.java
index faa5014..f3a14f4 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SendUsernameServlet.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/keycloaksaml/SendUsernameServlet.java
@@ -26,7 +26,11 @@ public class SendUsernameServlet extends HttpServlet {
if (checkRoles != null) {
for (String role : checkRoles) {
System.out.println("check role: " + role);
- Assert.assertTrue(req.isUserInRole(role));
+ //Assert.assertTrue(req.isUserInRole(role));
+ if (!req.isUserInRole(role)) {
+ resp.sendError(403);
+ return;
+ }
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
index 9f7530d..9409805 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/KeycloakServer.java
@@ -289,37 +289,43 @@ public class KeycloakServer {
.setWorkerThreads(config.getWorkerThreads())
.setIoThreads(config.getWorkerThreads() / 8);
- server = new UndertowJaxrsServer().start(builder);
+ server = new UndertowJaxrsServer();
+ try {
+ server.start(builder);
- DeploymentInfo di = server.undertowDeployment(deployment, "");
- di.setClassLoader(getClass().getClassLoader());
- di.setContextPath("/auth");
- di.setDeploymentName("Keycloak");
- di.setDefaultEncoding("UTF-8");
+ DeploymentInfo di = server.undertowDeployment(deployment, "");
+ di.setClassLoader(getClass().getClassLoader());
+ di.setContextPath("/auth");
+ di.setDeploymentName("Keycloak");
+ di.setDefaultEncoding("UTF-8");
- di.setDefaultServletConfig(new DefaultServletConfig(true));
- di.addWelcomePage("theme/keycloak/welcome/resources/index.html");
+ di.setDefaultServletConfig(new DefaultServletConfig(true));
+ di.addWelcomePage("theme/keycloak/welcome/resources/index.html");
- FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
- di.addFilter(filter);
- di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
+ FilterInfo filter = Servlets.filter("SessionFilter", KeycloakSessionServletFilter.class);
+ di.addFilter(filter);
+ di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
- FilterInfo connectionFilter = Servlets.filter("ClientConnectionFilter", ClientConnectionFilter.class);
- di.addFilter(connectionFilter);
- di.addFilterUrlMapping("ClientConnectionFilter", "/*", DispatcherType.REQUEST);
+ FilterInfo connectionFilter = Servlets.filter("ClientConnectionFilter", ClientConnectionFilter.class);
+ di.addFilter(connectionFilter);
+ di.addFilterUrlMapping("ClientConnectionFilter", "/*", DispatcherType.REQUEST);
- server.deploy(di);
+ server.deploy(di);
- sessionFactory = ((KeycloakApplication) deployment.getApplication()).getSessionFactory();
+ sessionFactory = ((KeycloakApplication) deployment.getApplication()).getSessionFactory();
- setupDevConfig();
+ setupDevConfig();
- if (config.getResourcesHome() != null) {
- info("Loading resources from " + config.getResourcesHome());
- }
+ if (config.getResourcesHome() != null) {
+ info("Loading resources from " + config.getResourcesHome());
+ }
- info("Started Keycloak (http://" + config.getHost() + ":" + config.getPort() + "/auth) in "
- + (System.currentTimeMillis() - start) + " ms\n");
+ info("Started Keycloak (http://" + config.getHost() + ":" + config.getPort() + "/auth) in "
+ + (System.currentTimeMillis() - start) + " ms\n");
+ } catch (RuntimeException e) {
+ server.stop();
+ throw e;
+ }
}
private void info(String message) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index d18b615..8c673a0 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -1,6 +1,7 @@
package org.keycloak.testsuite.rule;
import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.SecurityInfo;
@@ -11,6 +12,8 @@ import org.junit.rules.ExternalResource;
import org.junit.rules.TemporaryFolder;
import org.keycloak.Config;
import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.saml.servlet.SamlFilter;
+import org.keycloak.adapters.servlet.KeycloakOIDCFilter;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.RealmModel;
@@ -24,6 +27,7 @@ import org.keycloak.testsuite.KeycloakServer;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.Time;
+import javax.servlet.DispatcherType;
import javax.servlet.Servlet;
import javax.ws.rs.core.Application;
import java.io.ByteArrayOutputStream;
@@ -350,6 +354,22 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
server.getServer().deploy(di);
}
+ public void deployApplicationWithFilter() {
+ DeploymentInfo di = createDeploymentInfo(name, contextPath, servletClass);
+ FilterInfo filter = new FilterInfo("keycloak-filter", KeycloakOIDCFilter.class);
+ if (null == keycloakConfigResolver) {
+ filter.addInitParam("keycloak.config.file", adapterConfigPath);
+ } else {
+ filter.addInitParam("keycloak.config.resolver", keycloakConfigResolver.getCanonicalName());
+ }
+ di.addFilter(filter);
+ di.addFilterUrlMapping("keycloak-filter", constraintUrl, DispatcherType.REQUEST);
+ server.getServer().deploy(di);
+
+
+
+ }
+
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java
new file mode 100755
index 0000000..88f97a6
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlAdapterTest.java
@@ -0,0 +1,147 @@
+package org.keycloak.testsuite.samlfilter;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.testsuite.keycloaksaml.SamlAdapterTestStrategy;
+import org.keycloak.testsuite.keycloaksaml.SamlSPFacade;
+import org.keycloak.testsuite.keycloaksaml.SendUsernameServlet;
+import org.openqa.selenium.WebDriver;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlAdapterTest {
+
+ @ClassRule
+ public static SamlKeycloakRule keycloakRule = new SamlKeycloakRule() {
+ @Override
+ public void initWars() {
+ ClassLoader classLoader = SamlAdapterTest.class.getClassLoader();
+
+ initializeSamlSecuredWar("/keycloak-saml/simple-post", "/sales-post", "post.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-post", "/sales-post-sig", "post-sig.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-post-email", "/sales-post-sig-email", "post-sig-email.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-post-transient", "/sales-post-sig-transient", "post-sig-transient.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-post-persistent", "/sales-post-sig-persistent", "post-sig-persistent.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-metadata", "/sales-metadata", "post-metadata.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-get", "/employee-sig", "employee-sig.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/mappers", "/employee2", "employee2.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/signed-front-get", "/employee-sig-front", "employee-sig-front.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/bad-client-signed-post", "/bad-client-sales-post-sig", "bad-client-post-sig.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/bad-realm-signed-post", "/bad-realm-sales-post-sig", "bad-realm-post-sig.war", classLoader);
+ initializeSamlSecuredWar("/keycloak-saml/encrypted-post", "/sales-post-enc", "post-enc.war", classLoader);
+ SamlAdapterTestStrategy.uploadSP("http://localhost:8081/auth");
+
+
+
+ }
+
+ @Override
+ public String getRealmJson() {
+ return "/keycloak-saml/testsaml.json";
+ }
+ };
+
+ @Rule
+ public SamlAdapterTestStrategy testStrategy = new SamlAdapterTestStrategy("http://localhost:8081/auth", "http://localhost:8081", keycloakRule);
+
+ @Test
+ public void testPostBadRealmSignature() {
+ testStrategy.testPostBadRealmSignature( new SamlAdapterTestStrategy.CheckAuthError() {
+ @Override
+ public void check(WebDriver driver) {
+ Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
+ }
+ });
+ }
+
+ @Test
+ public void testPostSimpleUnauthorized() {
+ List<String> requiredRoles = new LinkedList<>();
+ requiredRoles.add("manager");
+ requiredRoles.add("employee");
+ requiredRoles.add("user");
+ SendUsernameServlet.checkRoles = requiredRoles;
+ try {
+ testStrategy.testPostSimpleUnauthorized(new SamlAdapterTestStrategy.CheckAuthError() {
+ @Override
+ public void check(WebDriver driver) {
+ Assert.assertTrue(driver.getPageSource().contains("Forbidden"));
+ }
+ });
+ } finally {
+ SendUsernameServlet.checkRoles = null;
+ }
+ }
+
+ @Test
+ public void testMetadataPostSignedLoginLogout() throws Exception {
+ testStrategy.testMetadataPostSignedLoginLogout();
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogout() {
+ testStrategy.testRedirectSignedLoginLogout();
+ }
+
+ @Test
+ public void testPostSignedLoginLogoutEmailNameID() {
+ testStrategy.testPostSignedLoginLogoutEmailNameID();
+ }
+
+ @Test
+ public void testPostEncryptedLoginLogout() {
+ testStrategy.testPostEncryptedLoginLogout();
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogoutFrontNoSSO() {
+ testStrategy.testRedirectSignedLoginLogoutFrontNoSSO();
+ }
+
+ @Test
+ public void testPostSimpleLoginLogout() {
+ testStrategy.testPostSimpleLoginLogout();
+ }
+
+ @Test
+ public void testPostSignedLoginLogoutTransientNameID() {
+ testStrategy.testPostSignedLoginLogoutTransientNameID();
+ }
+
+ @Test
+ public void testPostSimpleLoginLogoutIdpInitiated() {
+ testStrategy.testPostSimpleLoginLogoutIdpInitiated();
+ }
+
+ @Test
+ public void testAttributes() throws Exception {
+ testStrategy.testAttributes();
+ }
+
+ @Test
+ public void testPostSignedLoginLogoutPersistentNameID() {
+ testStrategy.testPostSignedLoginLogoutPersistentNameID();
+ }
+
+ @Test
+ public void testPostBadClientSignature() {
+ testStrategy.testPostBadClientSignature();
+ }
+
+ @Test
+ public void testRedirectSignedLoginLogoutFront() {
+ testStrategy.testRedirectSignedLoginLogoutFront();
+ }
+
+ @Test
+ public void testPostSignedLoginLogout() {
+ testStrategy.testPostSignedLoginLogout();
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlKeycloakRule.java
new file mode 100755
index 0000000..e1fd3c8
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/samlfilter/SamlKeycloakRule.java
@@ -0,0 +1,125 @@
+package org.keycloak.testsuite.samlfilter;
+
+import io.undertow.security.idm.Account;
+import io.undertow.security.idm.Credential;
+import io.undertow.security.idm.IdentityManager;
+import io.undertow.server.handlers.resource.Resource;
+import io.undertow.server.handlers.resource.ResourceChangeListener;
+import io.undertow.server.handlers.resource.ResourceManager;
+import io.undertow.server.handlers.resource.URLResource;
+import io.undertow.servlet.api.DeploymentInfo;
+import io.undertow.servlet.api.FilterInfo;
+import io.undertow.servlet.api.LoginConfig;
+import io.undertow.servlet.api.SecurityConstraint;
+import io.undertow.servlet.api.ServletInfo;
+import io.undertow.servlet.api.WebResourceCollection;
+import org.keycloak.adapters.saml.servlet.SamlFilter;
+import org.keycloak.adapters.saml.undertow.SamlServletExtension;
+import org.keycloak.testsuite.keycloaksaml.SendUsernameServlet;
+import org.keycloak.testsuite.rule.AbstractKeycloakRule;
+
+import javax.servlet.DispatcherType;
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public abstract class SamlKeycloakRule extends AbstractKeycloakRule {
+
+ public static class TestResourceManager implements ResourceManager {
+
+ private final String basePath;
+
+ public TestResourceManager(String basePath){
+ this.basePath = basePath;
+ }
+
+ @Override
+ public Resource getResource(String path) throws IOException {
+ String temp = path;
+ String fullPath = basePath + temp;
+ URL url = getClass().getResource(fullPath);
+ if (url == null) {
+ System.out.println("url is null: " + fullPath);
+ }
+ return new URLResource(url, url.openConnection(), path);
+ }
+
+ @Override
+ public boolean isResourceChangeListenerSupported() {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void registerResourceChangeListener(ResourceChangeListener listener) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void removeResourceChangeListener(ResourceChangeListener listener) {
+ throw new RuntimeException();
+ }
+
+ @Override
+ public void close() throws IOException {
+ throw new RuntimeException();
+ }
+ }
+
+ public static class TestIdentityManager implements IdentityManager {
+ @Override
+ public Account verify(Account account) {
+ return account;
+ }
+
+ @Override
+ public Account verify(String userName, Credential credential) {
+ throw new RuntimeException("WTF");
+ }
+
+ @Override
+ public Account verify(Credential credential) {
+ throw new RuntimeException();
+ }
+ }
+
+ @Override
+ protected void setupKeycloak() {
+ String realmJson = getRealmJson();
+ server.importRealm(getClass().getResourceAsStream(realmJson));
+ initWars();
+ }
+
+ public abstract void initWars();
+
+ public void initializeSamlSecuredWar(String warResourcePath, String contextPath, String warDeploymentName, ClassLoader classLoader) {
+
+ ServletInfo regularServletInfo = new ServletInfo("servlet", SendUsernameServlet.class)
+ .addMapping("/*");
+
+ FilterInfo samlFilter = new FilterInfo("saml-filter", SamlFilter.class);
+
+
+ ResourceManager resourceManager = new TestResourceManager(warResourcePath);
+
+ DeploymentInfo deploymentInfo = new DeploymentInfo()
+ .setClassLoader(classLoader)
+ .setIdentityManager(new TestIdentityManager())
+ .setContextPath(contextPath)
+ .setDeploymentName(warDeploymentName)
+ .setResourceManager(resourceManager)
+ .addServlets(regularServletInfo)
+ .addFilter(samlFilter)
+ .addFilterUrlMapping("saml-filter", "/*", DispatcherType.REQUEST)
+ .addServletExtension(new SamlServletExtension());
+ server.getServer().deploy(deploymentInfo);
+ }
+
+ public String getRealmJson() {
+ return "/keycloak-saml/testsaml.json";
+ }
+
+
+}
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index 8460a13..e042f3c 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp"
signaturesRequired="true">
<SingleSignOnService requestBinding="POST"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 2f7c581..62c1b07 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index c892ec7..c4aece9 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index caaff5c..72cf80b 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index 51c8e0c..e268f52 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 909216d..1d06bf9 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 9a57c85..032aabf 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index 20d9348..9b66e70 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp"
signaturesRequired="true">
<SingleSignOnService requestBinding="POST"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index ef9856a..5d35145 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 7bc05f6..bcdb4a7 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 5d614fa..041d364 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index 501e8f5..a9d22aa 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java
index ffb3e0c..257fb55 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java
@@ -20,6 +20,8 @@ import org.keycloak.admin.client.Keycloak;
import org.keycloak.models.Constants;
import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.keycloak.testsuite.util.OAuthClient;
+
import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
@@ -55,6 +57,10 @@ public class ContainersTestEnricher {
@ClassScoped
private InstanceProducer<Keycloak> adminClient;
+ @Inject
+ @ClassScoped
+ private InstanceProducer<OAuthClient> oauthClient;
+
private ContainerController controller;
private final boolean migrationTests = System.getProperty("migration", "false").equals("true");
@@ -92,6 +98,7 @@ public class ContainersTestEnricher {
initializeTestContext(testClass);
initializeAdminClient();
+ initializeOAuthClient();
}
private void initializeTestContext(Class testClass) {
@@ -116,6 +123,10 @@ public class ContainersTestEnricher {
MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
}
+ private void initializeOAuthClient() {
+ oauthClient.set(new OAuthClient(getAuthServerContextRootFromSystemProperty() + "/auth"));
+ }
+
/**
*
* @param testClass
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java
index cf25d05..73583cf 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java
@@ -1,8 +1,6 @@
package org.keycloak.testsuite.arquillian;
-import org.keycloak.testsuite.arquillian.provider.URLProvider;
-import org.keycloak.testsuite.arquillian.provider.SuiteContextProvider;
-import org.keycloak.testsuite.arquillian.provider.TestContextProvider;
+import org.keycloak.testsuite.arquillian.provider.*;
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
import org.jboss.arquillian.container.test.impl.enricher.resource.URLResourceProvider;
import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
@@ -12,7 +10,6 @@ import org.jboss.arquillian.graphene.location.CustomizableURLResourceProvider;
import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
import org.keycloak.testsuite.arquillian.jira.JiraTestExecutionDecider;
-import org.keycloak.testsuite.arquillian.provider.AdminClientProvider;
import org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer;
/**
@@ -27,7 +24,8 @@ public class KeycloakArquillianExtension implements LoadableExtension {
builder
.service(ResourceProvider.class, SuiteContextProvider.class)
.service(ResourceProvider.class, TestContextProvider.class)
- .service(ResourceProvider.class, AdminClientProvider.class);
+ .service(ResourceProvider.class, AdminClientProvider.class)
+ .service(ResourceProvider.class, OAuthClientProvider.class);
builder
.service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java
new file mode 100644
index 0000000..4f54d18
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/OAuthClientProvider.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.arquillian.provider;
+
+import org.jboss.arquillian.core.api.Instance;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider;
+import org.keycloak.testsuite.util.OAuthClient;
+
+import java.lang.annotation.Annotation;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthClientProvider implements ResourceProvider {
+
+ @Inject
+ Instance<OAuthClient> oauthClient;
+
+ @Override
+ public boolean canProvide(Class<?> type) {
+ return OAuthClient.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
+ return oauthClient.get();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java
index e2dc4db..1ac7393 100644
--- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java
@@ -6,13 +6,7 @@ import io.undertow.servlet.api.DefaultServletConfig;
import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.FilterInfo;
import io.undertow.servlet.api.ServletInfo;
-
-import java.util.Collection;
-import java.util.Map;
-import javax.servlet.DispatcherType;
-
import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
-
import org.jboss.arquillian.container.spi.client.container.DeploymentException;
import org.jboss.arquillian.container.spi.client.container.LifecycleException;
import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription;
@@ -25,10 +19,13 @@ import org.jboss.resteasy.spi.ResteasyDeployment;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.shrinkwrap.descriptor.api.Descriptor;
import org.jboss.shrinkwrap.undertow.api.UndertowWebArchive;
-import org.keycloak.services.filters.ClientConnectionFilter;
import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.resources.KeycloakApplication;
+import javax.servlet.DispatcherType;
+import java.util.Collection;
+import java.util.Map;
+
public class CustomUndertowContainer implements DeployableContainer<CustomUndertowContainerConfiguration> {
protected final Logger log = Logger.getLogger(this.getClass());
@@ -52,10 +49,6 @@ public class CustomUndertowContainer implements DeployableContainer<CustomUndert
di.addFilter(filter);
di.addFilterUrlMapping("SessionFilter", "/*", DispatcherType.REQUEST);
- FilterInfo connectionFilter = Servlets.filter("ClientConnectionFilter", ClientConnectionFilter.class);
- di.addFilter(connectionFilter);
- di.addFilterUrlMapping("ClientConnectionFilter", "/*", DispatcherType.REQUEST);
-
return di;
}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
new file mode 100644
index 0000000..3e52bbb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/OAuthClient.java
@@ -0,0 +1,77 @@
+package org.keycloak.testsuite.util;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.http.NameValuePair;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.message.BasicNameValuePair;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.JsonSerialization;
+
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class OAuthClient {
+
+ private String baseUrl;
+
+ public OAuthClient(String baseUrl) {
+ this.baseUrl = baseUrl;
+ }
+
+ public AccessTokenResponse getToken(String realm, String clientId, String clientSecret, String username, String password) {
+ CloseableHttpClient httpclient = HttpClients.createDefault();
+ try {
+ HttpPost post = new HttpPost(OIDCLoginProtocolService.tokenUrl(UriBuilder.fromUri(baseUrl)).build(realm));
+
+ List<NameValuePair> parameters = new LinkedList<NameValuePair>();
+ parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD));
+ parameters.add(new BasicNameValuePair("username", username));
+ parameters.add(new BasicNameValuePair("password", password));
+ if (clientSecret != null) {
+ String authorization = BasicAuthHelper.createHeader(clientId, clientSecret);
+ post.setHeader("Authorization", authorization);
+ } else {
+ parameters.add(new BasicNameValuePair("client_id", clientId));
+ }
+
+ UrlEncodedFormEntity formEntity;
+ try {
+ formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException(e);
+ }
+ post.setEntity(formEntity);
+
+ CloseableHttpResponse response = httpclient.execute(post);
+
+ if (response.getStatusLine().getStatusCode() != 200) {
+ throw new RuntimeException("Failed to retrieve token: " + response.getStatusLine().toString() + " / " + IOUtils.toString(response.getEntity().getContent()));
+ }
+
+ return JsonSerialization.readValue(response.getEntity().getContent(), AccessTokenResponse.class);
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ finally {
+ try {
+ httpclient.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
index 3399115..99c5d67 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -20,6 +20,7 @@ import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import static org.keycloak.testsuite.admin.Users.setPasswordFor;
import org.keycloak.testsuite.arquillian.SuiteContext;
+import org.keycloak.testsuite.util.OAuthClient;
import org.openqa.selenium.WebDriver;
import org.keycloak.testsuite.auth.page.AuthServer;
import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
@@ -51,6 +52,9 @@ public abstract class AbstractKeycloakTest {
@ArquillianResource
protected Keycloak adminClient;
+ @ArquillianResource
+ protected OAuthClient oauthClient;
+
protected List<RealmRepresentation> testRealmReps;
@Drone
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
new file mode 100644
index 0000000..1d4b011
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/client/ClientRegistrationTest.java
@@ -0,0 +1,306 @@
+package org.keycloak.testsuite.client;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.client.registration.ClientRegistration;
+import org.keycloak.client.registration.ClientRegistrationException;
+import org.keycloak.client.registration.HttpErrorException;
+import org.keycloak.models.AdminRoles;
+import org.keycloak.models.Constants;
+import org.keycloak.representations.AccessTokenResponse;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.AbstractKeycloakTest;
+
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ClientRegistrationTest extends AbstractKeycloakTest {
+
+ private static final String REALM_NAME = "test";
+ private static final String CLIENT_ID = "test-client";
+ private static final String CLIENT_SECRET = "test-client-secret";
+
+ private ClientRegistration clientRegistrationAsAdmin;
+ private ClientRegistration clientRegistrationAsClient;
+
+ @Before
+ public void before() throws ClientRegistrationException {
+ clientRegistrationAsAdmin = clientBuilder().auth(getToken("manage-clients", "password")).build();
+ clientRegistrationAsClient = clientBuilder().auth(CLIENT_ID, CLIENT_SECRET).build();
+ }
+
+ @After
+ public void after() throws ClientRegistrationException {
+ clientRegistrationAsAdmin.close();
+ clientRegistrationAsClient.close();
+ }
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation rep = new RealmRepresentation();
+ rep.setEnabled(true);
+ rep.setRealm(REALM_NAME);
+ rep.setUsers(new LinkedList<UserRepresentation>());
+
+ LinkedList<CredentialRepresentation> credentials = new LinkedList<>();
+ CredentialRepresentation password = new CredentialRepresentation();
+ password.setType(CredentialRepresentation.PASSWORD);
+ password.setValue("password");
+ credentials.add(password);
+
+ UserRepresentation user = new UserRepresentation();
+ user.setEnabled(true);
+ user.setUsername("manage-clients");
+ user.setCredentials(credentials);
+ user.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.MANAGE_CLIENTS)));
+
+ rep.getUsers().add(user);
+
+ UserRepresentation user2 = new UserRepresentation();
+ user2.setEnabled(true);
+ user2.setUsername("create-clients");
+ user2.setCredentials(credentials);
+ user2.setClientRoles(Collections.singletonMap(Constants.REALM_MANAGEMENT_CLIENT_ID, Collections.singletonList(AdminRoles.CREATE_CLIENT)));
+
+ rep.getUsers().add(user2);
+
+ UserRepresentation user3 = new UserRepresentation();
+ user3.setEnabled(true);
+ user3.setUsername("no-access");
+ user3.setCredentials(credentials);
+
+ rep.getUsers().add(user3);
+
+ testRealms.add(rep);
+ }
+
+ private void registerClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
+ ClientRepresentation client = new ClientRepresentation();
+ client.setClientId(CLIENT_ID);
+ client.setSecret(CLIENT_SECRET);
+
+ ClientRepresentation createdClient = clientRegistration.create(client);
+ assertEquals(CLIENT_ID, createdClient.getClientId());
+
+ client = adminClient.realm(REALM_NAME).clients().get(createdClient.getId()).toRepresentation();
+ assertEquals(CLIENT_ID, client.getClientId());
+
+ AccessTokenResponse token2 = oauthClient.getToken(REALM_NAME, CLIENT_ID, CLIENT_SECRET, "manage-clients", "password");
+ assertNotNull(token2.getToken());
+ }
+
+ @Test
+ public void registerClientAsAdmin() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ }
+
+ @Test
+ public void registerClientAsAdminWithCreateOnly() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ try {
+ registerClient(clientRegistration);
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void registerClientAsAdminWithNoAccess() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ try {
+ registerClient(clientRegistration);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void getClientAsAdminWithCreateOnly() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ try {
+ clientRegistration.get(CLIENT_ID);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void wrongClient() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+
+ ClientRepresentation client = new ClientRepresentation();
+ client.setClientId("test-client-2");
+ client.setSecret("test-client-2-secret");
+
+ clientRegistrationAsAdmin.create(client);
+
+ ClientRegistration clientRegistration = clientBuilder().auth("test-client-2", "test-client-2-secret").build();
+
+ client = clientRegistration.get("test-client-2");
+ assertNotNull(client);
+ assertEquals("test-client-2", client.getClientId());
+
+ try {
+ try {
+ clientRegistration.get(CLIENT_ID);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+
+ client = clientRegistrationAsAdmin.get(CLIENT_ID);
+ try {
+ clientRegistration.update(client);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+
+ try {
+ clientRegistration.delete(CLIENT_ID);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ }
+ }
+ finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void getClientAsAdminWithNoAccess() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ try {
+ clientRegistration.get(CLIENT_ID);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ private void updateClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
+ ClientRepresentation client = clientRegistration.get(CLIENT_ID);
+ client.setRedirectUris(Collections.singletonList("http://localhost:8080/app"));
+
+ clientRegistration.update(client);
+
+ ClientRepresentation updatedClient = clientRegistration.get(CLIENT_ID);
+
+ assertEquals(1, updatedClient.getRedirectUris().size());
+ assertEquals("http://localhost:8080/app", updatedClient.getRedirectUris().get(0));
+ }
+
+ @Test
+ public void updateClientAsAdmin() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ updateClient(clientRegistrationAsAdmin);
+ }
+
+ @Test
+ public void updateClientAsAdminWithCreateOnly() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ try {
+ updateClient(clientRegistration);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void updateClientAsAdminWithNoAccess() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ try {
+ updateClient(clientRegistration);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void updateClientAsClient() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ updateClient(clientRegistrationAsClient);
+ }
+
+ private void deleteClient(ClientRegistration clientRegistration) throws ClientRegistrationException {
+ clientRegistration.delete(CLIENT_ID);
+
+ // Can't authenticate as client after client is deleted
+ ClientRepresentation client = clientRegistrationAsAdmin.get(CLIENT_ID);
+ assertNull(client);
+ }
+
+ @Test
+ public void deleteClientAsAdmin() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ deleteClient(clientRegistrationAsAdmin);
+ }
+
+ @Test
+ public void deleteClientAsAdminWithCreateOnly() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("create-clients", "password")).build();
+ try {
+ deleteClient(clientRegistration);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void deleteClientAsAdminWithNoAccess() throws ClientRegistrationException {
+ ClientRegistration clientRegistration = clientBuilder().auth(getToken("no-access", "password")).build();
+ try {
+ deleteClient(clientRegistration);
+ fail("Expected 403");
+ } catch (ClientRegistrationException e) {
+ assertEquals(403, ((HttpErrorException) e.getCause()).getStatusLine().getStatusCode());
+ } finally {
+ clientRegistration.close();
+ }
+ }
+
+ @Test
+ public void deleteClientAsClient() throws ClientRegistrationException {
+ registerClient(clientRegistrationAsAdmin);
+ deleteClient(clientRegistrationAsClient);
+ }
+
+ private ClientRegistration.ClientRegistrationBuilder clientBuilder() {
+ return ClientRegistration.create().realm("test").authServerUrl(testContext.getAuthServerContextRoot() + "/auth");
+ }
+
+ private String getToken(String username, String password) {
+ return oauthClient.getToken(REALM_NAME, "security-admin-console", null, username, password).getToken();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java
index 13edb9d..4930604 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java
@@ -24,6 +24,7 @@ import org.junit.Assert;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
import org.keycloak.representations.idm.UserRepresentation;
@@ -45,6 +46,7 @@ import org.openqa.selenium.Cookie;
* @author tkyjovsk
* @author vramik
*/
+@Ignore
public class LoginSettingsTest extends AbstractRealmTest {
private static final String NEW_USERNAME = "newUsername";
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
index 0263513..96d8c9c 100644
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
@@ -19,6 +19,7 @@ package org.keycloak.testsuite.console.realm;
import org.jboss.arquillian.graphene.page.Page;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.keycloak.testsuite.auth.page.account.Account;
import org.keycloak.testsuite.console.page.realm.BruteForceDetection;
@@ -38,6 +39,7 @@ import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
* @author Filip Kiss
* @author mhajas
*/
+@Ignore
public class SecurityDefensesTest extends AbstractRealmTest {
@Page
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
index 8f900b9..58d32e4 100644
--- a/testsuite/integration-arquillian/tests/pom.xml
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -203,6 +203,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-client-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty81/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty91/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/jetty/jetty92/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
testsuite/performance/pom.xml 4(+0 -4)
diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml
index 0842cab..b8871e4 100755
--- a/testsuite/performance/pom.xml
+++ b/testsuite/performance/pom.xml
@@ -21,10 +21,6 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
- <artifactId>keycloak-core-jaxrs</artifactId>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
<artifactId>keycloak-services</artifactId>
</dependency>
<dependency>
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat6/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat6/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/tomcat6/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat6/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat7/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat7/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/tomcat7/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat7/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index fa1b259..9baacaf 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
index 8b1bf3d..cd5ff99 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/bad-realm-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
index 24bfb64..d269a57 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/encrypted-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
index 4fc1dfc..6210399 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/mappers/WEB-INF/keycloak-saml.xml
@@ -5,10 +5,10 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="memberOf"/>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
index a8d84c1..e7f205a 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-front-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
index 724919b..b440776 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-get/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
index 1ffeb1b..532d898 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-metadata/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
index dcd6f5b..9f5d49d 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
index dbb9c31..332b828 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-email/WEB-INF/keycloak-saml.xml
@@ -12,9 +12,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
index 0415bb1..36a4435 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-persistent/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
index 4b96159..445d765 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/signed-post-transient/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
diff --git a/testsuite/tomcat8/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml b/testsuite/tomcat8/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
index e831ff7..bc0995c 100755
--- a/testsuite/tomcat8/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/tomcat8/src/test/resources/keycloak-saml/simple-post/WEB-INF/keycloak-saml.xml
@@ -5,9 +5,9 @@
logoutPage="/logout.jsp"
forceAuthentication="false">
<PrincipalNameMapping policy="FROM_NAME_ID"/>
- <RoleMapping>
+ <RoleIdentifiers>
<Attribute name="Role"/>
- </RoleMapping>
+ </RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService requestBinding="POST"
bindingUrl="http://localhost:8081/auth/realms/demo/protocol/saml"