keycloak-aplcache
Changes
connections/jpa/pom.xml 2(+1 -1)
connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java 2(+2 -0)
core/pom.xml 5(+0 -5)
core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionRepresentation.java 1(+0 -1)
distribution/docs-dist/pom.xml 28(+28 -0)
events/jpa/pom.xml 2(+1 -1)
forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ApplicationsBean.java 22(+19 -3)
forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js 84(+35 -49)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-role-detail.html 7(+7 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/view-object.html 3(+3 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/password-policy.html 2(+1 -1)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html 30(+12 -18)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html 12(+12 -0)
forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html 7(+7 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/ClientCredentialsProvider.java 2(+1 -1)
integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java 2(+1 -1)
integration/adapter-core/src/main/java/org/keycloak/adapters/OAuthRequestAuthenticator.java 6(+6 -0)
integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java 7(+6 -1)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java 1(+0 -1)
integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java 1(+0 -1)
misc/ReleaseProcess.md 19(+4 -15)
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java 1(+1 -0)
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java 12(+12 -0)
model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java 48(+48 -0)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java 1(+0 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java 2(+0 -2)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java 1(+0 -1)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java 8(+6 -2)
model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java 20(+20 -0)
model/jpa/pom.xml 8(+1 -7)
model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java 32(+32 -0)
model/sessions-infinispan/pom.xml 4(+0 -4)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/UserSessionAdapter.java 6(+5 -1)
model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java 6(+6 -0)
pom.xml 112(+53 -59)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AttributeStatementType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AudienceRestrictionCondition.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthenticationStatementType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthorizationDecisionStatementType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionAbstractType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsAbstractType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11DoNotCacheConditionType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11NameIdentifierType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementAbstractType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11SubjectStatementType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AttributeQueryType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthenticationQueryType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthorizationDecisionQueryType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11QueryAbstractType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestAbstractType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseAbstractType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11SubjectQueryAbstractType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AudienceRestrictionType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextClassRefType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclRefType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthzDecisionStatementType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedAssertionType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/KeyInfoConfirmationDataType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationDataType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationType.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ManageNameIDRequestType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingRequestType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingResponseType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/SubjectQueryAbstractType.java 2(+0 -2)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ConfigurationException.java 3(+1 -2)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/AssertionExpiredException.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssueInstantMissingException.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssuerNotTrustedException.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/SignatureValidationException.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyConfigurationException.java 1(+0 -1)
saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyProcessingException.java 1(+0 -1)
services/pom.xml 25(+0 -25)
services/src/docs/asciidoc/overview.adoc 13(+13 -0)
services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java 2(+1 -1)
testsuite/integration/pom.xml 2(+1 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java 9(+3 -6)
testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java 105(+92 -13)
testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java 1(+0 -1)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPTestConfiguration.java 6(+6 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java 13(+6 -7)
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java 18(+18 -0)
testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java 51(+51 -0)
testsuite/integration-arquillian/pom.xml 216(+35 -181)
testsuite/integration-arquillian/README.md 66(+53 -13)
testsuite/integration-arquillian/README_old.md 189(+189 -0)
testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/add-dialect-logger.xsl 28(+28 -0)
testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/add-dialect-logger.xsl 28(+28 -0)
testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/add-dialect-logger.xsl 28(+28 -0)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/account/AccountManagementTest.java 121(+0 -121)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/AbstractTest.java 91(+0 -91)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/fragment/Navigation.java 162(+0 -162)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Account.java 93(+0 -93)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Client.java 104(+0 -104)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/Role.java 72(+0 -72)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/model/User.java 146(+0 -146)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/account/PasswordPage.java 82(+0 -82)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/RegisterPage.java 99(+0 -99)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/ClientPage.java 134(+0 -134)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/LoginSettingsPage.java 63(+0 -63)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/PasswordPolicyPage.java 71(+0 -71)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/RolesPage.java 118(+0 -118)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/SecurityPage.java 109(+0 -109)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/user/RoleMappingsPage.java 71(+0 -71)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/page/settings/user/UserPage.java 204(+0 -204)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/test/client/AddNewClientTest.java 109(+0 -109)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/test/role/AddNewRoleTest.java 92(+0 -92)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/test/settings/ThemesSettingsTest.java 56(+0 -56)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/test/user/AddNewUserTest.java 119(+0 -119)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/admin/test/user/RoleMappingsTest.java 72(+0 -72)
testsuite/integration-arquillian/src/test/java/org/keycloak/testsuite/login/RegisterNewUserTest.java 136(+0 -136)
testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/karaf/src/test/java/org/keycloak/testsuite/adapter/example/KarafFuseExampleAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatBasicAuthExampleAdapterTest.java 15(+15 -0)
testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatDemoExampleAdapterTest.java 16(+16 -0)
testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatDemoServletsAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatSessionServletAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyBasicAuthExampleAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyDemoServletsAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflySessionServletAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8BasicAuthExampleAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8DemoServletsAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8SessionServletAdapterTest.java 14(+14 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/arquillian.xsl 16(+16 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/standalone.xsl 51(+51 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeBasicAuthExampleAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeCorsExampleAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeDemoExampleAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeJSConsoleExampleAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeDemoServletsAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeSessionServletAdapterTest.java 12(+12 -0)
testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/resources/web.xml 9(+9 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/AdapterLibsMode.java 30(+30 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AngularCorsProductExample.java 77(+77 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AppServerContextRoot.java 23(+23 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuthExample.java 41(+41 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CorsDatabaseServiceExample.java 26(+26 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDb.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDbErrorPage.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortal.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalExample.java 80(+80 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/DatabaseServiceExample.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AbstractFuseExample.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AdminInterface.java 17(+17 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerListing.java 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerPortalFuseExample.java 34(+34 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/ProductPortalFuseExample.java 50(+50 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java 39(+39 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java 70(+70 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant.java 46(+46 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenantExample.java 46(+46 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortal.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortalExample.java 59(+59 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortal.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SessionPortal.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CallAuthenticatedServlet.java 39(+39 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java 59(+59 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ErrorServlet.java 27(+27 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java 43(+43 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantResolver.java 46(+46 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantServlet.java 48(+48 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ProductServlet.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SessionServlet.java 40(+40 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java 111(+111 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/Users.java 67(+67 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AdapterLibsLocationProperty.java 19(+19 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContainer.java 20(+20 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContext.java 18(+18 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AuthServerContext.java 18(+18 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/MultipleContainersExtension.java 61(+61 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/Registry.java 148(+148 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/RegistryCreator.java 186(+186 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/SecurityActions.java 307(+307 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java 187(+187 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java 140(+140 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentTargetModifier.java 40(+40 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JBossJiraParser.java 43(+43 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Jira.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JiraTestExecutionDecider.java 61(+61 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Status.java 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java 49(+49 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/AdminClientProvider.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/SuiteContextProvider.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/TestContextProvider.java 30(+30 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java 79(+79 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java 34(+34 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java 50(+50 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java 144(+144 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainerConfiguration.java 35(+35 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Account.java 81(+81 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java 59(+59 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountManagement.java 91(+48 -43)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Applications.java 52(+34 -18)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Autheticator.java 19(+11 -8)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ChangePassword.java 39(+17 -22)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java 24(+24 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/PasswordFields.java 56(+24 -32)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Sessions.java 57(+30 -27)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java 58(+58 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServer.java 34(+34 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServerContextRoot.java 26(+26 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java 24(+24 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java 72(+72 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java 68(+68 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java 125(+125 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java 13(+13 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java 15(+15 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java 78(+78 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java 63(+63 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java 27(+27 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java 21(+21 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java 9(+9 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java 88(+88 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java 47(+47 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java 121(+121 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java 49(+49 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java 147(+147 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java 212(+212 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java 85(+85 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java 100(+100 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java 55(+55 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java 107(+107 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java 138(+138 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java 18(+18 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java 91(+91 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java 23(+23 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java 230(+230 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java 25(+25 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java 104(+104 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java 99(+99 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java 35(+35 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java 80(+80 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java 20(+20 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java 130(+130 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java 34(+34 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java 76(+76 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/FlashMessage.java 37(+18 -19)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Menu.java 77(+42 -35)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java 36(+36 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/OnOffSwitch.java 75(+42 -33)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/PickList.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/idp/IdentityProviderSettings.java 103(+57 -46)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CreateRealm.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java 70(+70 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/GeneralSettings.java 7(+3 -4)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java 121(+121 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java 72(+72 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java 197(+197 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/ThemeSettings.java 36(+13 -23)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/TokenSettings.java 81(+48 -33)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java 23(+23 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/DefaultRoles.java 25(+13 -12)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RealmRoles.java 27(+16 -11)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java 33(+33 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java 182(+182 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java 114(+114 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java 85(+85 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/RealmSessions.java 21(+12 -9)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java 32(+32 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java 23(+23 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java 83(+83 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java 22(+22 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java 139(+139 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java 28(+28 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java 46(+46 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java 142(+142 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java 14(+14 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Provider.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/RequiredUserAction.java 30(+16 -14)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/SocialProvider.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/model/Theme.java 2(+1 -1)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java 83(+83 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java 23(+23 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java 61(+61 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java 43(+43 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java 128(+128 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/SecondBrowser.java 21(+11 -10)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java 57(+57 -0)
testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/WaitUtils.java 61(+28 -33)
testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java 104(+104 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java 176(+176 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java 31(+31 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java 80(+80 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java 67(+67 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java 132(+132 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RememberMeTest.java 53(+43 -10)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java 101(+101 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java 88(+88 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java 139(+139 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java 62(+62 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java 45(+45 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java 68(+68 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java 105(+105 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java 157(+157 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java 131(+131 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java 133(+133 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java 386(+386 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSessionServletAdapterTest.java 184(+184 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java 111(+111 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java 164(+164 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/RequiredActionsTest.java 137(+137 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java 74(+74 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java 273(+273 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java 163(+163 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java 84(+84 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/LoginEventsTest.java 85(+85 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java 71(+71 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/idp/IdentityProviderTest.java 73(+36 -37)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/AbstractRealmTest.java 27(+27 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java 266(+266 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java 255(+255 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/ThemeSettingsTest.java 57(+38 -19)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/TokensTest.java 94(+52 -42)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/AbstractRolesTest.java 26(+26 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/DefaultRolesTest.java 61(+61 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/RealmRolesTest.java 229(+229 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/sessions/SessionsTest.java 47(+28 -19)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/AbstractUserTest.java 39(+39 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/RequiredUserActionsTest.java 147(+147 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UserAttributesTest.java 90(+90 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UsersTest.java 52(+52 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AttributesAssert.java 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java 54(+54 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailServer.java 90(+90 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/TestEventsLogger.java 44(+44 -0)
testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java 95(+95 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak-relative.json 9(+9 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/web.xml 41(+41 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/web.xml 59(+59 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json 11(+11 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-cookie.json 12(+12 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-relative.json 9(+9 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/web.xml 59(+59 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json 161(+161 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml 40(+40 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml 16(+16 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant1-keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant2-keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/web.xml 42(+42 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak-relative.json 9(+9 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/web.xml 40(+40 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/web.xml 30(+30 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/META-INF/context.xml 3(+3 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/jetty-web.xml 29(+29 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/keycloak.json 10(+10 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/web.xml 40(+40 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant1-realm.json 82(+82 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant2-realm.json 72(+72 -0)
testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json 99(+99 -0)
testsuite/integration-arquillian/tests/pom.xml 597(+597 -0)
testsuite/jetty/jetty81/pom.xml 2(+1 -1)
testsuite/jetty/jetty91/pom.xml 2(+1 -1)
testsuite/jetty/jetty92/pom.xml 2(+1 -1)
testsuite/performance/pom.xml 2(+1 -1)
testsuite/proxy/pom.xml 2(+1 -1)
testsuite/tomcat6/pom.xml 2(+1 -1)
testsuite/tomcat7/pom.xml 2(+1 -1)
testsuite/tomcat8/pom.xml 2(+1 -1)
testsuite/wildfly/pom.xml 2(+1 -1)
Details
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
index 8e4d163..24b56bb 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLEndpoint.java
@@ -240,6 +240,7 @@ public class SAMLEndpoint {
.relayState(relayState);
if (config.isWantAuthnRequestsSigned()) {
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+ .signatureAlgorithm(provider.getSignatureAlgorithm())
.signDocument();
}
try {
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
index 6d87d96..e28aa97 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java
@@ -37,6 +37,7 @@ import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
import org.keycloak.saml.SAML2AuthnRequestBuilder;
import org.keycloak.saml.SAML2LogoutRequestBuilder;
import org.keycloak.saml.SAML2NameIDPolicyBuilder;
+import org.keycloak.saml.SignatureAlgorithm;
import org.keycloak.saml.common.constants.GeneralConstants;
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
@@ -108,6 +109,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
KeyPair keypair = new KeyPair(publicKey, privateKey);
binding.signWith(keypair);
+ binding.signatureAlgorithm(getSignatureAlgorithm());
binding.signDocument();
}
@@ -201,6 +203,7 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
.relayState(userSession.getId());
if (getConfig().isWantAuthnRequestsSigned()) {
binding.signWith(realm.getPrivateKey(), realm.getPublicKey(), realm.getCertificate())
+ .signatureAlgorithm(getSignatureAlgorithm())
.signDocument();
}
return binding;
@@ -250,4 +253,14 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
"</EntityDescriptor>\n";
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
}
+
+ public SignatureAlgorithm getSignatureAlgorithm() {
+ String alg = getConfig().getSignatureAlgorithm();
+ if (alg != null) {
+ SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
+ if (algorithm != null) return algorithm;
+ }
+ return SignatureAlgorithm.RSA_SHA256;
+ }
+
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
index ad11be3..6ab3963 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/SAMLIdentityProviderConfig.java
@@ -87,6 +87,14 @@ public class SAMLIdentityProviderConfig extends IdentityProviderModel {
getConfig().put("wantAuthnRequestsSigned", String.valueOf(wantAuthnRequestsSigned));
}
+ public String getSignatureAlgorithm() {
+ return getConfig().get("signatureAlgorithm");
+ }
+
+ public void setSignatureAlgorithm(String signatureAlgorithm) {
+ getConfig().put("signatureAlgorithm", signatureAlgorithm);
+ }
+
public String getEncryptionPublicKey() {
return getConfig().get("encryptionPublicKey");
}
connections/jpa/pom.xml 2(+1 -1)
diff --git a/connections/jpa/pom.xml b/connections/jpa/pom.xml
index b36b850..0a99ed2 100755
--- a/connections/jpa/pom.xml
+++ b/connections/jpa/pom.xml
@@ -24,7 +24,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 7a55d1e..f8f33be 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -29,6 +29,8 @@
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticatorConfigEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredActionProviderEntity</class>
+ <class>org.keycloak.models.jpa.entities.OfflineUserSessionEntity</class>
+ <class>org.keycloak.models.jpa.entities.OfflineClientSessionEntity</class>
<!-- JpaAuditProviders -->
<class>org.keycloak.events.jpa.EventEntity</class>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml
new file mode 100644
index 0000000..24902d5
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.6.0.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
+ <changeSet author="mposolda@redhat.com" id="1.6.0">
+
+ <addColumn tableName="KEYCLOAK_ROLE">
+ <column name="SCOPE_PARAM_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
+ <constraints nullable="false"/>
+ </column>
+ </addColumn>
+
+ <createTable tableName="OFFLINE_USER_SESSION">
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USER_SESSION_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="DATA" type="CLOB"/>
+ </createTable>
+
+ <createTable tableName="OFFLINE_CLIENT_SESSION">
+ <column name="USER_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CLIENT_SESSION_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="USER_SESSION_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="CLIENT_ID" type="VARCHAR(36)">
+ <constraints nullable="false"/>
+ </column>
+ <column name="DATA" type="CLOB"/>
+ </createTable>
+
+ <addPrimaryKey columnNames="USER_SESSION_ID" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
+ <addPrimaryKey columnNames="CLIENT_SESSION_ID" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="OFFLINE_USER_SESSION" constraintName="FK_OFFLINE_US_SES_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="USER_ID" baseTableName="OFFLINE_CLIENT_SESSION" constraintName="FK_OFFLINE_CL_SES_USER" referencedColumnNames="ID" referencedTableName="USER_ENTITY"/>
+ <addForeignKeyConstraint baseColumnNames="USER_SESSION_ID" baseTableName="OFFLINE_CLIENT_SESSION" constraintName="FK_OFFLINE_CL_US_SES" referencedColumnNames="USER_SESSION_ID" referencedTableName="OFFLINE_USER_SESSION"/>
+
+ </changeSet>
+</databaseChangeLog>
\ No newline at end of file
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
index ca5d0e9..6cd96c6 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -9,4 +9,5 @@
<include file="META-INF/jpa-changelog-1.3.0.xml"/>
<include file="META-INF/jpa-changelog-1.4.0.xml"/>
<include file="META-INF/jpa-changelog-1.5.0.xml"/>
+ <include file="META-INF/jpa-changelog-1.6.0.xml"/>
</databaseChangeLog>
diff --git a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
index 423bf3e..90e8c98 100755
--- a/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
+++ b/connections/mongo/src/main/java/org/keycloak/connections/mongo/DefaultMongoConnectionFactoryProvider.java
@@ -48,6 +48,8 @@ public class DefaultMongoConnectionFactoryProvider implements MongoConnectionPro
"org.keycloak.models.entities.AuthenticationFlowEntity",
"org.keycloak.models.entities.AuthenticatorConfigEntity",
"org.keycloak.models.entities.RequiredActionProviderEntity",
+ "org.keycloak.models.entities.OfflineUserSessionEntity",
+ "org.keycloak.models.entities.OfflineClientSessionEntity",
};
private static final Logger logger = Logger.getLogger(DefaultMongoConnectionFactoryProvider.class);
core/pom.xml 5(+0 -5)
diff --git a/core/pom.xml b/core/pom.xml
index e470755..7ea016a 100755
--- a/core/pom.xml
+++ b/core/pom.xml
@@ -29,27 +29,22 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
diff --git a/core/src/main/java/org/keycloak/OAuth2Constants.java b/core/src/main/java/org/keycloak/OAuth2Constants.java
index e17d21f..022dadd 100644
--- a/core/src/main/java/org/keycloak/OAuth2Constants.java
+++ b/core/src/main/java/org/keycloak/OAuth2Constants.java
@@ -38,6 +38,9 @@ public interface OAuth2Constants {
// https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-03#section-2.2
String CLIENT_ASSERTION_TYPE_JWT = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
+ // http://openid.net/specs/openid-connect-core-1_0.html#OfflineAccess
+ String OFFLINE_ACCESS = "offline_access";
+
}
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionRepresentation.java
index 643170f..0ccdd07 100755
--- a/core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthenticationExecutionRepresentation.java
@@ -8,7 +8,6 @@ import java.util.Comparator;
* @version $Revision: 1 $
*/
public class AuthenticationExecutionRepresentation implements Serializable {
- private static final long serialVersionUID = 1L;
private String authenticatorConfig;
private String authenticator;
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthenticationFlowRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthenticationFlowRepresentation.java
index f8ba6a8..d91e1e6 100755
--- a/core/src/main/java/org/keycloak/representations/idm/AuthenticationFlowRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthenticationFlowRepresentation.java
@@ -8,7 +8,6 @@ import java.util.List;
* @version $Revision: 1 $
*/
public class AuthenticationFlowRepresentation implements Serializable {
- private static final long serialVersionUID = 1L;
private String alias;
private String description;
diff --git a/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
index 1f0b3ad..9bd5ad1 100755
--- a/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/AuthenticatorConfigRepresentation.java
@@ -9,7 +9,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class AuthenticatorConfigRepresentation implements Serializable {
- private static final long serialVersionUID = 1L;
private String alias;
private Map<String, String> config = new HashMap<String, String>();
diff --git a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
index c335ab4..4100785 100755
--- a/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/RoleRepresentation.java
@@ -12,6 +12,7 @@ public class RoleRepresentation {
protected String id;
protected String name;
protected String description;
+ protected Boolean scopeParamRequired;
protected boolean composite;
protected Composites composites;
@@ -46,9 +47,10 @@ public class RoleRepresentation {
public RoleRepresentation() {
}
- public RoleRepresentation(String name, String description) {
+ public RoleRepresentation(String name, String description, boolean scopeParamRequired) {
this.name = name;
this.description = description;
+ this.scopeParamRequired = scopeParamRequired;
}
public String getId() {
@@ -75,6 +77,14 @@ public class RoleRepresentation {
this.description = description;
}
+ public Boolean isScopeParamRequired() {
+ return scopeParamRequired;
+ }
+
+ public void setScopeParamRequired(Boolean scopeParamRequired) {
+ this.scopeParamRequired = scopeParamRequired;
+ }
+
public Composites getComposites() {
return composites;
}
diff --git a/core/src/main/java/org/keycloak/representations/RefreshToken.java b/core/src/main/java/org/keycloak/representations/RefreshToken.java
index 7bc1edf..ff1ce68 100755
--- a/core/src/main/java/org/keycloak/representations/RefreshToken.java
+++ b/core/src/main/java/org/keycloak/representations/RefreshToken.java
@@ -1,6 +1,7 @@
package org.keycloak.representations;
import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.util.RefreshTokenUtil;
import java.util.HashMap;
import java.util.Map;
@@ -11,9 +12,8 @@ import java.util.Map;
*/
public class RefreshToken extends AccessToken {
-
private RefreshToken() {
- type("REFRESH");
+ type(RefreshTokenUtil.TOKEN_TYPE_REFRESH);
}
/**
diff --git a/core/src/main/java/org/keycloak/util/RefreshTokenUtil.java b/core/src/main/java/org/keycloak/util/RefreshTokenUtil.java
new file mode 100644
index 0000000..0a759a5
--- /dev/null
+++ b/core/src/main/java/org/keycloak/util/RefreshTokenUtil.java
@@ -0,0 +1,62 @@
+package org.keycloak.util;
+
+import java.io.IOException;
+
+import org.keycloak.OAuth2Constants;
+import org.keycloak.representations.RefreshToken;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class RefreshTokenUtil {
+
+ public static final String TOKEN_TYPE_REFRESH = "REFRESH";
+
+ public static final String TOKEN_TYPE_OFFLINE = "OFFLINE";
+
+ public static boolean isOfflineTokenRequested(String scopeParam) {
+ if (scopeParam == null) {
+ return false;
+ }
+
+ String[] scopes = scopeParam.split(" ");
+ for (String scope : scopes) {
+ if (OAuth2Constants.OFFLINE_ACCESS.equals(scope)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+ /**
+ * Return refresh token or offline otkne
+ *
+ * @param decodedToken
+ * @return
+ */
+ public static RefreshToken getRefreshToken(byte[] decodedToken) throws IOException {
+ return JsonSerialization.readValue(decodedToken, RefreshToken.class);
+ }
+
+ private static RefreshToken getRefreshToken(String refreshToken) throws IOException {
+ byte[] decodedToken = Base64Url.decode(refreshToken);
+ return getRefreshToken(decodedToken);
+ }
+
+ /**
+ * Return true if given refreshToken represents offline token
+ *
+ * @param refreshToken
+ * @return
+ */
+ public static boolean isOfflineToken(String refreshToken) {
+ try {
+ RefreshToken token = getRefreshToken(refreshToken);
+ return token.getType().equals(TOKEN_TYPE_OFFLINE);
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
+ }
+ }
+
+}
distribution/docs-dist/pom.xml 28(+28 -0)
diff --git a/distribution/docs-dist/pom.xml b/distribution/docs-dist/pom.xml
index 1f1ce1d..fdee7b4 100755
--- a/distribution/docs-dist/pom.xml
+++ b/distribution/docs-dist/pom.xml
@@ -14,12 +14,40 @@
<description/>
<dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
+ <type>pom</type>
+ </dependency>
</dependencies>
+
<build>
<finalName>keycloak-docs-${project.version}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <minmemory>128m</minmemory>
+ <maxmemory>1024m</maxmemory>
+ <dependencySourceIncludes>
+ <dependencySourceInclude>org.keycloak:*</dependencySourceInclude>
+ </dependencySourceIncludes>
+ <includeDependencySources>true</includeDependencySources>
+ <includeTransitiveDependencySources>true</includeTransitiveDependencySources>
+ </configuration>
+ <executions>
+ <execution>
+ <id>aggregate-javadoc</id>
+ <phase>compile</phase>
+ <goals>
+ <goal>javadoc</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
diff --git a/docbook/reference/en/en-US/modules/auth-spi.xml b/docbook/reference/en/en-US/modules/auth-spi.xml
index ff7f061..10cb89d 100755
--- a/docbook/reference/en/en-US/modules/auth-spi.xml
+++ b/docbook/reference/en/en-US/modules/auth-spi.xml
@@ -898,7 +898,7 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
}
]]></programlisting>
- where the <literal>mysecret</literal> needs to be replaced with the real value of client secret. You can obtain it from client admin console.
+ where the <literal>mysecret</literal> needs to be replaced with the real value of client secret. You can obtain it from admin console from client configuration.
</para>
</listitem>
</varlistentry>
@@ -906,7 +906,7 @@ public class SecretQuestionRequiredActionFactory implements RequiredActionFactor
<term>Authentication with signed JWT</term>
<listitem>
<para>
- This is based on the <ulink url="https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-03">JWT Bearer Token Profiles for OAuth 2.0</ulink> specification.
+ This is based on the <ulink url="https://tools.ietf.org/html/rfc7523">JWT Bearer Token Profiles for OAuth 2.0</ulink> specification.
The client/adapter generates the <ulink url="https://tools.ietf.org/html/rfc7519">JWT</ulink> and signs it with his private key.
The Keycloak then verifies the signed JWT with the client's public key and authenticates client based on it.
</para>
diff --git a/events/api/src/main/java/org/keycloak/events/Details.java b/events/api/src/main/java/org/keycloak/events/Details.java
index b7ec677..b9c5338 100755
--- a/events/api/src/main/java/org/keycloak/events/Details.java
+++ b/events/api/src/main/java/org/keycloak/events/Details.java
@@ -20,6 +20,7 @@ public interface Details {
String REMEMBER_ME = "remember_me";
String TOKEN_ID = "token_id";
String REFRESH_TOKEN_ID = "refresh_token_id";
+ String REFRESH_TOKEN_TYPE = "refresh_token_type";
String VALIDATE_ACCESS_TOKEN = "validate_access_token";
String UPDATED_REFRESH_TOKEN_ID = "updated_refresh_token_id";
String NODE_HOST = "node_host";
events/jpa/pom.xml 2(+1 -1)
diff --git a/events/jpa/pom.xml b/events/jpa/pom.xml
index d6e7e9e..9fbd1d8 100755
--- a/events/jpa/pom.xml
+++ b/events/jpa/pom.xml
@@ -35,7 +35,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
diff --git a/examples/fuse/cxf-jaxws/src/main/java/org/keycloak/example/ws/UnknownProductFault.java b/examples/fuse/cxf-jaxws/src/main/java/org/keycloak/example/ws/UnknownProductFault.java
index 671b71e..d9aa035 100644
--- a/examples/fuse/cxf-jaxws/src/main/java/org/keycloak/example/ws/UnknownProductFault.java
+++ b/examples/fuse/cxf-jaxws/src/main/java/org/keycloak/example/ws/UnknownProductFault.java
@@ -4,7 +4,6 @@ import javax.xml.ws.WebFault;
@WebFault(name = "UnknownProductFault")
public class UnknownProductFault extends Exception {
- public static final long serialVersionUID = 20081110144906L;
private org.keycloak.example.ws.types.UnknownProductFault unknownProductFault;
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ApplicationsBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ApplicationsBean.java
index 9d9035a..371b5d0 100644
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ApplicationsBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/ApplicationsBean.java
@@ -1,5 +1,6 @@
package org.keycloak.account.freemarker.model;
+import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -11,6 +12,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.services.offline.OfflineTokenUtils;
import org.keycloak.util.MultivaluedHashMap;
/**
@@ -21,6 +23,9 @@ public class ApplicationsBean {
private List<ApplicationEntry> applications = new LinkedList<ApplicationEntry>();
public ApplicationsBean(RealmModel realm, UserModel user) {
+
+ Set<ClientModel> offlineClients = OfflineTokenUtils.findClientsWithOfflineToken(realm, user);
+
List<ClientModel> realmClients = realm.getClients();
for (ClientModel client : realmClients) {
// Don't show bearerOnly clients
@@ -28,7 +33,7 @@ public class ApplicationsBean {
continue;
}
- Set<RoleModel> availableRoles = TokenManager.getAccess(null, client, user);
+ Set<RoleModel> availableRoles = TokenManager.getAccess(null, false, client, user);
// Don't show applications, which user doesn't have access into (any available roles)
if (availableRoles.isEmpty()) {
continue;
@@ -52,7 +57,13 @@ public class ApplicationsBean {
}
}
- ApplicationEntry appEntry = new ApplicationEntry(realmRolesAvailable, resourceRolesAvailable, realmRolesGranted, resourceRolesGranted, client, claimsGranted);
+ List<String> additionalGrants = new ArrayList<>();
+ if (offlineClients.contains(client)) {
+ additionalGrants.add("${offlineToken}");
+ }
+
+ ApplicationEntry appEntry = new ApplicationEntry(realmRolesAvailable, resourceRolesAvailable, realmRolesGranted, resourceRolesGranted, client,
+ claimsGranted, additionalGrants);
applications.add(appEntry);
}
}
@@ -82,16 +93,18 @@ public class ApplicationsBean {
private final MultivaluedHashMap<String, ClientRoleEntry> resourceRolesGranted;
private final ClientModel client;
private final List<String> claimsGranted;
+ private final List<String> additionalGrants;
public ApplicationEntry(List<RoleModel> realmRolesAvailable, MultivaluedHashMap<String, ClientRoleEntry> resourceRolesAvailable,
List<RoleModel> realmRolesGranted, MultivaluedHashMap<String, ClientRoleEntry> resourceRolesGranted,
- ClientModel client, List<String> claimsGranted) {
+ ClientModel client, List<String> claimsGranted, List<String> additionalGrants) {
this.realmRolesAvailable = realmRolesAvailable;
this.resourceRolesAvailable = resourceRolesAvailable;
this.realmRolesGranted = realmRolesGranted;
this.resourceRolesGranted = resourceRolesGranted;
this.client = client;
this.claimsGranted = claimsGranted;
+ this.additionalGrants = additionalGrants;
}
public List<RoleModel> getRealmRolesAvailable() {
@@ -118,6 +131,9 @@ public class ApplicationsBean {
return claimsGranted;
}
+ public List<String> getAdditionalGrants() {
+ return additionalGrants;
+ }
}
// Same class used in OAuthGrantBean as well. Maybe should be merged into common-freemarker...
diff --git a/forms/common-themes/src/main/resources/theme/base/account/applications.ftl b/forms/common-themes/src/main/resources/theme/base/account/applications.ftl
index 4e01c02..b2bbdf2 100755
--- a/forms/common-themes/src/main/resources/theme/base/account/applications.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/account/applications.ftl
@@ -18,6 +18,7 @@
<td>${msg("availablePermissions")}</td>
<td>${msg("grantedPermissions")}</td>
<td>${msg("grantedPersonalInfo")}</td>
+ <td>${msg("additionalGrants")}</td>
<td>${msg("action")}</td>
</tr>
</thead>
@@ -76,7 +77,13 @@
</td>
<td>
- <#if application.client.consentRequired && application.claimsGranted?has_content>
+ <#list application.additionalGrants as grant>
+ ${advancedMsg(grant)}<#if grant_has_next>, </#if>
+ </#list>
+ </td>
+
+ <td>
+ <#if (application.client.consentRequired && application.claimsGranted?has_content) || application.additionalGrants?has_content>
<button type='submit' class='${properties.kcButtonClass!} ${properties.kcButtonPrimaryClass!}' id='revoke-${application.client.clientId}' name='clientId' value="${application.client.id}">${msg("revoke")}</button>
</#if>
</td>
diff --git a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
index 2c3b8c7..23a112d 100755
--- a/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/account/messages/messages_en.properties
@@ -52,6 +52,7 @@ role_manage-events=Manage events
role_view-profile=View profile
role_manage-account=Manage account
role_read-token=Read token
+role_offline-access=Offline access
client_account=Account
client_security-admin-console=Security Admin Console
client_realm-management=Realm Management
@@ -85,9 +86,11 @@ application=Application
availablePermissions=Available Permissions
grantedPermissions=Granted Permissions
grantedPersonalInfo=Granted Personal Info
+additionalGrants=Additional Grants
action=Action
inResource=in
fullAccess=Full Access
+offlineToken=Offline Token
revoke=Revoke Grant
configureAuthenticators=Configured Authenticators
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 903131d..756c89e 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
@@ -2036,3 +2036,26 @@ module.directive( 'kcOpen', function ( $location ) {
});
};
});
+
+module.directive('kcOnReadFile', function ($parse) {
+ console.debug('kcOnReadFile');
+ return {
+ restrict: 'A',
+ scope: false,
+ link: function(scope, element, attrs) {
+ var fn = $parse(attrs.kcOnReadFile);
+
+ element.on('change', function(onChangeEvent) {
+ var reader = new FileReader();
+
+ reader.onload = function(onLoadEvent) {
+ scope.$apply(function() {
+ fn(scope, {$fileContent:onLoadEvent.target.result});
+ });
+ };
+
+ reader.readAsText((onChangeEvent.srcElement || onChangeEvent.target).files[0]);
+ });
+ }
+ };
+});
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 4027a85..15dc679 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
@@ -155,7 +155,7 @@ module.controller('RealmDropdownCtrl', function($scope, Realm, Current, Auth, $l
}
});
-module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, WhoAmI, $location, Dialog, Notifications, Auth) {
+module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $http, WhoAmI, $location, $route, Dialog, Notifications, Auth, $modal) {
console.log('RealmCreateCtrl');
Current.realm = null;
@@ -169,55 +169,21 @@ module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $
var oldCopy = angular.copy($scope.realm);
- $scope.onFileSelect = function($files) {
- $scope.files = $files;
+ $scope.importFile = function($fileContent){
+ $scope.realm = angular.copy(JSON.parse($fileContent));
+ $scope.importing = true;
};
- $scope.clearFileSelect = function() {
- $scope.files = null;
- }
-
- $scope.uploadFile = function() {
- //$files: an array of files selected, each file has name, size, and type.
- for (var i = 0; i < $scope.files.length; i++) {
- var $file = $scope.files[i];
- $scope.upload = $upload.upload({
- url: authUrl + '/admin/realms', //upload.php script, node.js route, or servlet url
- // method: POST or PUT,
- // headers: {'headerKey': 'headerValue'}, withCredential: true,
- data: {myObj: ""},
- file: $file
- /* set file formData name for 'Content-Desposition' header. Default: 'file' */
- //fileFormDataName: myFile,
- /* customize how data is added to formData. See #40#issuecomment-28612000 for example */
- //formDataAppender: function(formData, key, val){}
- }).progress(function(evt) {
- console.log('percent: ' + parseInt(100.0 * evt.loaded / evt.total));
- }).success(function(data, status, headers) {
- Realm.query(function(data) {
- Current.realms = data;
-
-
- WhoAmI.get(function(user) {
- Auth.user = user;
-
- Notifications.success("The realm has been uploaded.");
-
- var location = headers('Location');
- if (location) {
- $location.url("/realms/" + location.substring(location.lastIndexOf('/') + 1));
- } else {
- $location.url("/realms");
- }
- });
- });
- })
- .error(function() {
- Notifications.error("The realm can not be uploaded. Please verify the file.");
-
- });
- //.then(success, error, progress);
- }
+ $scope.viewImportDetails = function() {
+ $modal.open({
+ templateUrl: resourceUrl + '/partials/modal/view-object.html',
+ controller: 'ObjectModalCtrl',
+ resolve: {
+ object: function () {
+ return $scope.realm;
+ }
+ }
+ })
};
$scope.$watch('realm', function() {
@@ -226,6 +192,12 @@ 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() {
@@ -243,10 +215,17 @@ module.controller('RealmCreateCtrl', function($scope, Current, Realm, $upload, $
};
$scope.cancel = function() {
- window.history.back();
+ $location.url("/");
};
+
+ $scope.reset = function() {
+ $route.reload();
+ }
});
+module.controller('ObjectModalCtrl', function($scope, object) {
+ $scope.object = object;
+});
module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, serverInfo, $http, $location, Dialog, Notifications, WhoAmI, Auth) {
$scope.createRealm = !realm.realm;
@@ -693,10 +672,17 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
}
];
+ $scope.signatureAlgorithms = [
+ "RSA_SHA1",
+ "RSA_SHA256",
+ "RSA_SHA512",
+ "DSA_SHA1"
+ ];
if (instance && instance.alias) {
} else {
$scope.identityProvider.config.nameIDPolicyFormat = $scope.nameIdFormats[0].format;
+ $scope.identityProvider.config.signatureAlgorithm = $scope.signatureAlgorithms[1];
$scope.identityProvider.updateProfileFirstLoginMode = "off";
}
}
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 c0ce766..f93c96e 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
@@ -32,6 +32,13 @@
required> -->
</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>
+ <div class="col-md-6">
+ <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch />
+ </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>
<div class="col-md-6">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/view-object.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/view-object.html
new file mode 100644
index 0000000..ee50aee
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/view-object.html
@@ -0,0 +1,3 @@
+<div style="padding: 20px 20px 10px 20px">
+ <pre ng-bind = "{{object}} | json"></pre>
+</div>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/password-policy.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/password-policy.html
index 2da9b0d..58942ce 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/password-policy.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/password-policy.html
@@ -34,7 +34,7 @@
placeholder="No value assigned" min="1" required>
</td>
<td class="kc-action-cell">
- <button class="btn btn-default btn-block btn-sm" ng-click="removePolicy($index)">Delete</button>
+ <button type="button" class="btn btn-default btn-block btn-sm" ng-click="removePolicy($index)">Delete</button>
</td>
</tr>
</tbody>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
index 630188d..4ac36fd 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/realm-create.html
@@ -1,29 +1,22 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
<form class="form-horizontal" name="realmForm" novalidate>
<fieldset>
- <legend><span class="text">Import Realm</span></legend>
+ <legend><span class="text">Create Realm</span></legend>
<div class="form-group">
- <label for="import-file" class="col-sm-2 control-label">Import JSON File </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>
- <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">{{files[0].name}}</span>
+ <label for="name" class="col-sm-2 control-label">Import</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>
+ <input id="import-file" type="file" class="hidden" kc-on-read-file="importFile($fileContent)">
</div>
- </div>
- <div class="form-group">
- <div class="col-md-10 col-md-offset-2">
- <button type="submit" data-ng-disabled="files.length == 0" data-ng-click="uploadFile()" class="btn btn-primary">Upload</button>
- <button type="submit" data-ng-disabled="files.length == 0" data-ng-click="clearFileSelect()" class="btn btn-default">Cancel</button>
+
+ <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>
</div>
</div>
- </fieldset>
- </form>
- <form class="form-horizontal" name="realmForm" novalidate>
- <fieldset>
- <legend><span class="text">Create Realm</span></legend>
<div class="form-group">
<label for="name" class="col-sm-2 control-label">Name <span class="required">*</span></label>
@@ -42,6 +35,7 @@
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save data-ng-disabled="!changed">Create</button>
+ <button kc-cancel data-ng-click="cancel()">Cancel</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 1723919..5387826 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
@@ -135,6 +135,18 @@
</div>
<kc-tooltip> Indicates whether the identity provider expects signed a AuthnRequest.</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>
+ <div class="col-sm-6">
+ <div>
+ <select class="form-control" id="signatureAlgorithm"
+ ng-model="identityProvider.config.signatureAlgorithm"
+ ng-options="alg for alg in signatureAlgorithms">
+ </select>
+ </div>
+ </div>
+ <kc-tooltip>The signature algorithm to use to sign documents.</kc-tooltip>
+ </div>
<div class="form-group">
<label class="col-md-2 control-label" for="forceAuthn">Force Authentication</label>
<div class="col-md-6">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
index 49d09c8..6bf854e 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/role-detail.html
@@ -28,6 +28,13 @@
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="role.description"></textarea>
</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>
+ <div class="col-md-6">
+ <input ng-model="role.scopeParamRequired" name="scopeParamRequired" id="scopeParamRequired" onoffswitch />
+ </div>
+ </div>
<div class="form-group" data-ng-hide="create">
<label class="col-md-2 control-label" for="compositeSwitch" class="control-label">Composite Roles</label>
<div class="col-md-6">
diff --git a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
index 2257508..584bea3 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/login-update-profile.ftl
@@ -6,6 +6,16 @@
${msg("loginProfileTitle")}
<#elseif section = "form">
<form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
+ <#if realm.editUsernameAllowed>
+ <div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('username',properties.kcFormGroupErrorClass!)}">
+ <div class="${properties.kcLabelWrapperClass!}">
+ <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
+ </div>
+ <div class="${properties.kcInputWrapperClass!}">
+ <input type="text" id="username" name="username" value="${(user.username!'')?html}" class="${properties.kcInputClass!}"/>
+ </div>
+ </div>
+ </#if>
<div class="${properties.kcFormGroupClass!} ${messagesPerField.printIfExists('email',properties.kcFormGroupErrorClass!)}">
<div class="${properties.kcLabelWrapperClass!}">
<label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
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 3dfb8ea..94b0627 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
@@ -104,6 +104,7 @@ role_manage-events=Manage events
role_view-profile=View profile
role_manage-account=Manage account
role_read-token=Read token
+role_offline-access=Offline access
client_account=Account
client_security-admin-console=Security Admin Console
client_realm-management=Realm Management
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
index 6f73cc5..e730c14 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
@@ -70,6 +70,8 @@ public class ProfileBean {
}
+ public String getUsername() { return formData != null ? formData.getFirst("username") : user.getUsername(); }
+
public String getFirstName() {
return formData != null ? formData.getFirst("firstName") : user.getFirstName();
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
index b161ad2..e6ae21d 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
@@ -66,6 +66,10 @@ public class RealmBean {
return realm.isInternationalizationEnabled();
}
+ public boolean isEditUsernameAllowed() {
+ return realm.isEditUsernameAllowed();
+ }
+
public boolean isPassword() {
for (RequiredCredentialModel r : realm.getRequiredCredentials()) {
if (r.getType().equals(CredentialRepresentation.PASSWORD)) {
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/ClientCredentialsProvider.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/ClientCredentialsProvider.java
index 80a0c4d..2c6a92d 100644
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/ClientCredentialsProvider.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/ClientCredentialsProvider.java
@@ -14,7 +14,7 @@ import org.keycloak.adapters.KeycloakDeployment;
*
* You must specify a file
* META-INF/services/org.keycloak.adapters.authentication.ClientCredentialsProvider in the WAR that this class is contained in (or in the JAR that is attached to the WEB-INF/lib or as jboss module
- * if you want to share the implementation among more WARs). This file must have the fully qualified class name of all your ClientAuthenticatorFactory classes
+ * if you want to share the implementation among more WARs).
*
* NOTE: The SPI is not finished and method signatures are still subject to change in future versions (for example to support
* authentication with client certificate)
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java
index d68c7cb..1c8907e 100644
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/authentication/JWTClientCredentialsProvider.java
@@ -13,7 +13,7 @@ import org.keycloak.util.Time;
/**
* Client authentication based on JWT signed by client private key .
- * See <a href="https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-03">specs</a> for more details.
+ * See <a href="https://tools.ietf.org/html/rfc7519">specs</a> for more details.
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/RolePrincipal.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/RolePrincipal.java
index a4e4423..d578c81 100644
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/RolePrincipal.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/jaas/RolePrincipal.java
@@ -8,7 +8,6 @@ import java.security.Principal;
*/
public class RolePrincipal implements Principal, Serializable {
- private static final long serialVersionUID = -5538962177019315447L;
private String roleName = null;
public RolePrincipal(String roleName) {
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 1dfe41d..d077d7d 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
@@ -134,6 +134,9 @@ public class OAuthRequestAuthenticator {
String idpHint = getQueryParamValue(AdapterConstants.KC_IDP_HINT);
url = UriUtils.stripQueryParam(url, AdapterConstants.KC_IDP_HINT);
+ String scope = getQueryParamValue(OAuth2Constants.SCOPE);
+ url = UriUtils.stripQueryParam(url, OAuth2Constants.SCOPE);
+
KeycloakUriBuilder redirectUriBuilder = deployment.getAuthUrl().clone()
.queryParam(OAuth2Constants.RESPONSE_TYPE, OAuth2Constants.CODE)
.queryParam(OAuth2Constants.CLIENT_ID, deployment.getResourceName())
@@ -146,6 +149,9 @@ public class OAuthRequestAuthenticator {
if (idpHint != null && idpHint.length() > 0) {
redirectUriBuilder.queryParam(AdapterConstants.KC_IDP_HINT,idpHint);
}
+ if (scope != null && scope.length() > 0) {
+ redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, scope);
+ }
return redirectUriBuilder.build().toString();
}
diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
index 52668de..b938a0b 100755
--- a/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
+++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/RefreshableKeycloakSecurityContext.java
@@ -117,7 +117,12 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
}
this.token = token;
- this.refreshToken = response.getRefreshToken();
+ if (response.getRefreshToken() != null) {
+ if (log.isTraceEnabled()) {
+ log.trace("Setup new refresh token to the security context");
+ }
+ this.refreshToken = response.getRefreshToken();
+ }
this.tokenString = tokenString;
tokenStore.refreshCallback(this);
return true;
diff --git a/integration/js/src/main/resources/keycloak.js b/integration/js/src/main/resources/keycloak.js
index 46d3b18..f384e7b 100755
--- a/integration/js/src/main/resources/keycloak.js
+++ b/integration/js/src/main/resources/keycloak.js
@@ -164,6 +164,10 @@
url += '&kc_idp_hint=' + options.idpHint;
}
+ if (options && options.scope) {
+ url += '&scope=' + options.scope;
+ }
+
return url;
}
diff --git a/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java b/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java
index 53bb52c..109ffe1 100755
--- a/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java
+++ b/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimpleGroup.java
@@ -8,7 +8,6 @@ import java.util.HashSet;
import java.util.Set;
public class SimpleGroup extends SimplePrincipal implements Group {
- private static final long serialVersionUID = 3273437693505893786L;
private final Set<Principal> members = new HashSet<Principal>();
/**
diff --git a/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java b/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java
index ada37d7..29f46ec 100755
--- a/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java
+++ b/integration/tomcat/tomcat-adapter-spi/src/main/java/org/keycloak/adapters/tomcat/SimplePrincipal.java
@@ -14,7 +14,6 @@ import java.security.Principal;
public class SimplePrincipal implements Principal, Serializable {
/** SimplePrincipal.java */
- private static final long serialVersionUID = -5645357206342793145L;
/** The unique identifier for this principal. */
private final String name;
misc/ReleaseProcess.md 19(+4 -15)
diff --git a/misc/ReleaseProcess.md b/misc/ReleaseProcess.md
index 9a0e3d4..1fa620b 100644
--- a/misc/ReleaseProcess.md
+++ b/misc/ReleaseProcess.md
@@ -7,31 +7,21 @@
## Release
-*Releasing currently requires using JDK 7 due to a bug in JAX-RS Doclets*
-
### Clone from GitHub
# git clone https://github.com/keycloak/keycloak.git
# cd keycloak
-### Update version
-
- # mvn versions:set -DnewVersion=$VERSION -DgenerateBackupPoms=false -Pjboss-release
+### Prepare the release
-### Build
+ # mvn -Pjboss-release release:prepare
- # mvn install install -Pdistribution
- # mvn install -Pjboss-release -DskipTests
+### Perform the release
-### Tag
-
- # git tag $VERSION
- # git push --tags
+ # mvn -Pjboss-release release:perform
### Deploy to Nexus
- # mvn deploy -DskipTests -Pjboss-release
-
Then login to Nexus and release the maven uploads in the staging area. Artifacts will eventually be synced to Maven Central, but this can take up to 24 hours.
### Upload
@@ -51,7 +41,6 @@ Upload all artifacts to downloads.jboss.org (see https://mojo.redhat.com/docs/DO
# git tag $VERSION
# git push --tags
-
## After Release
### Update Bower
diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModel.java b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java
index 792822e..beb2f98 100755
--- a/model/api/src/main/java/org/keycloak/migration/MigrationModel.java
+++ b/model/api/src/main/java/org/keycloak/migration/MigrationModel.java
@@ -11,7 +11,7 @@ public interface MigrationModel {
/**
* Must have the form of major.minor.micro as the version is parsed and numbers are compared
*/
- public static final String LATEST_VERSION = "1.5.0";
+ public static final String LATEST_VERSION = "1.6.0";
String getStoredVersion();
void setStoredVersion(String version);
diff --git a/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
index 61b6244..e5c9484 100755
--- a/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
+++ b/model/api/src/main/java/org/keycloak/migration/MigrationModelManager.java
@@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
import org.keycloak.migration.migrators.MigrateTo1_3_0;
import org.keycloak.migration.migrators.MigrateTo1_4_0;
import org.keycloak.migration.migrators.MigrateTo1_5_0;
+import org.keycloak.migration.migrators.MigrateTo1_6_0;
import org.keycloak.migration.migrators.MigrationTo1_2_0_CR1;
import org.keycloak.models.KeycloakSession;
@@ -47,6 +48,12 @@ public class MigrationModelManager {
}
new MigrateTo1_5_0().migrate(session);
}
+ if (stored == null || stored.lessThan(MigrateTo1_6_0.VERSION)) {
+ if (stored != null) {
+ logger.debug("Migrating older model to 1.6.0 updates");
+ }
+ new MigrateTo1_6_0().migrate(session);
+ }
model.setStoredVersion(MigrationModel.LATEST_VERSION);
}
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
new file mode 100644
index 0000000..8010586
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrateTo1_6_0.java
@@ -0,0 +1,25 @@
+package org.keycloak.migration.migrators;
+
+import java.util.List;
+
+import org.keycloak.migration.ModelVersion;
+import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class MigrateTo1_6_0 {
+
+ public static final ModelVersion VERSION = new ModelVersion("1.6.0");
+
+ public void migrate(KeycloakSession session) {
+ List<RealmModel> realms = session.realms().getRealms();
+ for (RealmModel realm : realms) {
+ KeycloakModelUtils.setupOfflineTokens(realm);
+ }
+
+ }
+
+}
diff --git a/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java b/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java
index c9e10ad..1aa4929 100755
--- a/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java
+++ b/model/api/src/main/java/org/keycloak/migration/migrators/MigrationTo1_2_0_CR1.java
@@ -5,6 +5,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.util.List;
@@ -26,7 +27,9 @@ public class MigrationTo1_2_0_CR1 {
client.setFullScopeAllowed(false);
for (String role : Constants.BROKER_SERVICE_ROLES) {
- client.addRole(role).setDescription("${role_"+ role.toLowerCase().replaceAll("_", "-") +"}");
+ RoleModel roleModel = client.addRole(role);
+ roleModel.setDescription("${role_" + role.toLowerCase().replaceAll("_", "-") + "}");
+ roleModel.setScopeParamRequired(false);
}
}
}
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
index acb3975..04c24a9 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
@@ -8,7 +8,6 @@ import java.io.Serializable;
* @version $Revision: 1 $
*/
public class AuthenticationExecutionModel implements Serializable {
- private static final long serialVersionUID = 1L;
public static class ExecutionComparator implements Comparator<AuthenticationExecutionModel> {
public static final ExecutionComparator SINGLETON = new ExecutionComparator();
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
index f538d30..1db5970 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationFlowModel.java
@@ -7,7 +7,6 @@ import java.io.Serializable;
* @version $Revision: 1 $
*/
public class AuthenticationFlowModel implements Serializable {
- private static final long serialVersionUID = 1L;
private String id;
private String alias;
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
index 9e7f46d..22e3cfa 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticatorConfigModel.java
@@ -9,7 +9,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class AuthenticatorConfigModel implements Serializable {
- private static final long serialVersionUID = 1L;
private String id;
private String alias;
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index b1adbfa..5fe3189 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -1,5 +1,7 @@
package org.keycloak.models;
+import org.keycloak.OAuth2Constants;
+
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
@@ -16,4 +18,5 @@ public interface Constants {
String INSTALLED_APP_URL = "http://localhost";
String READ_TOKEN_ROLE = "read-token";
String[] BROKER_SERVICE_ROLES = {READ_TOKEN_ROLE};
+ String OFFLINE_ACCESS_ROLE = OAuth2Constants.OFFLINE_ACCESS;
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/OfflineClientSessionEntity.java b/model/api/src/main/java/org/keycloak/models/entities/OfflineClientSessionEntity.java
new file mode 100644
index 0000000..69ad60b
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/OfflineClientSessionEntity.java
@@ -0,0 +1,35 @@
+package org.keycloak.models.entities;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineClientSessionEntity {
+
+ private String clientSessionId;
+ private String clientId;
+ private String data;
+
+ public String getClientSessionId() {
+ return clientSessionId;
+ }
+
+ public void setClientSessionId(String clientSessionId) {
+ this.clientSessionId = clientSessionId;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/OfflineUserSessionEntity.java b/model/api/src/main/java/org/keycloak/models/entities/OfflineUserSessionEntity.java
new file mode 100644
index 0000000..e785898
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/entities/OfflineUserSessionEntity.java
@@ -0,0 +1,37 @@
+package org.keycloak.models.entities;
+
+import java.util.List;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineUserSessionEntity {
+
+ private String userSessionId;
+ private String data;
+ private List<OfflineClientSessionEntity> offlineClientSessions;
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+
+ public List<OfflineClientSessionEntity> getOfflineClientSessions() {
+ return offlineClientSessions;
+ }
+
+ public void setOfflineClientSessions(List<OfflineClientSessionEntity> offlineClientSessions) {
+ this.offlineClientSessions = offlineClientSessions;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/RoleEntity.java b/model/api/src/main/java/org/keycloak/models/entities/RoleEntity.java
index a610d39..4b4551e 100644
--- a/model/api/src/main/java/org/keycloak/models/entities/RoleEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/RoleEntity.java
@@ -9,6 +9,7 @@ public class RoleEntity extends AbstractIdentifiableEntity {
private String name;
private String description;
+ private boolean scopeParamRequired;
private List<String> compositeRoleIds;
@@ -31,6 +32,14 @@ public class RoleEntity extends AbstractIdentifiableEntity {
this.description = description;
}
+ public boolean isScopeParamRequired() {
+ return scopeParamRequired;
+ }
+
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ this.scopeParamRequired = scopeParamRequired;
+ }
+
public List<String> getCompositeRoleIds() {
return compositeRoleIds;
}
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
index 8c82a8e..66020db 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
@@ -28,6 +28,7 @@ public class UserEntity extends AbstractIdentifiableEntity {
private List<FederatedIdentityEntity> federatedIdentities;
private String federationLink;
private String serviceAccountClientLink;
+ private List<OfflineUserSessionEntity> offlineUserSessions;
public String getUsername() {
return username;
@@ -157,5 +158,13 @@ public class UserEntity extends AbstractIdentifiableEntity {
public void setServiceAccountClientLink(String serviceAccountClientLink) {
this.serviceAccountClientLink = serviceAccountClientLink;
}
+
+ public List<OfflineUserSessionEntity> getOfflineUserSessions() {
+ return offlineUserSessions;
+ }
+
+ public void setOfflineUserSessions(List<OfflineUserSessionEntity> offlineUserSessions) {
+ this.offlineUserSessions = offlineUserSessions;
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/IdentityProviderMapperModel.java b/model/api/src/main/java/org/keycloak/models/IdentityProviderMapperModel.java
index e3acf7f..781268e 100755
--- a/model/api/src/main/java/org/keycloak/models/IdentityProviderMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/IdentityProviderMapperModel.java
@@ -10,7 +10,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class IdentityProviderMapperModel implements Serializable {
- private static final long serialVersionUID = 1L;
protected String id;
protected String name;
diff --git a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
index 363ae52..fa3eb71 100755
--- a/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
+++ b/model/api/src/main/java/org/keycloak/models/IdentityProviderModel.java
@@ -30,7 +30,6 @@ import org.keycloak.representations.idm.IdentityProviderRepresentation;
* @author Pedro Igor
*/
public class IdentityProviderModel implements Serializable {
- private static final long serialVersionUID = 1L;
private String internalId;
diff --git a/model/api/src/main/java/org/keycloak/models/ImpersonationConstants.java b/model/api/src/main/java/org/keycloak/models/ImpersonationConstants.java
index 274e7a6..54f212a 100755
--- a/model/api/src/main/java/org/keycloak/models/ImpersonationConstants.java
+++ b/model/api/src/main/java/org/keycloak/models/ImpersonationConstants.java
@@ -26,6 +26,7 @@ public class ImpersonationConstants {
if (realmAdminApp.getRole(IMPERSONATION_ROLE) != null) return;
RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ROLE);
impersonationRole.setDescription("${role_" + IMPERSONATION_ROLE + "}");
+ impersonationRole.setScopeParamRequired(false);
adminRole.addCompositeRole(impersonationRole);
}
@@ -36,6 +37,7 @@ public class ImpersonationConstants {
if (realmAdminApp.getRole(IMPERSONATION_ROLE) != null) return;
RoleModel impersonationRole = realmAdminApp.addRole(IMPERSONATION_ROLE);
impersonationRole.setDescription("${role_" + IMPERSONATION_ROLE + "}");
+ impersonationRole.setScopeParamRequired(false);
RoleModel adminRole = realmAdminApp.getRole(AdminRoles.REALM_ADMIN);
adminRole.addCompositeRole(impersonationRole);
}
diff --git a/model/api/src/main/java/org/keycloak/models/OfflineClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/OfflineClientSessionModel.java
new file mode 100644
index 0000000..47ae23a
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/OfflineClientSessionModel.java
@@ -0,0 +1,44 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineClientSessionModel {
+
+ private String clientSessionId;
+ private String userSessionId;
+ private String clientId;
+ private String data;
+
+ public String getClientSessionId() {
+ return clientSessionId;
+ }
+
+ public void setClientSessionId(String clientSessionId) {
+ this.clientSessionId = clientSessionId;
+ }
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/OfflineUserSessionModel.java b/model/api/src/main/java/org/keycloak/models/OfflineUserSessionModel.java
new file mode 100644
index 0000000..9907783
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/OfflineUserSessionModel.java
@@ -0,0 +1,26 @@
+package org.keycloak.models;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineUserSessionModel {
+
+ private String userSessionId;
+ private String data;
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
index 5bdae2c..aff7d37 100755
--- a/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
+++ b/model/api/src/main/java/org/keycloak/models/PasswordPolicy.java
@@ -15,7 +15,6 @@ import java.util.regex.Pattern;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class PasswordPolicy implements Serializable {
- private static final long serialVersionUID = 1L;
public static final String INVALID_PASSWORD_MIN_LENGTH_MESSAGE = "invalidPasswordMinLengthMessage";
public static final String INVALID_PASSWORD_MIN_DIGITS_MESSAGE = "invalidPasswordMinDigitsMessage";
diff --git a/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java b/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
index 60d528b..06f264b 100755
--- a/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ProtocolMapperModel.java
@@ -10,7 +10,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class ProtocolMapperModel implements Serializable {
- private static final long serialVersionUID = 1L;
protected String id;
protected String name;
diff --git a/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java b/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
index 84cf92c..2aea1e2 100755
--- a/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RequiredCredentialModel.java
@@ -10,7 +10,6 @@ import java.util.Map;
* @version $Revision: 1 $
*/
public class RequiredCredentialModel implements Serializable {
- private static final long serialVersionUID = 1L;
protected String type;
protected boolean input;
diff --git a/model/api/src/main/java/org/keycloak/models/RoleModel.java b/model/api/src/main/java/org/keycloak/models/RoleModel.java
index c296795..9904a16 100755
--- a/model/api/src/main/java/org/keycloak/models/RoleModel.java
+++ b/model/api/src/main/java/org/keycloak/models/RoleModel.java
@@ -17,6 +17,10 @@ public interface RoleModel {
void setName(String name);
+ boolean isScopeParamRequired();
+
+ void setScopeParamRequired(boolean scopeParamRequired);
+
boolean isComposite();
void addCompositeRole(RoleModel role);
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
index ea08a58..e63ed2c 100644
--- a/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationMapperModel.java
@@ -7,7 +7,6 @@ import java.util.Map;
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class UserFederationMapperModel implements Serializable {
- private static final long serialVersionUID = 1L;
protected String id;
protected String name;
diff --git a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
index 4f5feda..9780766 100755
--- a/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserFederationProviderModel.java
@@ -11,7 +11,6 @@ import java.util.Map;
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
*/
public class UserFederationProviderModel implements Serializable {
- private static final long serialVersionUID = 1L;
private String id;
private String providerName;
diff --git a/model/api/src/main/java/org/keycloak/models/UserModel.java b/model/api/src/main/java/org/keycloak/models/UserModel.java
index 3282e61..51e8fc4 100755
--- a/model/api/src/main/java/org/keycloak/models/UserModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserModel.java
@@ -1,5 +1,6 @@
package org.keycloak.models;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -113,6 +114,15 @@ public interface UserModel {
void updateConsent(UserConsentModel consent);
boolean revokeConsentForClient(String clientInternalId);
+ void addOfflineUserSession(OfflineUserSessionModel offlineUserSession);
+ OfflineUserSessionModel getOfflineUserSession(String userSessionId);
+ Collection<OfflineUserSessionModel> getOfflineUserSessions();
+ boolean removeOfflineUserSession(String userSessionId);
+ void addOfflineClientSession(OfflineClientSessionModel offlineClientSession);
+ OfflineClientSessionModel getOfflineClientSession(String clientSessionId);
+ Collection<OfflineClientSessionModel> getOfflineClientSessions();
+ boolean removeOfflineClientSession(String clientSessionId);
+
public static enum RequiredAction {
VERIFY_EMAIL, UPDATE_PROFILE, CONFIGURE_TOTP, UPDATE_PASSWORD
}
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
index ff3f19a..12ebd70 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -40,6 +40,7 @@ public interface UserSessionModel {
public String getNote(String name);
public void setNote(String name, String value);
public void removeNote(String name);
+ public Map<String, String> getNotes();
State getState();
void setState(State state);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
index b64ebb0..846269e 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/KeycloakModelUtils.java
@@ -4,6 +4,7 @@ import org.bouncycastle.openssl.PEMWriter;
import org.keycloak.constants.KerberosConstants;
import org.keycloak.constants.ServiceAccountConstants;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
@@ -360,4 +361,13 @@ public final class KeycloakModelUtils {
public static String toLowerCaseSafe(String str) {
return str==null ? null : str.toLowerCase();
}
+
+ public static void setupOfflineTokens(RealmModel realm) {
+ if (realm.getRole(Constants.OFFLINE_ACCESS_ROLE) == null) {
+ RoleModel role = realm.addRole(Constants.OFFLINE_ACCESS_ROLE);
+ role.setDescription("${role_offline-access}");
+ role.setScopeParamRequired(true);
+ realm.addDefaultRole(Constants.OFFLINE_ACCESS_ROLE);
+ }
+ }
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index bf2360e..b71fce9 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -89,6 +89,7 @@ public class ModelToRepresentation {
rep.setId(role.getId());
rep.setName(role.getName());
rep.setDescription(role.getDescription());
+ rep.setScopeParamRequired(role.isScopeParamRequired());
rep.setComposite(role.isComposite());
return rep;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 811655b..77ae66e 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -181,6 +181,8 @@ public class RepresentationToModel {
// Application role may already exists (for example if it is defaultRole)
RoleModel role = roleRep.getId()!=null ? client.addRole(roleRep.getId(), roleRep.getName()) : client.addRole(roleRep.getName());
role.setDescription(roleRep.getDescription());
+ boolean scopeParamRequired = roleRep.isScopeParamRequired()==null ? false : roleRep.isScopeParamRequired();
+ role.setScopeParamRequired(scopeParamRequired);
}
}
}
@@ -633,6 +635,8 @@ public class RepresentationToModel {
public static void createRole(RealmModel newRealm, RoleRepresentation roleRep) {
RoleModel role = roleRep.getId()!=null ? newRealm.addRole(roleRep.getId(), roleRep.getName()) : newRealm.addRole(roleRep.getName());
if (roleRep.getDescription() != null) role.setDescription(roleRep.getDescription());
+ boolean scopeParamRequired = roleRep.isScopeParamRequired() == null ? false : roleRep.isScopeParamRequired();
+ role.setScopeParamRequired(scopeParamRequired);
}
private static void addComposites(RoleModel role, RoleRepresentation roleRep, RealmModel realm) {
diff --git a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
index 4cd162b..f727c75 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
@@ -1,12 +1,15 @@
package org.keycloak.models.utils;
import org.keycloak.models.ClientModel;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
+import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -255,4 +258,44 @@ public class UserModelDelegate implements UserModel {
public void setCreatedTimestamp(Long timestamp){
delegate.setCreatedTimestamp(timestamp);
}
+
+ @Override
+ public void addOfflineUserSession(OfflineUserSessionModel userSession) {
+ delegate.addOfflineUserSession(userSession);
+ }
+
+ @Override
+ public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
+ return delegate.getOfflineUserSession(userSessionId);
+ }
+
+ @Override
+ public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
+ return delegate.getOfflineUserSessions();
+ }
+
+ @Override
+ public boolean removeOfflineUserSession(String userSessionId) {
+ return delegate.removeOfflineUserSession(userSessionId);
+ }
+
+ @Override
+ public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
+ delegate.addOfflineClientSession(clientSession);
+ }
+
+ @Override
+ public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
+ return delegate.getOfflineClientSession(clientSessionId);
+ }
+
+ @Override
+ public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
+ return delegate.getOfflineClientSessions();
+ }
+
+ @Override
+ public boolean removeOfflineClientSession(String clientSessionId) {
+ return delegate.removeOfflineClientSession(clientSessionId);
+ }
}
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java
index 9448def..7706459 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RoleAdapter.java
@@ -89,6 +89,16 @@ public class RoleAdapter implements RoleModel {
}
@Override
+ public boolean isScopeParamRequired() {
+ return role.isScopeParamRequired();
+ }
+
+ @Override
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ role.setScopeParamRequired(scopeParamRequired);
+ }
+
+ @Override
public boolean isComposite() {
return role.getCompositeRoleIds() != null && role.getCompositeRoleIds().size() > 0;
}
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
index 7461cbd..d89ce63 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
@@ -22,7 +22,10 @@ import org.keycloak.models.ClientModel;
import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
import org.keycloak.models.ModelDuplicateException;
+import org.keycloak.models.ModelException;
import org.keycloak.models.OTPPolicy;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
@@ -32,6 +35,8 @@ import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.CredentialEntity;
import org.keycloak.models.entities.FederatedIdentityEntity;
+import org.keycloak.models.entities.OfflineClientSessionEntity;
+import org.keycloak.models.entities.OfflineUserSessionEntity;
import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.entities.UserEntity;
import org.keycloak.models.utils.KeycloakModelUtils;
@@ -39,6 +44,7 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.util.Time;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -216,7 +222,7 @@ public class UserAdapter implements UserModel, Comparable {
@Override
public Map<String, List<String>> getAttributes() {
- return user.getAttributes()==null ? Collections.<String, List<String>>emptyMap() : Collections.unmodifiableMap((Map)user.getAttributes());
+ return user.getAttributes()==null ? Collections.<String, List<String>>emptyMap() : Collections.unmodifiableMap((Map) user.getAttributes());
}
@Override
@@ -569,6 +575,142 @@ public class UserAdapter implements UserModel, Comparable {
}
@Override
+ public void addOfflineUserSession(OfflineUserSessionModel userSession) {
+ if (user.getOfflineUserSessions() == null) {
+ user.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
+ }
+
+ if (getUserSessionEntityById(userSession.getUserSessionId()) != null) {
+ throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + user.getUsername());
+ }
+
+ OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
+ entity.setUserSessionId(userSession.getUserSessionId());
+ entity.setData(userSession.getData());
+ entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
+ user.getOfflineUserSessions().add(entity);
+ }
+
+ @Override
+ public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
+ OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
+ return entity==null ? null : toModel(entity);
+ }
+
+ @Override
+ public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
+ if (user.getOfflineUserSessions()==null) {
+ return Collections.emptyList();
+ } else {
+ List<OfflineUserSessionModel> result = new ArrayList<>();
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ result.add(toModel(entity));
+ }
+ return result;
+ }
+ }
+
+ private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
+ OfflineUserSessionModel model = new OfflineUserSessionModel();
+ model.setUserSessionId(entity.getUserSessionId());
+ model.setData(entity.getData());
+ return model;
+ }
+
+ @Override
+ public boolean removeOfflineUserSession(String userSessionId) {
+ OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
+ if (entity != null) {
+ user.getOfflineUserSessions().remove(entity);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private OfflineUserSessionEntity getUserSessionEntityById(String userSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ if (entity.getUserSessionId().equals(userSessionId)) {
+ return entity;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
+ OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(clientSession.getUserSessionId());
+ if (userSessionEntity == null) {
+ throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + user.getUsername());
+ }
+
+ OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
+ clEntity.setClientSessionId(clientSession.getClientSessionId());
+ clEntity.setClientId(clientSession.getClientId());
+ clEntity.setData(clientSession.getData());
+
+ userSessionEntity.getOfflineClientSessions().add(clEntity);
+ }
+
+ @Override
+ public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ if (clSession.getClientSessionId().equals(clientSessionId)) {
+ return toModel(clSession, userSession.getUserSessionId());
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
+ OfflineClientSessionModel model = new OfflineClientSessionModel();
+ model.setClientSessionId(cls.getClientSessionId());
+ model.setClientId(cls.getClientId());
+ model.setData(cls.getData());
+ model.setUserSessionId(userSessionId);
+ return model;
+ }
+
+ @Override
+ public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
+ List<OfflineClientSessionModel> result = new ArrayList<>();
+
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ result.add(toModel(clSession, userSession.getUserSessionId()));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean removeOfflineClientSession(String clientSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ if (clSession.getClientSessionId().equals(clientSessionId)) {
+ userSession.getOfflineClientSessions().remove(clSession);
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserModel)) return false;
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
index 68b31ed..df7c144 100644
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/DefaultCacheUserProvider.java
@@ -319,6 +319,7 @@ public class DefaultCacheUserProvider implements CacheUserProvider {
@Override
public void preRemove(RealmModel realm, ClientModel client) {
+ realmInvalidations.add(realm.getId()); // easier to just invalidate whole realm
getDelegate().preRemove(realm, client);
}
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
index 536c2ef..861c252 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/RoleAdapter.java
@@ -60,6 +60,18 @@ public class RoleAdapter implements RoleModel {
}
@Override
+ public boolean isScopeParamRequired() {
+ if (updated != null) return updated.isScopeParamRequired();
+ return cached.isScopeParamRequired();
+ }
+
+ @Override
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ getDelegateForUpdate();
+ updated.setScopeParamRequired(scopeParamRequired);
+ }
+
+ @Override
public String getId() {
if (updated != null) return updated.getId();
return cached.getId();
diff --git a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
index 5a74b01..769f1b4 100755
--- a/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
+++ b/model/invalidation-cache/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
@@ -348,4 +348,52 @@ public class UserAdapter implements UserModel {
getDelegateForUpdate();
return updated.revokeConsentForClient(clientId);
}
+
+ @Override
+ public void addOfflineUserSession(OfflineUserSessionModel userSession) {
+ getDelegateForUpdate();
+ updated.addOfflineUserSession(userSession);
+ }
+
+ @Override
+ public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
+ if (updated != null) return updated.getOfflineUserSession(userSessionId);
+ return cached.getOfflineUserSessions().get(userSessionId);
+ }
+
+ @Override
+ public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
+ if (updated != null) return updated.getOfflineUserSessions();
+ return cached.getOfflineUserSessions().values();
+ }
+
+ @Override
+ public boolean removeOfflineUserSession(String userSessionId) {
+ getDelegateForUpdate();
+ return updated.removeOfflineUserSession(userSessionId);
+ }
+
+ @Override
+ public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
+ getDelegateForUpdate();
+ updated.addOfflineClientSession(clientSession);
+ }
+
+ @Override
+ public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
+ if (updated != null) return updated.getOfflineClientSession(clientSessionId);
+ return cached.getOfflineClientSessions().get(clientSessionId);
+ }
+
+ @Override
+ public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
+ if (updated != null) return updated.getOfflineClientSessions();
+ return cached.getOfflineClientSessions().values();
+ }
+
+ @Override
+ public boolean removeOfflineClientSession(String clientSessionId) {
+ getDelegateForUpdate();
+ return updated.removeOfflineClientSession(clientSessionId);
+ }
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
index 25749c3..11447d0 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClient.java
@@ -21,7 +21,6 @@ import java.util.TreeMap;
* @version $Revision: 1 $
*/
public class CachedClient implements Serializable {
- private static final long serialVersionUID = 1L;
private String id;
private String clientId;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java
index da97671..cc1a207 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedClientRole.java
@@ -9,8 +9,6 @@ import org.keycloak.models.RoleModel;
*/
public class CachedClientRole extends CachedRole {
- private static final long serialVersionUID = 1L;
-
private final String idClient;
public CachedClientRole(String idClient, RoleModel model, RealmModel realm) {
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index 3d96870..27bab74 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -33,7 +33,6 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class CachedRealm implements Serializable {
- private static final long serialVersionUID = 1L;
private String id;
private String name;
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
index 3fb7332..1bbaa5c 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRole.java
@@ -13,12 +13,11 @@ import java.util.Set;
*/
public class CachedRole implements Serializable {
- private static final long serialVersionUID = 1L;
-
final protected String id;
final protected String name;
final protected String realm;
final protected String description;
+ final protected Boolean scopeParamRequired;
final protected boolean composite;
final protected Set<String> composites = new HashSet<String>();
@@ -27,6 +26,7 @@ public class CachedRole implements Serializable {
description = model.getDescription();
id = model.getId();
name = model.getName();
+ scopeParamRequired = model.isScopeParamRequired();
this.realm = realm.getId();
if (composite) {
for (RoleModel child : model.getComposites()) {
@@ -52,6 +52,10 @@ public class CachedRole implements Serializable {
return description;
}
+ public Boolean isScopeParamRequired() {
+ return scopeParamRequired;
+ }
+
public boolean isComposite() {
return composite;
}
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
index 9757c63..d38b6f9 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
@@ -1,5 +1,7 @@
package org.keycloak.models.cache.entities;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialValueModel;
@@ -7,9 +9,11 @@ import org.keycloak.models.UserModel;
import org.keycloak.util.MultivaluedHashMap;
import java.io.Serializable;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -33,6 +37,8 @@ public class CachedUser implements Serializable {
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
private Set<String> requiredActions = new HashSet<>();
private Set<String> roleMappings = new HashSet<String>();
+ private Map<String, OfflineUserSessionModel> offlineUserSessions = new HashMap<>();
+ private Map<String, OfflineClientSessionModel> offlineClientSessions = new HashMap<>();
public CachedUser(RealmModel realm, UserModel user) {
this.id = user.getId();
@@ -53,6 +59,12 @@ public class CachedUser implements Serializable {
for (RoleModel role : user.getRoleMappings()) {
roleMappings.add(role.getId());
}
+ for (OfflineUserSessionModel offlineSession : user.getOfflineUserSessions()) {
+ offlineUserSessions.put(offlineSession.getUserSessionId(), offlineSession);
+ }
+ for (OfflineClientSessionModel offlineSession : user.getOfflineClientSessions()) {
+ offlineClientSessions.put(offlineSession.getClientSessionId(), offlineSession);
+ }
}
public String getId() {
@@ -118,4 +130,12 @@ public class CachedUser implements Serializable {
public String getServiceAccountClientLink() {
return serviceAccountClientLink;
}
+
+ public Map<String, OfflineUserSessionModel> getOfflineUserSessions() {
+ return offlineUserSessions;
+ }
+
+ public Map<String, OfflineClientSessionModel> getOfflineClientSessions() {
+ return offlineClientSessions;
+ }
}
model/jpa/pom.xml 8(+1 -7)
diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml
index f4f9eec..4013fca 100755
--- a/model/jpa/pom.xml
+++ b/model/jpa/pom.xml
@@ -17,17 +17,14 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>net.iharder</groupId>
<artifactId>base64</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
@@ -44,18 +41,15 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
- <scope>provided</scope>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
- <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineClientSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineClientSessionEntity.java
new file mode 100644
index 0000000..23081c4
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineClientSessionEntity.java
@@ -0,0 +1,81 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NamedQueries({
+ @NamedQuery(name="deleteOfflineClientSessionsByRealm", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId)"),
+ @NamedQuery(name="deleteOfflineClientSessionsByRealmAndLink", query="delete from OfflineClientSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
+ @NamedQuery(name="deleteOfflineClientSessionsByClient", query="delete from OfflineClientSessionEntity sess where sess.clientId=:clientId")
+})
+@Table(name="OFFLINE_CLIENT_SESSION")
+@Entity
+public class OfflineClientSessionEntity {
+
+ @Id
+ @Column(name="CLIENT_SESSION_ID", length = 36)
+ protected String clientSessionId;
+
+ @Column(name="USER_SESSION_ID", length = 36)
+ protected String userSessionId;
+
+ @Column(name="CLIENT_ID", length = 36)
+ protected String clientId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
+ protected UserEntity user;
+
+ @Column(name="DATA")
+ protected String data;
+
+ public String getClientSessionId() {
+ return clientSessionId;
+ }
+
+ public void setClientSessionId(String clientSessionId) {
+ this.clientSessionId = clientSessionId;
+ }
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public String getClientId() {
+ return clientId;
+ }
+
+ public void setClientId(String clientId) {
+ this.clientId = clientId;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineUserSessionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineUserSessionEntity.java
new file mode 100644
index 0000000..d2726fa
--- /dev/null
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/OfflineUserSessionEntity.java
@@ -0,0 +1,59 @@
+package org.keycloak.models.jpa.entities;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@NamedQueries({
+ @NamedQuery(name="deleteOfflineUserSessionsByRealm", query="delete from OfflineUserSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId)"),
+ @NamedQuery(name="deleteOfflineUserSessionsByRealmAndLink", query="delete from OfflineUserSessionEntity sess where sess.user IN (select u from UserEntity u where u.realmId=:realmId and u.federationLink=:link)"),
+ @NamedQuery(name="deleteDetachedOfflineUserSessions", query="delete from OfflineUserSessionEntity sess where sess.userSessionId NOT IN (select c.userSessionId from OfflineClientSessionEntity c)")
+})
+@Table(name="OFFLINE_USER_SESSION")
+@Entity
+public class OfflineUserSessionEntity {
+
+ @Id
+ @Column(name="USER_SESSION_ID", length = 36)
+ protected String userSessionId;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name="USER_ID")
+ protected UserEntity user;
+
+ @Column(name="DATA")
+ protected String data;
+
+ public String getUserSessionId() {
+ return userSessionId;
+ }
+
+ public void setUserSessionId(String userSessionId) {
+ this.userSessionId = userSessionId;
+ }
+
+ public UserEntity getUser() {
+ return user;
+ }
+
+ public void setUser(UserEntity user) {
+ this.user = user;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(String data) {
+ this.data = data;
+ }
+}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
index 4c9edb7..437867e 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/RoleEntity.java
@@ -37,6 +37,8 @@ public class RoleEntity {
private String name;
@Column(name = "DESCRIPTION")
private String description;
+ @Column(name = "SCOPE_PARAM_REQUIRED")
+ private boolean scopeParamRequired;
// hax! couldn't get constraint to work properly
@Column(name = "REALM_ID")
@@ -93,6 +95,14 @@ public class RoleEntity {
this.description = description;
}
+ public boolean isScopeParamRequired() {
+ return scopeParamRequired;
+ }
+
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ this.scopeParamRequired = scopeParamRequired;
+ }
+
public Collection<RoleEntity> getCompositeRoles() {
return compositeRoles;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
index 2da1641..24d23db 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserEntity.java
@@ -3,9 +3,13 @@ package org.keycloak.models.jpa.entities;
import org.keycloak.models.utils.KeycloakModelUtils;
import javax.persistence.CascadeType;
+import javax.persistence.CollectionTable;
import javax.persistence.Column;
+import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
@@ -14,6 +18,8 @@ import javax.persistence.UniqueConstraint;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -83,6 +89,12 @@ public class UserEntity {
@Column(name="SERVICE_ACCOUNT_CLIENT_LINK")
protected String serviceAccountClientLink;
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
+ protected Collection<OfflineUserSessionEntity> offlineUserSessions = new ArrayList<>();
+
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="user")
+ protected Collection<OfflineClientSessionEntity> offlineClientSessions = new ArrayList<>();
+
public String getId() {
return id;
}
@@ -212,6 +224,22 @@ public class UserEntity {
this.serviceAccountClientLink = serviceAccountClientLink;
}
+ public Collection<OfflineUserSessionEntity> getOfflineUserSessions() {
+ return offlineUserSessions;
+ }
+
+ public void setOfflineUserSessions(Collection<OfflineUserSessionEntity> offlineUserSessions) {
+ this.offlineUserSessions = offlineUserSessions;
+ }
+
+ public Collection<OfflineClientSessionEntity> getOfflineClientSessions() {
+ return offlineClientSessions;
+ }
+
+ public void setOfflineClientSessions(Collection<OfflineClientSessionEntity> offlineClientSessions) {
+ this.offlineClientSessions = offlineClientSessions;
+ }
+
@Override
public boolean equals(Object o) {
if (this == o) return true;
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index d92e93d..8cee4ea 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -169,6 +169,10 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUserAttributesByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteOfflineClientSessionsByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
+ num = em.createNamedQuery("deleteOfflineUserSessionsByRealm")
+ .setParameter("realmId", realm.getId()).executeUpdate();
num = em.createNamedQuery("deleteUsersByRealm")
.setParameter("realmId", realm.getId()).executeUpdate();
}
@@ -195,6 +199,14 @@ public class JpaUserProvider implements UserProvider {
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
.executeUpdate();
+ num = em.createNamedQuery("deleteOfflineClientSessionsByRealmAndLink")
+ .setParameter("realmId", realm.getId())
+ .setParameter("link", link.getId())
+ .executeUpdate();
+ num = em.createNamedQuery("deleteOfflineUserSessionsByRealmAndLink")
+ .setParameter("realmId", realm.getId())
+ .setParameter("link", link.getId())
+ .executeUpdate();
num = em.createNamedQuery("deleteUsersByRealmAndLink")
.setParameter("realmId", realm.getId())
.setParameter("link", link.getId())
@@ -212,6 +224,8 @@ public class JpaUserProvider implements UserProvider {
em.createNamedQuery("deleteUserConsentProtMappersByClient").setParameter("clientId", client.getId()).executeUpdate();
em.createNamedQuery("deleteUserConsentRolesByClient").setParameter("clientId", client.getId()).executeUpdate();
em.createNamedQuery("deleteUserConsentsByClient").setParameter("clientId", client.getId()).executeUpdate();
+ em.createNamedQuery("deleteOfflineClientSessionsByClient").setParameter("clientId", client.getId()).executeUpdate();
+ em.createNamedQuery("deleteDetachedOfflineUserSessions").executeUpdate();
}
@Override
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
index 4ab8d33..0e49d59 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RoleAdapter.java
@@ -50,6 +50,16 @@ public class RoleAdapter implements RoleModel {
}
@Override
+ public boolean isScopeParamRequired() {
+ return role.isScopeParamRequired();
+ }
+
+ @Override
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ role.setScopeParamRequired(scopeParamRequired);
+ }
+
+ @Override
public String getId() {
return role.getId();
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 67def6b..bdcf1f1 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -2,6 +2,8 @@ package org.keycloak.models.jpa;
import org.keycloak.models.ClientModel;
import org.keycloak.models.OTPPolicy;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.ModelDuplicateException;
@@ -14,6 +16,8 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.jpa.entities.CredentialEntity;
+import org.keycloak.models.jpa.entities.OfflineClientSessionEntity;
+import org.keycloak.models.jpa.entities.OfflineUserSessionEntity;
import org.keycloak.models.jpa.entities.UserConsentEntity;
import org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity;
import org.keycloak.models.jpa.entities.UserConsentRoleEntity;
@@ -37,6 +41,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -751,6 +756,124 @@ public class UserAdapter implements UserModel {
}
@Override
+ public void addOfflineUserSession(OfflineUserSessionModel offlineSession) {
+ OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
+ entity.setUser(user);
+ entity.setUserSessionId(offlineSession.getUserSessionId());
+ entity.setData(offlineSession.getData());
+ em.persist(entity);
+ user.getOfflineUserSessions().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ if (entity.getUserSessionId().equals(userSessionId)) {
+ return toModel(entity);
+ }
+ }
+ return null;
+ }
+
+ private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
+ OfflineUserSessionModel model = new OfflineUserSessionModel();
+ model.setUserSessionId(entity.getUserSessionId());
+ model.setData(entity.getData());
+ return model;
+ }
+
+ @Override
+ public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
+ List<OfflineUserSessionModel> result = new LinkedList<>();
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ result.add(toModel(entity));
+ }
+ return result;
+ }
+
+ @Override
+ public boolean removeOfflineUserSession(String userSessionId) {
+ OfflineUserSessionEntity found = null;
+ for (OfflineUserSessionEntity session : user.getOfflineUserSessions()) {
+ if (session.getUserSessionId().equals(userSessionId)) {
+ found = session;
+ break;
+ }
+ }
+
+ if (found == null) {
+ return false;
+ } else {
+ user.getOfflineUserSessions().remove(found);
+ em.remove(found);
+ em.flush();
+ return true;
+ }
+ }
+
+ @Override
+ public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
+ OfflineClientSessionEntity entity = new OfflineClientSessionEntity();
+ entity.setUser(user);
+ entity.setClientSessionId(clientSession.getClientSessionId());
+ entity.setUserSessionId(clientSession.getUserSessionId());
+ entity.setClientId(clientSession.getClientId());
+ entity.setData(clientSession.getData());
+ em.persist(entity);
+ user.getOfflineClientSessions().add(entity);
+ em.flush();
+ }
+
+ @Override
+ public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
+ for (OfflineClientSessionEntity entity : user.getOfflineClientSessions()) {
+ if (entity.getClientSessionId().equals(clientSessionId)) {
+ return toModel(entity);
+ }
+ }
+ return null;
+ }
+
+ private OfflineClientSessionModel toModel(OfflineClientSessionEntity entity) {
+ OfflineClientSessionModel model = new OfflineClientSessionModel();
+ model.setClientSessionId(entity.getClientSessionId());
+ model.setClientId(entity.getClientId());
+ model.setUserSessionId(entity.getUserSessionId());
+ model.setData(entity.getData());
+ return model;
+ }
+
+ @Override
+ public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
+ List<OfflineClientSessionModel> result = new LinkedList<>();
+ for (OfflineClientSessionEntity entity : user.getOfflineClientSessions()) {
+ result.add(toModel(entity));
+ }
+ return result;
+ }
+
+ @Override
+ public boolean removeOfflineClientSession(String clientSessionId) {
+ OfflineClientSessionEntity found = null;
+ for (OfflineClientSessionEntity session : user.getOfflineClientSessions()) {
+ if (session.getClientSessionId().equals(clientSessionId)) {
+ found = session;
+ break;
+ }
+ }
+
+ if (found == null) {
+ return false;
+ } else {
+ user.getOfflineClientSessions().remove(found);
+ em.remove(found);
+ em.flush();
+ return true;
+ }
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserModel)) return false;
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 308d9fb..14081c1 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -19,6 +19,8 @@ import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserProvider;
import org.keycloak.models.entities.FederatedIdentityEntity;
+import org.keycloak.models.entities.OfflineClientSessionEntity;
+import org.keycloak.models.entities.OfflineUserSessionEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.utils.CredentialValidation;
@@ -399,6 +401,36 @@ public class MongoUserProvider implements UserProvider {
.and("clientId").is(client.getId())
.get();
getMongoStore().removeEntities(MongoUserConsentEntity.class, query, false, invocationContext);
+
+ // Remove all offlineClientSessions
+ query = new QueryBuilder()
+ .and("offlineUserSessions.offlineClientSessions.clientId").is(client.getId())
+ .get();
+ List<MongoUserEntity> users = getMongoStore().loadEntities(MongoUserEntity.class, query, invocationContext);
+ for (MongoUserEntity user : users) {
+ boolean anyRemoved = false;
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clientSession : userSession.getOfflineClientSessions()) {
+ if (clientSession.getClientId().equals(client.getId())) {
+ userSession.getOfflineClientSessions().remove(clientSession);
+ anyRemoved = true;
+ break;
+ }
+ }
+
+ // Check if it was last clientSession. Then remove userSession too
+ if (userSession.getOfflineClientSessions().size() == 0) {
+ user.getOfflineUserSessions().remove(userSession);
+ anyRemoved = true;
+ break;
+ }
+ }
+
+ if (anyRemoved) {
+ getMongoStore().updateEntity(user, invocationContext);
+ }
+
+ }
}
@Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
index 91ef361..f3d918b 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RoleAdapter.java
@@ -69,6 +69,17 @@ public class RoleAdapter extends AbstractMongoAdapter<MongoRoleEntity> implement
}
@Override
+ public boolean isScopeParamRequired() {
+ return role.isScopeParamRequired();
+ }
+
+ @Override
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ role.setScopeParamRequired(scopeParamRequired);
+ updateRole();
+ }
+
+ @Override
public boolean isComposite() {
return role.getCompositeRoleIds() != null && role.getCompositeRoleIds().size() > 0;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 8130ff5..a475bb6 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -8,6 +8,8 @@ import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientModel;
import org.keycloak.models.OTPPolicy;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.UserConsentModel;
import org.keycloak.models.KeycloakSession;
@@ -20,6 +22,8 @@ import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserCredentialValueModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.entities.CredentialEntity;
+import org.keycloak.models.entities.OfflineClientSessionEntity;
+import org.keycloak.models.entities.OfflineUserSessionEntity;
import org.keycloak.models.entities.UserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserConsentEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
@@ -30,6 +34,7 @@ import org.keycloak.models.utils.Pbkdf2PasswordEncoder;
import org.keycloak.util.Time;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@@ -628,6 +633,145 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
}
@Override
+ public void addOfflineUserSession(OfflineUserSessionModel userSession) {
+ if (user.getOfflineUserSessions() == null) {
+ user.setOfflineUserSessions(new ArrayList<OfflineUserSessionEntity>());
+ }
+
+ if (getUserSessionEntityById(userSession.getUserSessionId()) != null) {
+ throw new ModelDuplicateException("User session already exists with id " + userSession.getUserSessionId() + " for user " + getMongoEntity().getUsername());
+ }
+
+ OfflineUserSessionEntity entity = new OfflineUserSessionEntity();
+ entity.setUserSessionId(userSession.getUserSessionId());
+ entity.setData(userSession.getData());
+ entity.setOfflineClientSessions(new ArrayList<OfflineClientSessionEntity>());
+ user.getOfflineUserSessions().add(entity);
+ updateUser();
+ }
+
+ @Override
+ public OfflineUserSessionModel getOfflineUserSession(String userSessionId) {
+ OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
+ return entity==null ? null : toModel(entity);
+ }
+
+ @Override
+ public Collection<OfflineUserSessionModel> getOfflineUserSessions() {
+ if (user.getOfflineUserSessions()==null) {
+ return Collections.emptyList();
+ } else {
+ List<OfflineUserSessionModel> result = new ArrayList<>();
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ result.add(toModel(entity));
+ }
+ return result;
+ }
+ }
+
+ private OfflineUserSessionModel toModel(OfflineUserSessionEntity entity) {
+ OfflineUserSessionModel model = new OfflineUserSessionModel();
+ model.setUserSessionId(entity.getUserSessionId());
+ model.setData(entity.getData());
+ return model;
+ }
+
+ @Override
+ public boolean removeOfflineUserSession(String userSessionId) {
+ OfflineUserSessionEntity entity = getUserSessionEntityById(userSessionId);
+ if (entity != null) {
+ user.getOfflineUserSessions().remove(entity);
+ updateUser();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ private OfflineUserSessionEntity getUserSessionEntityById(String userSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity entity : user.getOfflineUserSessions()) {
+ if (entity.getUserSessionId().equals(userSessionId)) {
+ return entity;
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public void addOfflineClientSession(OfflineClientSessionModel clientSession) {
+ OfflineUserSessionEntity userSessionEntity = getUserSessionEntityById(clientSession.getUserSessionId());
+ if (userSessionEntity == null) {
+ throw new ModelException("OfflineUserSession with ID " + clientSession.getUserSessionId() + " doesn't exist for user " + getMongoEntity().getUsername());
+ }
+
+ OfflineClientSessionEntity clEntity = new OfflineClientSessionEntity();
+ clEntity.setClientSessionId(clientSession.getClientSessionId());
+ clEntity.setClientId(clientSession.getClientId());
+ clEntity.setData(clientSession.getData());
+
+ userSessionEntity.getOfflineClientSessions().add(clEntity);
+ updateUser();
+ }
+
+ @Override
+ public OfflineClientSessionModel getOfflineClientSession(String clientSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ if (clSession.getClientSessionId().equals(clientSessionId)) {
+ return toModel(clSession, userSession.getUserSessionId());
+ }
+ }
+ }
+ }
+
+ return null;
+ }
+
+ private OfflineClientSessionModel toModel(OfflineClientSessionEntity cls, String userSessionId) {
+ OfflineClientSessionModel model = new OfflineClientSessionModel();
+ model.setClientSessionId(cls.getClientSessionId());
+ model.setClientId(cls.getClientId());
+ model.setData(cls.getData());
+ model.setUserSessionId(userSessionId);
+ return model;
+ }
+
+ @Override
+ public Collection<OfflineClientSessionModel> getOfflineClientSessions() {
+ List<OfflineClientSessionModel> result = new ArrayList<>();
+
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ result.add(toModel(clSession, userSession.getUserSessionId()));
+ }
+ }
+ }
+
+ return result;
+ }
+
+ @Override
+ public boolean removeOfflineClientSession(String clientSessionId) {
+ if (user.getOfflineUserSessions() != null) {
+ for (OfflineUserSessionEntity userSession : user.getOfflineUserSessions()) {
+ for (OfflineClientSessionEntity clSession : userSession.getOfflineClientSessions()) {
+ if (clSession.getClientSessionId().equals(clientSessionId)) {
+ userSession.getOfflineClientSessions().remove(clSession);
+ updateUser();
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+ }
+
+ @Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof UserModel)) return false;
model/sessions-infinispan/pom.xml 4(+0 -4)
diff --git a/model/sessions-infinispan/pom.xml b/model/sessions-infinispan/pom.xml
index 3d8c256..ee64a6b 100755
--- a/model/sessions-infinispan/pom.xml
+++ b/model/sessions-infinispan/pom.xml
@@ -17,22 +17,18 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-infinispan</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
- <scope>provided</scope>
</dependency>
</dependencies>
</project>
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/UserSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/UserSessionAdapter.java
index fbe8682..a9db618 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/UserSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/compat/UserSessionAdapter.java
@@ -10,6 +10,7 @@ import org.keycloak.models.sessions.infinispan.compat.entities.UserSessionEntity
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -144,5 +145,8 @@ public class UserSessionAdapter implements UserSessionModel {
}
-
+ @Override
+ public Map<String, String> getNotes() {
+ return entity.getNotes();
+ }
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
index 6cfc121..c7104fb 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/UserSessionAdapter.java
@@ -14,6 +14,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -110,6 +111,11 @@ public class UserSessionAdapter implements UserSessionModel {
}
@Override
+ public Map<String, String> getNotes() {
+ return entity.getNotes();
+ }
+
+ @Override
public State getState() {
return entity.getState();
}
pom.xml 112(+53 -59)
diff --git a/pom.xml b/pom.xml
index c0a1cc1..bfc0c06 100755
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,7 @@
<io.netty.version>4.0.26.Final</io.netty.version>
<xnio.netty.netty-xnio-transport.version>0.1.1.Final</xnio.netty.netty-xnio-transport.version>
<hibernate.javax.persistence.version>1.0.0.Final</hibernate.javax.persistence.version>
+ <hibernate.javax.persistence.artifactId>hibernate-jpa-2.1-api</hibernate.javax.persistence.artifactId>
<hibernate.entitymanager.version>4.3.10.Final</hibernate.entitymanager.version>
<h2.version>1.4.187</h2.version>
<mysql.version>5.1.29</mysql.version>
@@ -322,7 +323,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<version>${hibernate.javax.persistence.version}</version>
</dependency>
<dependency>
@@ -1255,6 +1256,15 @@
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-release-plugin</artifactId>
+ <version>2.5.2</version>
+ <configuration>
+ <autoVersionSubmodules>true</autoVersionSubmodules>
+ <tagNameFormat>@{project.version}</tagNameFormat>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<forkMode>once</forkMode>
@@ -1349,6 +1359,48 @@
</build>
<profiles>
+ <profile>
+ <id>distribution</id>
+ <modules>
+ <module>distribution</module>
+ </modules>
+ </profile>
+
+ <profile>
+ <id>jboss-release</id>
+ <modules>
+ <module>docbook</module>
+ <module>distribution</module>
+ </modules>
+ </profile>
+
+ <profile>
+ <id>doclint-java8-disable</id>
+ <activation>
+ <jdk>[1.8,)</jdk>
+ </activation>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-javadoc-plugin</artifactId>
+ <configuration>
+ <additionalparam>-Xdoclint:none</additionalparam>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>arquillian-integration-tests</id>
+ <modules>
+ <module>distribution</module>
+ <module>testsuite/integration-arquillian</module>
+ </modules>
+ </profile>
+
<!-- Configure the JBoss Early Access Maven repository -->
<profile>
<id>jboss-earlyaccess-repository</id>
@@ -1382,63 +1434,5 @@
</pluginRepository>
</pluginRepositories>
</profile>
- <profile>
- <id>distribution</id>
- <modules>
- <module>distribution</module>
- <module>testsuite/integration-arquillian</module>
- </modules>
- </profile>
- <profile>
- <id>jboss-release</id>
- <modules>
- <module>docbook</module>
- <module>distribution</module>
- <module>testsuite/integration-arquillian</module>
- </modules>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <executions>
- <execution>
- <id>aggregate</id>
- <phase>package</phase>
- <goals>
- <goal>aggregate</goal>
- </goals>
- <configuration>
- <minmemory>128m</minmemory>
- <maxmemory>1024m</maxmemory>
- <aggregate>true</aggregate>
- <excludePackageNames>
- se.unlogic.*:com.restfully.*:org.jboss.resteasy.examples.*:org.jboss.resteasy.tests.*
- </excludePackageNames>
- </configuration>
- </execution>
- </executions>
- </plugin>
- </plugins>
- </build>
- </profile>
- <profile>
- <id>doclint-java8-disable</id>
- <activation>
- <jdk>[1.8,)</jdk>
- </activation>
-
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-javadoc-plugin</artifactId>
- <configuration>
- <additionalparam>-Xdoclint:none</additionalparam>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
</profiles>
</project>
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonActionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonActionType.java
index 6936b57..81e0685 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonActionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonActionType.java
@@ -27,8 +27,6 @@ import java.io.Serializable;
*/
public class CommonActionType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected String namespace;
protected String value;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAdviceType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAdviceType.java
index b6d3a2b..b5beb37 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAdviceType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAdviceType.java
@@ -30,8 +30,6 @@ import java.util.List;
*/
public class CommonAdviceType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<Object> advices = new ArrayList<Object>();
/**
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAssertionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAssertionType.java
index 0637d7f..7d5d518 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAssertionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonAssertionType.java
@@ -28,8 +28,6 @@ import java.io.Serializable;
*/
public class CommonAssertionType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected XMLGregorianCalendar issueInstant;
protected String ID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonConditionsType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonConditionsType.java
index 4ad88a5..d69a7b9 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonConditionsType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonConditionsType.java
@@ -26,8 +26,6 @@ import java.io.Serializable;
*/
public class CommonConditionsType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected XMLGregorianCalendar notBefore;
protected XMLGregorianCalendar notOnOrAfter;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonRequestAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonRequestAbstractType.java
index 9ba0638..6dd2219 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonRequestAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonRequestAbstractType.java
@@ -30,8 +30,6 @@ import java.io.Serializable;
*/
public abstract class CommonRequestAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected String id;
protected XMLGregorianCalendar issueInstant;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonResponseType.java
index fbb4f65..c04fa9e 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonResponseType.java
@@ -28,8 +28,6 @@ import java.io.Serializable;
*/
public class CommonResponseType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected String id;
protected XMLGregorianCalendar issueInstant;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonStatusDetailType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonStatusDetailType.java
index cb427e3..6c9d6b9 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonStatusDetailType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/common/CommonStatusDetailType.java
@@ -43,8 +43,6 @@ import java.util.List;
*/
public class CommonStatusDetailType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<Object> any = new ArrayList<Object>();
/**
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ActionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ActionType.java
index 8e70a3b..6fda13a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ActionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ActionType.java
@@ -31,5 +31,4 @@ import org.keycloak.dom.saml.common.CommonActionType;
*/
public class SAML11ActionType extends CommonActionType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AdviceType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AdviceType.java
index 0f0dcc6..29ebad5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AdviceType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AdviceType.java
@@ -29,5 +29,4 @@ import org.keycloak.dom.saml.common.CommonAdviceType;
*/
public class SAML11AdviceType extends CommonAdviceType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AssertionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AssertionType.java
index 1a72241..edd3ee0 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AssertionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AssertionType.java
@@ -46,8 +46,6 @@ import java.util.List;
*/
public class SAML11AssertionType extends CommonAssertionType {
- private static final long serialVersionUID = 1L;
-
protected int majorVersion = 1;
protected int minorVersion = 1;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AttributeStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AttributeStatementType.java
index f572210..1ef6b82 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AttributeStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AttributeStatementType.java
@@ -33,8 +33,6 @@ import java.util.List;
*/
public class SAML11AttributeStatementType extends SAML11SubjectStatementType {
- private static final long serialVersionUID = 1L;
-
protected List<SAML11AttributeType> attribute = new ArrayList<SAML11AttributeType>();
public void add(SAML11AttributeType aAttribute) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AudienceRestrictionCondition.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AudienceRestrictionCondition.java
index f54ef37..33e2a94 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AudienceRestrictionCondition.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AudienceRestrictionCondition.java
@@ -33,8 +33,6 @@ import java.util.List;
*/
public class SAML11AudienceRestrictionCondition extends SAML11ConditionAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<URI> audience = new ArrayList<URI>();
public void add(URI advice) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthenticationStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthenticationStatementType.java
index fd2fbe0..2ee15e8 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthenticationStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthenticationStatementType.java
@@ -35,8 +35,6 @@ import java.util.List;
*/
public class SAML11AuthenticationStatementType extends SAML11SubjectStatementType {
- private static final long serialVersionUID = 1L;
-
protected URI authenticationMethod;
protected XMLGregorianCalendar authenticationInstant;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthorizationDecisionStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthorizationDecisionStatementType.java
index 970535b..c85701c 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthorizationDecisionStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11AuthorizationDecisionStatementType.java
@@ -36,8 +36,6 @@ import java.util.List;
*/
public class SAML11AuthorizationDecisionStatementType extends SAML11SubjectStatementType {
- private static final long serialVersionUID = 1L;
-
protected List<SAML11ActionType> actions = new ArrayList<SAML11ActionType>();
protected SAML11EvidenceType evidence;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionAbstractType.java
index 4208335..23f3231 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionAbstractType.java
@@ -25,5 +25,4 @@ import java.io.Serializable;
*/
public abstract class SAML11ConditionAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsAbstractType.java
index 9106bf2..c56143f 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsAbstractType.java
@@ -25,5 +25,4 @@ import java.io.Serializable;
*/
public abstract class SAML11ConditionsAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsType.java
index 4a4662c..8a7b33b 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionsType.java
@@ -36,8 +36,6 @@ import java.util.List;
*/
public class SAML11ConditionsType extends CommonConditionsType {
- private static final long serialVersionUID = 1L;
-
public List<SAML11ConditionAbstractType> conditions = new ArrayList<SAML11ConditionAbstractType>();
public void add(SAML11ConditionAbstractType condition) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionType.java
index f06bf32..c6a0446 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11ConditionType.java
@@ -23,5 +23,4 @@ package org.keycloak.dom.saml.v1.assertion;
*/
public class SAML11ConditionType extends SAML11ConditionAbstractType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11DoNotCacheConditionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11DoNotCacheConditionType.java
index dc0c791..3ba9b7d 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11DoNotCacheConditionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11DoNotCacheConditionType.java
@@ -23,5 +23,4 @@ package org.keycloak.dom.saml.v1.assertion;
*/
public class SAML11DoNotCacheConditionType extends SAML11ConditionAbstractType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11NameIdentifierType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11NameIdentifierType.java
index 0687f3c..78c3c94 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11NameIdentifierType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11NameIdentifierType.java
@@ -30,8 +30,6 @@ import java.net.URI;
*/
public class SAML11NameIdentifierType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected String nameQualifier;
protected URI format;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementAbstractType.java
index 96f1a24..f7a7ce5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementAbstractType.java
@@ -25,5 +25,4 @@ import java.io.Serializable;
*/
public abstract class SAML11StatementAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementType.java
index fa8a94d..8015555 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11StatementType.java
@@ -23,5 +23,4 @@ package org.keycloak.dom.saml.v1.assertion;
*/
public class SAML11StatementType extends SAML11StatementAbstractType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11SubjectStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11SubjectStatementType.java
index d6118ae..d1a64e5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11SubjectStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/assertion/SAML11SubjectStatementType.java
@@ -28,8 +28,6 @@ package org.keycloak.dom.saml.v1.assertion;
*/
public class SAML11SubjectStatementType extends SAML11StatementAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SAML11SubjectType subject;
public SAML11SubjectStatementType() {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AttributeQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AttributeQueryType.java
index 9f13479..d79a6c3 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AttributeQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AttributeQueryType.java
@@ -36,8 +36,6 @@ import java.util.List;
*/
public class SAML11AttributeQueryType extends SAML11SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<SAML11AttributeDesignatorType> attributeDesignator = new ArrayList<SAML11AttributeDesignatorType>();
protected URI resource;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthenticationQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthenticationQueryType.java
index d2d7bc0..ac9ff4b 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthenticationQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthenticationQueryType.java
@@ -31,8 +31,6 @@ import java.net.URI;
*/
public class SAML11AuthenticationQueryType extends SAML11SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected URI authenticationMethod;
public URI getAuthenticationMethod() {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthorizationDecisionQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthorizationDecisionQueryType.java
index dc9d9b8..061b50a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthorizationDecisionQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11AuthorizationDecisionQueryType.java
@@ -39,8 +39,6 @@ import java.util.List;
*/
public class SAML11AuthorizationDecisionQueryType extends SAML11SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<SAML11ActionType> action = new ArrayList<SAML11ActionType>();
protected SAML11EvidenceType evidence;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11QueryAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11QueryAbstractType.java
index 3b2c6df..88e52f4 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11QueryAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11QueryAbstractType.java
@@ -25,5 +25,4 @@ import java.io.Serializable;
*/
public abstract class SAML11QueryAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestAbstractType.java
index f0fa48b..c63c23f 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestAbstractType.java
@@ -39,8 +39,6 @@ import java.util.List;
*/
public abstract class SAML11RequestAbstractType extends CommonRequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected int majorVersion = 1;
protected int minorVersion = 1;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestType.java
index f86ff31..a7d7307 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11RequestType.java
@@ -36,8 +36,6 @@ import java.util.List;
*/
public class SAML11RequestType extends SAML11RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SAML11QueryAbstractType query;
protected List<String> assertionIDRef = new ArrayList<String>();
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseAbstractType.java
index 48b5938..1afd7b7 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseAbstractType.java
@@ -37,8 +37,6 @@ import java.net.URI;
*/
public abstract class SAML11ResponseAbstractType extends CommonResponseType {
- private static final long serialVersionUID = 1L;
-
protected int majorVersion = 1;
protected int minorVersion = 1;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseType.java
index c07dae4..29936e5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11ResponseType.java
@@ -35,8 +35,6 @@ import java.util.List;
*/
public class SAML11ResponseType extends SAML11ResponseAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<SAML11AssertionType> assertions = new ArrayList<SAML11AssertionType>();
protected SAML11StatusType status;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusCodeType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusCodeType.java
index 38c56bf..3591e62 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusCodeType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusCodeType.java
@@ -30,8 +30,6 @@ import java.io.Serializable;
*/
public class SAML11StatusCodeType implements Serializable {
- private static final long serialVersionUID = 1L;
-
public static final SAML11StatusCodeType SUCCESS = new SAML11StatusCodeType(new QName("samlp:Success"));
protected SAML11StatusCodeType statusCode;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusType.java
index 7f4b7a6..afea6a9 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11StatusType.java
@@ -32,8 +32,6 @@ import java.io.Serializable;
*/
public class SAML11StatusType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected SAML11StatusCodeType statusCode;
protected String statusMessage;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11SubjectQueryAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11SubjectQueryAbstractType.java
index c9ef462..0328e16 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11SubjectQueryAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v1/protocol/SAML11SubjectQueryAbstractType.java
@@ -31,8 +31,6 @@ import org.keycloak.dom.saml.v1.assertion.SAML11SubjectType;
*/
public class SAML11SubjectQueryAbstractType extends SAML11QueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SAML11SubjectType subject;
public SAML11SubjectType getSubject() {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ActionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ActionType.java
index c31bf4e..98b7510 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ActionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ActionType.java
@@ -38,5 +38,4 @@ import org.keycloak.dom.saml.common.CommonActionType;
*/
public class ActionType extends CommonActionType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AdviceType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AdviceType.java
index 8f9fc2d..31ca7d1 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AdviceType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AdviceType.java
@@ -45,5 +45,4 @@ import org.keycloak.dom.saml.common.CommonAdviceType;
*/
public class AdviceType extends CommonAdviceType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AssertionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AssertionType.java
index 7cbd35e..b401bcb 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AssertionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AssertionType.java
@@ -43,8 +43,6 @@ import java.util.Set;
*/
public class AssertionType extends CommonAssertionType {
- private static final long serialVersionUID = 1L;
-
private Element signature;
private final String version = "2.0";
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java
index 9b65f38..0cb88c4 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeStatementType.java
@@ -45,7 +45,6 @@ import java.util.List;
*/
public class AttributeStatementType extends StatementAbstractType {
- private static final long serialVersionUID = 1L;
protected List<ASTChoiceType> attributes = new ArrayList<ASTChoiceType>();
/**
@@ -81,7 +80,6 @@ public class AttributeStatementType extends StatementAbstractType {
public static class ASTChoiceType implements Serializable {
- private static final long serialVersionUID = 1L;
private AttributeType attribute;
private EncryptedElementType encryptedAssertion;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeType.java
index c62111d..4393008 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AttributeType.java
@@ -50,8 +50,6 @@ import java.util.Map;
*/
public class AttributeType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<Object> attributeValue = new ArrayList<Object>();
protected String name;
protected String nameFormat;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AudienceRestrictionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AudienceRestrictionType.java
index 38e1f9d..566dcbb 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AudienceRestrictionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AudienceRestrictionType.java
@@ -44,8 +44,6 @@ import java.util.List;
*/
public class AudienceRestrictionType extends ConditionAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<URI> audience = new ArrayList<URI>();
/**
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextClassRefType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextClassRefType.java
index 00c7611..e06d5ed 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextClassRefType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextClassRefType.java
@@ -28,8 +28,6 @@ import java.net.URI;
*/
public class AuthnContextClassRefType implements URIType, Serializable {
- private static final long serialVersionUID = 1L;
-
private final URI value;
public AuthnContextClassRefType(URI value) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclRefType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclRefType.java
index e28ca5c..247aaeb 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclRefType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclRefType.java
@@ -28,7 +28,6 @@ import java.net.URI;
*/
public class AuthnContextDeclRefType implements URIType, Serializable {
- private static final long serialVersionUID = 1L;
private URI value;
public AuthnContextDeclRefType(URI value) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclType.java
index 51749b9..bfde9d6 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextDeclType.java
@@ -27,8 +27,6 @@ import java.io.Serializable;
*/
public class AuthnContextDeclType implements Serializable {
- private static final long serialVersionUID = 1L;
-
private Object value;
public AuthnContextDeclType(Object value) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextType.java
index 0f9e18c..38069ef 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnContextType.java
@@ -60,8 +60,6 @@ import java.util.Set;
*/
public class AuthnContextType implements Serializable {
- private static final long serialVersionUID = 1L;
-
private final Set<URI> authenticatingAuthority = new LinkedHashSet<URI>();
private AuthnContextTypeSequence sequence;
@@ -165,8 +163,6 @@ public class AuthnContextType implements Serializable {
*/
public class AuthnContextTypeSequence implements Serializable {
- private static final long serialVersionUID = 1L;
-
private AuthnContextClassRefType classRef;
private AuthnContextDeclType authnContextDecl;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnStatementType.java
index 33d8916..b68df7c 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthnStatementType.java
@@ -44,8 +44,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class AuthnStatementType extends StatementAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SubjectLocalityType subjectLocality;
protected AuthnContextType authnContext;
protected XMLGregorianCalendar authnInstant;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthzDecisionStatementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthzDecisionStatementType.java
index fa8a861..fd9efb5 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthzDecisionStatementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/AuthzDecisionStatementType.java
@@ -45,7 +45,6 @@ import java.util.List;
*/
public class AuthzDecisionStatementType extends StatementAbstractType {
- private static final long serialVersionUID = 1L;
protected List<ActionType> action = new ArrayList<ActionType>();
protected EvidenceType evidence;
protected String resource;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/BaseIDAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/BaseIDAbstractType.java
index 0090d23..67056bf 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/BaseIDAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/BaseIDAbstractType.java
@@ -38,7 +38,6 @@ import java.io.Serializable;
*/
public abstract class BaseIDAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
private String nameQualifier;
private String sPNameQualifier;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionAbstractType.java
index 64fe2ab..c1fd41a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionAbstractType.java
@@ -37,5 +37,4 @@ import java.io.Serializable;
*/
public abstract class ConditionAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionsType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionsType.java
index 0d77f40..65444cd 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionsType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ConditionsType.java
@@ -50,8 +50,6 @@ import java.util.List;
*/
public class ConditionsType extends CommonConditionsType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<ConditionAbstractType> conditions = new ArrayList<ConditionAbstractType>();
/**
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedAssertionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedAssertionType.java
index 31cc80d..88b863d 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedAssertionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedAssertionType.java
@@ -27,8 +27,6 @@ import org.w3c.dom.Element;
*/
public class EncryptedAssertionType extends EncryptedElementType {
- private static final long serialVersionUID = 1L;
-
public EncryptedAssertionType() {
super();
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedElementType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedElementType.java
index 19c13d6..073a8e1 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedElementType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EncryptedElementType.java
@@ -29,8 +29,6 @@ import java.io.Serializable;
*/
public class EncryptedElementType implements Serializable {
- private static final long serialVersionUID = 1L;
-
/**
* <complexType name="EncryptedElementType"> <sequence> <element ref="xenc:EncryptedData"/> <element
* ref="xenc:EncryptedKey"
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EvidenceType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EvidenceType.java
index 47110fa..c93a168 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EvidenceType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/EvidenceType.java
@@ -48,7 +48,6 @@ import java.util.List;
*/
public class EvidenceType implements Serializable {
- private static final long serialVersionUID = 1L;
protected List<ChoiceType> evidences = new ArrayList<ChoiceType>();
/**
@@ -80,7 +79,6 @@ public class EvidenceType implements Serializable {
public static class ChoiceType implements Serializable {
- private static final long serialVersionUID = 1L;
private String AssertionIDRef;
private URI AssertionURIRef;
private AssertionType assertion;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/KeyInfoConfirmationDataType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/KeyInfoConfirmationDataType.java
index e11dc49..bb3ed69 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/KeyInfoConfirmationDataType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/KeyInfoConfirmationDataType.java
@@ -40,8 +40,6 @@ import org.w3c.dom.Element;
*/
public class KeyInfoConfirmationDataType extends SubjectConfirmationDataType {
- private static final long serialVersionUID = 2510471236717847074L;
-
protected Element keyInfo;
public Element getKeyInfo() {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/NameIDType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/NameIDType.java
index 3f7aec1..25a2b8a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/NameIDType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/NameIDType.java
@@ -35,7 +35,6 @@ public class NameIDType extends BaseIDAbstractType {
* name="SPNameQualifier" type="string" use="optional"/> </attributeGroup>
*/
- private static final long serialVersionUID = 1L;
private String value;
private URI format;
private String sPProvidedID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/OneTimeUseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/OneTimeUseType.java
index 1898c7b..0b890a7 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/OneTimeUseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/OneTimeUseType.java
@@ -35,5 +35,4 @@ package org.keycloak.dom.saml.v2.assertion;
*/
public class OneTimeUseType extends ConditionAbstractType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ProxyRestrictionType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ProxyRestrictionType.java
index 5072181..2c032f4 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ProxyRestrictionType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/ProxyRestrictionType.java
@@ -45,8 +45,6 @@ import java.util.List;
*/
public class ProxyRestrictionType extends ConditionAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<URI> audience = new ArrayList<URI>();
protected BigInteger count;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/StatementAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/StatementAbstractType.java
index 0230b15..3f80635 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/StatementAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/StatementAbstractType.java
@@ -38,5 +38,4 @@ import java.io.Serializable;
*/
public abstract class StatementAbstractType implements Serializable {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationDataType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationDataType.java
index 274104f..579581a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationDataType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationDataType.java
@@ -50,8 +50,6 @@ import java.util.Map;
*/
public class SubjectConfirmationDataType implements Serializable {
- private static final long serialVersionUID = 7695748370849965158L;
-
protected XMLGregorianCalendar notBefore;
protected XMLGregorianCalendar notOnOrAfter;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationType.java
index 6979022..dca866a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectConfirmationType.java
@@ -47,7 +47,6 @@ import java.io.Serializable;
*/
public class SubjectConfirmationType implements Serializable {
- private static final long serialVersionUID = 1L;
protected BaseIDAbstractType baseID;
protected NameIDType nameID;
protected EncryptedElementType encryptedID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectLocalityType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectLocalityType.java
index 4a8bb60..a89561d 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectLocalityType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectLocalityType.java
@@ -39,8 +39,6 @@ import java.io.Serializable;
*/
public class SubjectLocalityType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected String address;
protected String dnsName;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectType.java
index 61aeb08..c391bae 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/assertion/SubjectType.java
@@ -52,8 +52,6 @@ import java.util.List;
*/
public class SubjectType implements Serializable {
- private static final long serialVersionUID = 1L;
-
protected List<SubjectConfirmationType> subjectConfirmation = new ArrayList<SubjectConfirmationType>();
protected STSubType subType;
@@ -114,8 +112,6 @@ public class SubjectType implements Serializable {
public static class STSubType implements Serializable {
- private static final long serialVersionUID = -4073731807610876524L;
-
private BaseIDAbstractType baseID;
private EncryptedElementType encryptedID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/metadata/RequestedAttributeType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/metadata/RequestedAttributeType.java
index 2d6bb85..428ba9f 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/metadata/RequestedAttributeType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/metadata/RequestedAttributeType.java
@@ -38,8 +38,6 @@ import org.keycloak.dom.saml.v2.assertion.AttributeType;
*/
public class RequestedAttributeType extends AttributeType {
- private static final long serialVersionUID = 1L;
-
public RequestedAttributeType(String name) {
super(name);
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResolveType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResolveType.java
index 6d6f3ed..90b2641 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResolveType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResolveType.java
@@ -40,8 +40,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class ArtifactResolveType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected String artifact;
public ArtifactResolveType(String id, XMLGregorianCalendar instant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResponseType.java
index 8ec699c..9fc3bb0 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ArtifactResponseType.java
@@ -40,8 +40,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class ArtifactResponseType extends StatusResponseType {
- private static final long serialVersionUID = 1L;
-
protected Object any;
public ArtifactResponseType(String id, XMLGregorianCalendar issueInstant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AssertionIDRequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AssertionIDRequestType.java
index 4f3e946..fed66ee 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AssertionIDRequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AssertionIDRequestType.java
@@ -43,8 +43,6 @@ import java.util.List;
*/
public class AssertionIDRequestType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<String> assertionIDRef = new ArrayList<String>();
public AssertionIDRequestType(String id, XMLGregorianCalendar instant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AttributeQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AttributeQueryType.java
index 9f706a5..fdf5484 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AttributeQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AttributeQueryType.java
@@ -45,8 +45,6 @@ import java.util.List;
*/
public class AttributeQueryType extends SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<AttributeType> attribute = new ArrayList<AttributeType>();
public AttributeQueryType(String id, XMLGregorianCalendar instant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnQueryType.java
index d6951eb..9898068 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnQueryType.java
@@ -41,8 +41,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class AuthnQueryType extends SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected RequestedAuthnContextType requestedAuthnContext;
protected String sessionIndex;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnRequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnRequestType.java
index aa6c8d9..1491848 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnRequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthnRequestType.java
@@ -56,8 +56,6 @@ import java.net.URI;
*/
public class AuthnRequestType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SubjectType subject;
protected NameIDPolicyType nameIDPolicy;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthzDecisionQueryType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthzDecisionQueryType.java
index 20ee1b9..2c52846 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthzDecisionQueryType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/AuthzDecisionQueryType.java
@@ -49,8 +49,6 @@ import java.util.List;
*/
public class AuthzDecisionQueryType extends SubjectQueryAbstractType {
- private static final long serialVersionUID = 1L;
-
protected List<ActionType> action = new ArrayList<ActionType>();
protected EvidenceType evidence;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/LogoutRequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/LogoutRequestType.java
index 7249481..6db55a1 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/LogoutRequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/LogoutRequestType.java
@@ -54,8 +54,6 @@ import java.util.List;
*/
public class LogoutRequestType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected BaseIDAbstractType baseID;
protected NameIDType nameID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ManageNameIDRequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ManageNameIDRequestType.java
index c798d12..89952bb 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ManageNameIDRequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ManageNameIDRequestType.java
@@ -51,8 +51,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class ManageNameIDRequestType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected NameIDType nameID;
protected EncryptedElementType encryptedID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingRequestType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingRequestType.java
index d2b77b5..bd5fe43 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingRequestType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingRequestType.java
@@ -49,8 +49,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class NameIDMappingRequestType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected BaseIDAbstractType baseID;
protected NameIDType nameID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingResponseType.java
index 30ce02c..dedc8ff 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/NameIDMappingResponseType.java
@@ -44,8 +44,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class NameIDMappingResponseType extends StatusResponseType {
- private static final long serialVersionUID = 1L;
-
protected NameIDType nameID;
protected EncryptedElementType encryptedID;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/RequestAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/RequestAbstractType.java
index 5fa57ab..c670169 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/RequestAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/RequestAbstractType.java
@@ -52,8 +52,6 @@ import java.net.URI;
*/
public abstract class RequestAbstractType extends CommonRequestAbstractType implements SAML2Object {
- private static final long serialVersionUID = 1L;
-
protected NameIDType issuer;
protected ExtensionsType extensions;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ResponseType.java
index e4664b4..fec0f13 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/ResponseType.java
@@ -47,8 +47,6 @@ import java.util.List;
*/
public class ResponseType extends StatusResponseType {
- private static final long serialVersionUID = 1L;
-
protected List<RTChoiceType> assertions = new ArrayList<ResponseType.RTChoiceType>();
public ResponseType(String id, XMLGregorianCalendar issueInstant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusDetailType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusDetailType.java
index 22f2171..2166e3a 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusDetailType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusDetailType.java
@@ -40,5 +40,4 @@ import org.keycloak.dom.saml.common.CommonStatusDetailType;
*/
public class StatusDetailType extends CommonStatusDetailType {
- private static final long serialVersionUID = 1L;
}
\ No newline at end of file
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusResponseType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusResponseType.java
index 5cbd1d1..12235b3 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusResponseType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/StatusResponseType.java
@@ -53,8 +53,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public class StatusResponseType extends CommonResponseType implements SAML2Object {
- private static final long serialVersionUID = 1L;
-
protected NameIDType issuer;
protected ExtensionsType extensions;
diff --git a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/SubjectQueryAbstractType.java b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/SubjectQueryAbstractType.java
index 31b2958..857e3e7 100755
--- a/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/SubjectQueryAbstractType.java
+++ b/saml/saml-core/src/main/java/org/keycloak/dom/saml/v2/protocol/SubjectQueryAbstractType.java
@@ -42,8 +42,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
*/
public abstract class SubjectQueryAbstractType extends RequestAbstractType {
- private static final long serialVersionUID = 1L;
-
protected SubjectType subject;
public SubjectQueryAbstractType(String id, XMLGregorianCalendar instant) {
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ConfigurationException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ConfigurationException.java
index dd1b9dd..4a377b0 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ConfigurationException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ConfigurationException.java
@@ -26,8 +26,7 @@ import java.security.GeneralSecurityException;
* @since May 22, 2009
*/
public class ConfigurationException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
-
+
public ConfigurationException() {
super();
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/AssertionExpiredException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/AssertionExpiredException.java
index 3877363..746c072 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/AssertionExpiredException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/AssertionExpiredException.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
* @since Dec 12, 2008
*/
public class AssertionExpiredException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
protected String id;
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssueInstantMissingException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssueInstantMissingException.java
index aae6dd5..e8cbd56 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssueInstantMissingException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssueInstantMissingException.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
* @since Jun 3, 2009
*/
public class IssueInstantMissingException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
public IssueInstantMissingException() {
super();
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssuerNotTrustedException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssuerNotTrustedException.java
index a9cc524..02820ee 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssuerNotTrustedException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/IssuerNotTrustedException.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
* @since Jan 26, 2009
*/
public class IssuerNotTrustedException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
public IssuerNotTrustedException() {
super();
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/SignatureValidationException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/SignatureValidationException.java
index 59f0b82..bfafecd 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/SignatureValidationException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/SignatureValidationException.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
* @since Jul 28, 2011
*/
public class SignatureValidationException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
public SignatureValidationException() {
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/WSTrustException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/WSTrustException.java
index 75f56ff..937ebb6 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/WSTrustException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/fed/WSTrustException.java
@@ -28,8 +28,6 @@ import java.security.GeneralSecurityException;
*/
public class WSTrustException extends GeneralSecurityException {
- private static final long serialVersionUID = -232066282004315310L;
-
/**
* <p>
* Creates an instance of {@code WSTrustException} using the specified error message.
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ParsingException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ParsingException.java
index fb02e6f..ea1fa2b 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ParsingException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ParsingException.java
@@ -28,7 +28,6 @@ import java.security.GeneralSecurityException;
* @since May 22, 2009
*/
public class ParsingException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
private Location location;
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/PicketLinkException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/PicketLinkException.java
index 0a6645d..799e377 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/PicketLinkException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/PicketLinkException.java
@@ -27,8 +27,6 @@ package org.keycloak.saml.common.exceptions;
*/
public class PicketLinkException extends RuntimeException {
- private static final long serialVersionUID = 789326682407249952L;
-
public PicketLinkException() {
super();
}
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ProcessingException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ProcessingException.java
index 313e9e1..2b88e2f 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ProcessingException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/ProcessingException.java
@@ -26,7 +26,6 @@ import java.security.GeneralSecurityException;
* @since May 22, 2009
*/
public class ProcessingException extends GeneralSecurityException {
- private static final long serialVersionUID = 1L;
public ProcessingException() {
super();
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyConfigurationException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyConfigurationException.java
index f8fc736..7f88085 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyConfigurationException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyConfigurationException.java
@@ -24,7 +24,6 @@ package org.keycloak.saml.common.exceptions;
* @since May 22, 2009
*/
public class TrustKeyConfigurationException extends ConfigurationException {
- private static final long serialVersionUID = 1L;
public TrustKeyConfigurationException() {
super();
diff --git a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyProcessingException.java b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyProcessingException.java
index e08644a..b900f0e 100755
--- a/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyProcessingException.java
+++ b/saml/saml-core/src/main/java/org/keycloak/saml/common/exceptions/TrustKeyProcessingException.java
@@ -24,7 +24,6 @@ package org.keycloak.saml.common.exceptions;
* @since May 22, 2009
*/
public class TrustKeyProcessingException extends ProcessingException {
- private static final long serialVersionUID = 1L;
public TrustKeyProcessingException() {
super();
services/pom.xml 25(+0 -25)
diff --git a/services/pom.xml b/services/pom.xml
index 3f6bd51..1e2e84e 100755
--- a/services/pom.xml
+++ b/services/pom.xml
@@ -21,67 +21,54 @@
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcpkix-jdk15on</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core-jaxrs</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-connections-http-client</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-forms-common-freemarker</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-events-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-account-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-email-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-login-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-invalidation-cache-model</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-social-core</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
@@ -90,33 +77,27 @@
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-timer-basic</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-export-import-api</artifactId>
- <scope>provided</scope>
</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>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jaxrs</artifactId>
- <scope>provided</scope>
<exclusions>
<exclusion>
<groupId>log4j</groupId>
@@ -135,32 +116,26 @@
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-multipart-provider</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-xc</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>com.google.zxing</groupId>
<artifactId>javase</artifactId>
- <scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
diff --git a/services/src/docs/asciidoc/index.adoc b/services/src/docs/asciidoc/index.adoc
index 226d206..17ffc52 100644
--- a/services/src/docs/asciidoc/index.adoc
+++ b/services/src/docs/asciidoc/index.adoc
@@ -1,3 +1,3 @@
-include::{generated}/overview.adoc[]
+include::overview.adoc[]
include::{generated}/paths.adoc[]
include::{generated}/definitions.adoc[]
\ No newline at end of file
services/src/docs/asciidoc/overview.adoc 13(+13 -0)
diff --git a/services/src/docs/asciidoc/overview.adoc b/services/src/docs/asciidoc/overview.adoc
new file mode 100644
index 0000000..e2457ab
--- /dev/null
+++ b/services/src/docs/asciidoc/overview.adoc
@@ -0,0 +1,13 @@
+= Keycloak Admin REST API
+
+== Overview
+This is a REST API reference for the Keycloak Admin
+
+=== Version information
+Version: 1
+
+=== URI scheme
+Host: localhost:8080
+BasePath: /auth
+Schemes: HTTP
+
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
index 0c308ab..96f81cb 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/client/JWTClientAuthenticator.java
@@ -27,7 +27,7 @@ import org.keycloak.services.Urls;
/**
* Client authentication based on JWT signed by client private key .
- * See <a href="https://tools.ietf.org/html/draft-jones-oauth-jwt-bearer-03">specs</a> for more details.
+ * See <a href="https://tools.ietf.org/html/rfc7519">specs</a> for more details.
*
* This is server side, which verifies JWT from client_assertion parameter, where the assertion was created on adapter side by
* org.keycloak.adapters.authentication.JWTClientCredentialsProvider
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
index 42c2e02..9630f3b 100755
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/UpdateProfile.java
@@ -52,7 +52,7 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
RealmModel realm = context.getRealm();
- List<FormMessage> errors = Validation.validateUpdateProfileForm(formData);
+ List<FormMessage> errors = Validation.validateUpdateProfileForm(realm, formData);
if (errors != null && !errors.isEmpty()) {
Response challenge = context.form()
.setErrors(errors)
@@ -62,6 +62,28 @@ public class UpdateProfile implements RequiredActionProvider, RequiredActionFact
return;
}
+ if (realm.isEditUsernameAllowed()) {
+ String username = formData.getFirst("username");
+ String oldUsername = user.getUsername();
+
+ boolean usernameChanged = oldUsername != null ? !oldUsername.equals(username) : username != null;
+
+ if (usernameChanged) {
+
+ if (session.users().getUserByUsername(username, realm) != null) {
+ Response challenge = context.form()
+ .setError(Messages.USERNAME_EXISTS)
+ .setFormData(formData)
+ .createResponse(UserModel.RequiredAction.UPDATE_PROFILE);
+ context.challenge(challenge);
+ return;
+ }
+
+ user.setUsername(username);
+ }
+
+ }
+
user.setFirstName(formData.getFirst("firstName"));
user.setLastName(formData.getFirst("lastName"));
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
index 15c1f46..00970e2 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenEndpoint.java
@@ -262,11 +262,14 @@ public class TokenEndpoint {
AccessTokenResponse res;
try {
- res = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event, headers);
+ TokenManager.RefreshResult result = tokenManager.refreshAccessToken(session, uriInfo, clientConnection, realm, client, refreshToken, event, headers);
+ res = result.getResponse();
- UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
- updateClientSessions(userSession.getClientSessions());
- updateUserSessionFromClientAuth(userSession);
+ if (!result.isOfflineToken()) {
+ UserSessionModel userSession = session.sessions().getUserSession(realm, res.getSessionState());
+ updateClientSessions(userSession.getClientSessions());
+ updateUserSessionFromClientAuth(userSession);
+ }
} catch (OAuthErrorException e) {
event.error(Errors.INVALID_TOKEN);
@@ -337,6 +340,8 @@ public class TokenEndpoint {
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
+ clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
+
AuthenticationFlowModel flow = realm.getDirectGrantFlow();
String flowId = flow.getId();
AuthenticationProcessor processor = new AuthenticationProcessor();
@@ -363,7 +368,7 @@ public class TokenEndpoint {
updateUserSessionFromClientAuth(userSession);
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
- .generateAccessToken(session, scope, client, user, userSession, clientSession)
+ .generateAccessToken()
.generateRefreshToken()
.generateIDToken()
.build();
@@ -415,6 +420,7 @@ public class TokenEndpoint {
ClientSessionModel clientSession = sessions.createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setNote(OIDCLoginProtocol.ISSUER, Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName()));
+ clientSession.setNote(OIDCLoginProtocol.SCOPE_PARAM, scope);
UserSessionModel userSession = sessions.createUserSession(realm, clientUser, clientUsername, clientConnection.getRemoteAddr(), ServiceAccountConstants.CLIENT_AUTH, false, null, null);
event.session(userSession);
@@ -429,7 +435,7 @@ public class TokenEndpoint {
updateUserSessionFromClientAuth(userSession);
AccessTokenResponse res = tokenManager.responseBuilder(realm, client, event, session, userSession, clientSession)
- .generateAccessToken(session, scope, client, clientUser, userSession, clientSession)
+ .generateAccessToken()
.generateRefreshToken()
.generateIDToken()
.build();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
index afd2a8a..714c0d1 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCWellKnownProvider.java
@@ -20,7 +20,7 @@ public class OIDCWellKnownProvider implements WellKnownProvider {
public static final List<String> DEFAULT_ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = list("RS256");
- public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD);
+ public static final List<String> DEFAULT_GRANT_TYPES_SUPPORTED = list(OAuth2Constants.AUTHORIZATION_CODE, OAuth2Constants.REFRESH_TOKEN, OAuth2Constants.PASSWORD, OAuth2Constants.CLIENT_CREDENTIALS);
public static final List<String> DEFAULT_RESPONSE_TYPES_SUPPORTED = list(OAuth2Constants.CODE);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index 5ee8191..c43140a 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -2,9 +2,10 @@ package org.keycloak.protocol.oidc;
import org.jboss.logging.Logger;
import org.keycloak.ClientConnection;
+import org.keycloak.OAuth2Constants;
import org.keycloak.OAuthErrorException;
-import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder;
import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.jose.jws.JWSInput;
@@ -27,13 +28,20 @@ import org.keycloak.representations.AccessToken;
import org.keycloak.representations.AccessTokenResponse;
import org.keycloak.representations.IDToken;
import org.keycloak.representations.RefreshToken;
+import org.keycloak.services.ErrorResponseException;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.offline.OfflineTokenUtils;
+import org.keycloak.util.RefreshTokenUtil;
import org.keycloak.util.Time;
import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@@ -85,16 +93,27 @@ public class TokenManager {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "User disabled", "User disabled");
}
- UserSessionModel userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
- if (!AuthenticationManager.isSessionValid(realm, userSession)) {
- AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
- }
+ UserSessionModel userSession = null;
ClientSessionModel clientSession = null;
- for (ClientSessionModel clientSessionModel : userSession.getClientSessions()) {
- if (clientSessionModel.getId().equals(oldToken.getClientSession())) {
- clientSession = clientSessionModel;
- break;
+ if (RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(oldToken.getType())) {
+
+ clientSession = OfflineTokenUtils.findOfflineClientSession(realm, user, oldToken.getClientSession(), oldToken.getSessionState());
+ if (clientSession != null) {
+ userSession = clientSession.getUserSession();
+ }
+ } else {
+ // Find userSession regularly for online tokens
+ userSession = session.sessions().getUserSession(realm, oldToken.getSessionState());
+ if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+ AuthenticationManager.backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Session not active", "Session not active");
+ }
+
+ for (ClientSessionModel clientSessionModel : userSession.getClientSessions()) {
+ if (clientSessionModel.getId().equals(oldToken.getClientSession())) {
+ clientSession = clientSessionModel;
+ break;
+ }
}
}
@@ -117,7 +136,8 @@ public class TokenManager {
// recreate token.
- Set<RoleModel> requestedRoles = TokenManager.getAccess(null, clientSession.getClient(), user);
+ String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ Set<RoleModel> requestedRoles = TokenManager.getAccess(scopeParam, true, clientSession.getClient(), user);
AccessToken newToken = createClientAccessToken(session, requestedRoles, realm, client, user, userSession, clientSession);
verifyAccess(oldToken, newToken);
@@ -126,10 +146,12 @@ public class TokenManager {
}
- public AccessTokenResponse refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
+ public RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
RefreshToken refreshToken = verifyRefreshToken(realm, encodedRefreshToken);
- event.user(refreshToken.getSubject()).session(refreshToken.getSessionState()).detail(Details.REFRESH_TOKEN_ID, refreshToken.getId());
+ event.user(refreshToken.getSubject()).session(refreshToken.getSessionState())
+ .detail(Details.REFRESH_TOKEN_ID, refreshToken.getId())
+ .detail(Details.REFRESH_TOKEN_TYPE, refreshToken.getType());
TokenValidation validation = validateToken(session, uriInfo, connection, realm, refreshToken, headers);
// validate authorizedClient is same as validated client
@@ -140,11 +162,17 @@ public class TokenManager {
int currentTime = Time.currentTime();
validation.userSession.setLastSessionRefresh(currentTime);
- AccessTokenResponse res = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession)
+ AccessTokenResponseBuilder responseBuilder = responseBuilder(realm, authorizedClient, event, session, validation.userSession, validation.clientSession)
.accessToken(validation.newToken)
- .generateIDToken()
- .generateRefreshToken().build();
- return res;
+ .generateIDToken();
+
+ // Don't generate refresh token again if refresh was triggered with offline token
+ if (!refreshToken.getType().equals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE)) {
+ responseBuilder.generateRefreshToken();
+ }
+
+ AccessTokenResponse res = responseBuilder.build();
+ return new RefreshResult(res, RefreshTokenUtil.TOKEN_TYPE_OFFLINE.equals(refreshToken.getType()));
}
public RefreshToken verifyRefreshToken(RealmModel realm, String encodedRefreshToken) throws OAuthErrorException {
@@ -158,7 +186,7 @@ public class TokenManager {
} catch (Exception e) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", e);
}
- if (refreshToken.isExpired()) {
+ if (refreshToken.getExpiration() != 0 && refreshToken.isExpired()) {
throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired");
}
@@ -172,18 +200,18 @@ public class TokenManager {
IDToken idToken = null;
try {
if (!RSAProvider.verify(jws, realm.getPublicKey())) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token");
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid IDToken");
}
idToken = jws.readJsonContent(IDToken.class);
} catch (IOException e) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid refresh token", e);
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Invalid IDToken", e);
}
if (idToken.isExpired()) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Refresh token expired");
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "IDToken expired");
}
if (idToken.getIssuedAt() < realm.getNotBefore()) {
- throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale refresh token");
+ throw new OAuthErrorException(OAuthErrorException.INVALID_GRANT, "Stale IDToken");
}
return idToken;
}
@@ -206,7 +234,8 @@ public class TokenManager {
clientSession.setUserSession(session);
Set<String> requestedRoles = new HashSet<String>();
// todo scope param protocol independent
- for (RoleModel r : TokenManager.getAccess(null, clientSession.getClient(), user)) {
+ String scopeParam = clientSession.getNote(OAuth2Constants.SCOPE);
+ for (RoleModel r : TokenManager.getAccess(scopeParam, true, clientSession.getClient(), user)) {
requestedRoles.add(r.getId());
}
clientSession.setRoles(requestedRoles);
@@ -242,28 +271,62 @@ public class TokenManager {
}
}
- public static Set<RoleModel> getAccess(String scopeParam, ClientModel client, UserModel user) {
- // todo scopeParam is ignored until we figure out a scheme that fits with openid connect
+ public static Set<RoleModel> getAccess(String scopeParam, boolean applyScopeParam, ClientModel client, UserModel user) {
Set<RoleModel> requestedRoles = new HashSet<RoleModel>();
Set<RoleModel> roleMappings = user.getRoleMappings();
- if (client.isFullScopeAllowed()) return roleMappings;
- Set<RoleModel> scopeMappings = client.getScopeMappings();
- if (client instanceof ClientModel) {
- scopeMappings.addAll(((ClientModel) client).getRoles());
+ if (client.isFullScopeAllowed()) {
+ requestedRoles = roleMappings;
+ } else {
+
+ Set<RoleModel> scopeMappings = client.getScopeMappings();
+ scopeMappings.addAll(client.getRoles());
+
+ for (RoleModel role : roleMappings) {
+ for (RoleModel desiredRole : scopeMappings) {
+ Set<RoleModel> visited = new HashSet<RoleModel>();
+ applyScope(role, desiredRole, visited, requestedRoles);
+ }
+ }
}
- for (RoleModel role : roleMappings) {
- for (RoleModel desiredRole : scopeMappings) {
- Set<RoleModel> visited = new HashSet<RoleModel>();
- applyScope(role, desiredRole, visited, requestedRoles);
+ if (applyScopeParam) {
+ Collection<String> scopeParamRoles;
+ if (scopeParam != null) {
+ String[] scopes = scopeParam.split(" ");
+ scopeParamRoles = Arrays.asList(scopes);
+ } else {
+ scopeParamRoles = Collections.emptyList();
+ }
+
+ Set<RoleModel> roles = new HashSet<>();
+ for (RoleModel role : requestedRoles) {
+ String roleName = getRoleNameForScopeParam(role);
+ if (!role.isScopeParamRequired() || scopeParamRoles.contains(roleName)) {
+ roles.add(role);
+ } else {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Role '%s' excluded by scope param. Client is '%s', User is '%s', Scope param is '%s' ", role.getName(), client.getClientId(), user.getUsername(), scopeParam);
+ }
+ }
}
+ requestedRoles = roles;
}
return requestedRoles;
}
+ // For now, just use "roleName" for realm roles and "clientId/roleName" for client roles
+ private static String getRoleNameForScopeParam(RoleModel role) {
+ if (role.getContainer() instanceof RealmModel) {
+ return role.getName();
+ } else {
+ ClientModel client = (ClientModel) role.getContainer();
+ return client.getClientId() + "/" + role.getName();
+ }
+ }
+
public void verifyAccess(AccessToken token, AccessToken newToken) throws OAuthErrorException {
if (token.getRealmAccess() != null) {
if (newToken.getRealmAccess() == null) throw new OAuthErrorException(OAuthErrorException.INVALID_SCOPE, "User no long has permission for realm roles");
@@ -409,8 +472,10 @@ public class TokenManager {
return this;
}
- public AccessTokenResponseBuilder generateAccessToken(KeycloakSession session, String scopeParam, ClientModel client, UserModel user, UserSessionModel userSession, ClientSessionModel clientSession) {
- Set<RoleModel> requestedRoles = getAccess(scopeParam, client, user);
+ public AccessTokenResponseBuilder generateAccessToken() {
+ UserModel user = userSession.getUser();
+ String scopeParam = clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM);
+ Set<RoleModel> requestedRoles = getAccess(scopeParam, true, client, user);
accessToken = createClientAccessToken(session, requestedRoles, realm, client, user, userSession, clientSession);
return this;
}
@@ -419,10 +484,24 @@ public class TokenManager {
if (accessToken == null) {
throw new IllegalStateException("accessToken not set");
}
- refreshToken = new RefreshToken(accessToken);
+
+ String scopeParam = clientSession.getNote(OIDCLoginProtocol.SCOPE_PARAM);
+ boolean offlineTokenRequested = RefreshTokenUtil.isOfflineTokenRequested(scopeParam);
+ if (offlineTokenRequested) {
+ if (!OfflineTokenUtils.isOfflineTokenAllowed(realm, clientSession)) {
+ event.error(Errors.NOT_ALLOWED);
+ throw new ErrorResponseException("not_allowed", "Offline tokens not allowed for the user or client", Response.Status.BAD_REQUEST);
+ }
+
+ refreshToken = new RefreshToken(accessToken);
+ refreshToken.type(RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
+ OfflineTokenUtils.persistOfflineSession(clientSession, userSession);
+ } else {
+ refreshToken = new RefreshToken(accessToken);
+ refreshToken.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
+ }
refreshToken.id(KeycloakModelUtils.generateId());
refreshToken.issuedNow();
- refreshToken.expiration(Time.currentTime() + realm.getSsoSessionIdleTimeout());
return this;
}
@@ -459,6 +538,7 @@ public class TokenManager {
} else {
event.detail(Details.REFRESH_TOKEN_ID, refreshToken.getId());
}
+ event.detail(Details.REFRESH_TOKEN_TYPE, refreshToken.getType());
}
AccessTokenResponse res = new AccessTokenResponse();
@@ -489,4 +569,23 @@ public class TokenManager {
}
}
+ public class RefreshResult {
+
+ private final AccessTokenResponse response;
+ private final boolean offlineToken;
+
+ private RefreshResult(AccessTokenResponse response, boolean offlineToken) {
+ this.response = response;
+ this.offlineToken = offlineToken;
+ }
+
+ public AccessTokenResponse getResponse() {
+ return response;
+ }
+
+ public boolean isOfflineToken() {
+ return offlineToken;
+ }
+ }
+
}
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index fb0578e..958cd3e 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -27,6 +27,7 @@ import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.OAuthClientRepresentation;
import org.keycloak.representations.idm.RealmEventsConfigRepresentation;
import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
import org.keycloak.timer.TimerProvider;
import java.util.Collections;
@@ -95,6 +96,7 @@ public class RealmManager implements RealmImporter {
setupImpersonationService(realm);
setupAuthenticationFlows(realm);
setupRequiredActions(realm);
+ setupOfflineTokens(realm);
return realm;
}
@@ -107,6 +109,10 @@ public class RealmManager implements RealmImporter {
if (realm.getRequiredActionProviders().size() == 0) DefaultRequiredActions.addActions(realm);
}
+ protected void setupOfflineTokens(RealmModel realm) {
+ KeycloakModelUtils.setupOfflineTokens(realm);
+ }
+
protected void setupAdminConsole(RealmModel realm) {
ClientModel adminConsole = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
if (adminConsole == null) adminConsole = new ClientManager(this).createClient(realm, Constants.ADMIN_CONSOLE_CLIENT_ID);
@@ -216,12 +222,14 @@ public class RealmManager implements RealmImporter {
RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM);
adminRole.addCompositeRole(createRealmRole);
- createRealmRole.setDescription("${role_"+AdminRoles.CREATE_REALM+"}");
+ createRealmRole.setDescription("${role_" + AdminRoles.CREATE_REALM + "}");
+ createRealmRole.setScopeParamRequired(false);
} else {
adminRealm = model.getRealmByName(Config.getAdminRealm());
adminRole = adminRealm.getRole(AdminRoles.ADMIN);
}
adminRole.setDescription("${role_"+AdminRoles.ADMIN+"}");
+ adminRole.setScopeParamRequired(false);
ClientModel realmAdminApp = KeycloakModelUtils.createClient(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationClientId(realm.getName()));
// No localized name for now
@@ -232,6 +240,7 @@ public class RealmManager implements RealmImporter {
for (String r : AdminRoles.ALL_REALM_ROLES) {
RoleModel role = realmAdminApp.addRole(r);
role.setDescription("${role_"+r+"}");
+ role.setScopeParamRequired(false);
adminRole.addCompositeRole(role);
}
}
@@ -249,12 +258,14 @@ public class RealmManager implements RealmImporter {
}
RoleModel adminRole = realmAdminClient.addRole(AdminRoles.REALM_ADMIN);
adminRole.setDescription("${role_" + AdminRoles.REALM_ADMIN + "}");
+ adminRole.setScopeParamRequired(false);
realmAdminClient.setBearerOnly(true);
realmAdminClient.setFullScopeAllowed(false);
for (String r : AdminRoles.ALL_REALM_ROLES) {
RoleModel role = realmAdminClient.addRole(r);
role.setDescription("${role_"+r+"}");
+ role.setScopeParamRequired(false);
adminRole.addCompositeRole(role);
}
}
@@ -274,7 +285,9 @@ public class RealmManager implements RealmImporter {
for (String role : AccountRoles.ALL) {
client.addDefaultRole(role);
- client.getRole(role).setDescription("${role_"+role+"}");
+ RoleModel roleModel = client.getRole(role);
+ roleModel.setDescription("${role_" + role + "}");
+ roleModel.setScopeParamRequired(false);
}
}
}
@@ -292,7 +305,9 @@ public class RealmManager implements RealmImporter {
client.setFullScopeAllowed(false);
for (String role : Constants.BROKER_SERVICE_ROLES) {
- client.addRole(role).setDescription("${role_"+ role.toLowerCase().replaceAll("_", "-") +"}");
+ RoleModel roleModel = client.addRole(role);
+ roleModel.setDescription("${role_"+ role.toLowerCase().replaceAll("_", "-") +"}");
+ roleModel.setScopeParamRequired(false);
}
}
}
@@ -329,6 +344,7 @@ public class RealmManager implements RealmImporter {
if (!hasBrokerClient(rep)) setupBrokerService(realm);
if (!hasAdminConsoleClient(rep)) setupAdminConsole(realm);
+ if (!hasRealmRole(rep, Constants.OFFLINE_ACCESS_ROLE)) setupOfflineTokens(realm);
RepresentationToModel.importRealm(session, rep, realm);
@@ -409,6 +425,20 @@ public class RealmManager implements RealmImporter {
return false;
}
+ private boolean hasRealmRole(RealmRepresentation rep, String roleName) {
+ if (rep.getRoles() == null || rep.getRoles().getRealm() == null) {
+ return false;
+ }
+
+ for (RoleRepresentation role : rep.getRoles().getRealm()) {
+ if (roleName.equals(role.getName())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
/**
* Query users based on a search string:
* <p/>
diff --git a/services/src/main/java/org/keycloak/services/offline/OfflineClientSessionAdapter.java b/services/src/main/java/org/keycloak/services/offline/OfflineClientSessionAdapter.java
new file mode 100644
index 0000000..50abfd4
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/offline/OfflineClientSessionAdapter.java
@@ -0,0 +1,289 @@
+package org.keycloak.services.offline;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineClientSessionAdapter implements ClientSessionModel {
+
+ private final OfflineClientSessionModel model;
+ private final RealmModel realm;
+ private final ClientModel client;
+ private final OfflineUserSessionAdapter userSession;
+
+ private OfflineClientSessionData data;
+
+ public OfflineClientSessionAdapter(OfflineClientSessionModel model, RealmModel realm, ClientModel client, OfflineUserSessionAdapter userSession) {
+ this.model = model;
+ this.realm = realm;
+ this.client = client;
+ this.userSession = userSession;
+ }
+
+ // lazily init representation
+ private OfflineClientSessionData getData() {
+ if (data == null) {
+ try {
+ data = JsonSerialization.readValue(model.getData(), OfflineClientSessionData.class);
+ } catch (IOException ioe) {
+ throw new ModelException(ioe);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ public String getId() {
+ return model.getClientSessionId();
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ @Override
+ public ClientModel getClient() {
+ return client;
+ }
+
+ @Override
+ public UserSessionModel getUserSession() {
+ return userSession;
+ }
+
+ @Override
+ public void setUserSession(UserSessionModel userSession) {
+ throw new IllegalStateException("Not supported setUserSession");
+ }
+
+ @Override
+ public String getRedirectUri() {
+ return data.getRedirectUri();
+ }
+
+ @Override
+ public void setRedirectUri(String uri) {
+ throw new IllegalStateException("Not supported setRedirectUri");
+ }
+
+ @Override
+ public int getTimestamp() {
+ return 0;
+ }
+
+ @Override
+ public void setTimestamp(int timestamp) {
+ throw new IllegalStateException("Not supported setTimestamp");
+ }
+
+ @Override
+ public String getAction() {
+ return null;
+ }
+
+ @Override
+ public void setAction(String action) {
+ throw new IllegalStateException("Not supported setAction");
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ return getData().getRoles();
+ }
+
+ @Override
+ public void setRoles(Set<String> roles) {
+ throw new IllegalStateException("Not supported setRoles");
+ }
+
+ @Override
+ public Set<String> getProtocolMappers() {
+ return getData().getProtocolMappers();
+ }
+
+ @Override
+ public void setProtocolMappers(Set<String> protocolMappers) {
+ throw new IllegalStateException("Not supported setProtocolMappers");
+ }
+
+ @Override
+ public Map<String, ExecutionStatus> getExecutionStatus() {
+ return getData().getAuthenticatorStatus();
+ }
+
+ @Override
+ public void setExecutionStatus(String authenticator, ExecutionStatus status) {
+ throw new IllegalStateException("Not supported setExecutionStatus");
+ }
+
+ @Override
+ public void clearExecutionStatus() {
+ throw new IllegalStateException("Not supported clearExecutionStatus");
+ }
+
+ @Override
+ public UserModel getAuthenticatedUser() {
+ return userSession.getUser();
+ }
+
+ @Override
+ public void setAuthenticatedUser(UserModel user) {
+ throw new IllegalStateException("Not supported setAuthenticatedUser");
+ }
+
+ @Override
+ public String getAuthMethod() {
+ return getData().getAuthMethod();
+ }
+
+ @Override
+ public void setAuthMethod(String method) {
+ throw new IllegalStateException("Not supported setAuthMethod");
+ }
+
+ @Override
+ public String getNote(String name) {
+ return getData().getNotes()==null ? null : getData().getNotes().get(name);
+ }
+
+ @Override
+ public void setNote(String name, String value) {
+ throw new IllegalStateException("Not supported setNote");
+ }
+
+ @Override
+ public void removeNote(String name) {
+ throw new IllegalStateException("Not supported removeNote");
+ }
+
+ @Override
+ public Map<String, String> getNotes() {
+ return getData().getNotes();
+ }
+
+ @Override
+ public Set<String> getRequiredActions() {
+ throw new IllegalStateException("Not supported getRequiredActions");
+ }
+
+ @Override
+ public void addRequiredAction(String action) {
+ throw new IllegalStateException("Not supported addRequiredAction");
+ }
+
+ @Override
+ public void removeRequiredAction(String action) {
+ throw new IllegalStateException("Not supported removeRequiredAction");
+ }
+
+ @Override
+ public void addRequiredAction(UserModel.RequiredAction action) {
+ throw new IllegalStateException("Not supported addRequiredAction");
+ }
+
+ @Override
+ public void removeRequiredAction(UserModel.RequiredAction action) {
+ throw new IllegalStateException("Not supported removeRequiredAction");
+ }
+
+ @Override
+ public void setUserSessionNote(String name, String value) {
+ throw new IllegalStateException("Not supported setUserSessionNote");
+ }
+
+ @Override
+ public Map<String, String> getUserSessionNotes() {
+ throw new IllegalStateException("Not supported getUserSessionNotes");
+ }
+
+ @Override
+ public void clearUserSessionNotes() {
+ throw new IllegalStateException("Not supported clearUserSessionNotes");
+ }
+
+ protected static class OfflineClientSessionData {
+
+ @JsonProperty("authMethod")
+ private String authMethod;
+
+ @JsonProperty("redirectUri")
+ private String redirectUri;
+
+ @JsonProperty("protocolMappers")
+ private Set<String> protocolMappers;
+
+ @JsonProperty("roles")
+ private Set<String> roles;
+
+ @JsonProperty("notes")
+ private Map<String, String> notes;
+
+ @JsonProperty("authenticatorStatus")
+ private Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus = new HashMap<>();
+
+ public String getAuthMethod() {
+ return authMethod;
+ }
+
+ public void setAuthMethod(String authMethod) {
+ this.authMethod = authMethod;
+ }
+
+ public String getRedirectUri() {
+ return redirectUri;
+ }
+
+ public void setRedirectUri(String redirectUri) {
+ this.redirectUri = redirectUri;
+ }
+
+ public Set<String> getProtocolMappers() {
+ return protocolMappers;
+ }
+
+ public void setProtocolMappers(Set<String> protocolMappers) {
+ this.protocolMappers = protocolMappers;
+ }
+
+ public Set<String> getRoles() {
+ return roles;
+ }
+
+ public void setRoles(Set<String> roles) {
+ this.roles = roles;
+ }
+
+ public Map<String, String> getNotes() {
+ return notes;
+ }
+
+ public void setNotes(Map<String, String> notes) {
+ this.notes = notes;
+ }
+
+ public Map<String, ClientSessionModel.ExecutionStatus> getAuthenticatorStatus() {
+ return authenticatorStatus;
+ }
+
+ public void setAuthenticatorStatus(Map<String, ClientSessionModel.ExecutionStatus> authenticatorStatus) {
+ this.authenticatorStatus = authenticatorStatus;
+ }
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/offline/OfflineTokenUtils.java b/services/src/main/java/org/keycloak/services/offline/OfflineTokenUtils.java
new file mode 100644
index 0000000..b4900c1
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/offline/OfflineTokenUtils.java
@@ -0,0 +1,191 @@
+package org.keycloak.services.offline;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import org.jboss.logging.Logger;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * TODO: Change to utils?
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineTokenUtils {
+
+ protected static Logger logger = Logger.getLogger(OfflineTokenUtils.class);
+
+ public static void persistOfflineSession(ClientSessionModel clientSession, UserSessionModel userSession) {
+ UserModel user = userSession.getUser();
+ ClientModel client = clientSession.getClient();
+
+ // First verify if we already have offlineToken for this user+client . If yes, then invalidate it (This is to avoid leaks)
+ Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
+ for (OfflineClientSessionModel existing : clientSessions) {
+ if (existing.getClientId().equals(client.getId())) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Removing existing offline token for user '%s' and client '%s' . ClientSessionID was '%s' . Offline token will be replaced with new one",
+ user.getUsername(), client.getClientId(), existing.getClientSessionId());
+ }
+
+ user.removeOfflineClientSession(existing.getClientSessionId());
+
+ // Check if userSession is ours. If not, then check if it has other clientSessions and remove it otherwise
+ if (!existing.getUserSessionId().equals(userSession.getId())) {
+ checkUserSessionHasClientSessions(user, existing.getUserSessionId());
+ }
+ }
+ }
+
+ // Verify if we already have UserSession with this ID. If yes, don't create another one
+ OfflineUserSessionModel userSessionRep = user.getOfflineUserSession(userSession.getId());
+ if (userSessionRep == null) {
+ createOfflineUserSession(user, userSession);
+ }
+
+ // Create clientRep and save to DB.
+ createOfflineClientSession(user, clientSession, userSession);
+ }
+
+ // userSessionId is provided from offline token. It's used just to verify if it match the ID from clientSession representation
+ public static ClientSessionModel findOfflineClientSession(RealmModel realm, UserModel user, String clientSessionId, String userSessionId) {
+ OfflineClientSessionModel clientSession = user.getOfflineClientSession(clientSessionId);
+ if (clientSession == null) {
+ return null;
+ }
+
+ if (!userSessionId.equals(clientSession.getUserSessionId())) {
+ throw new ModelException("User session don't match. Offline client session " + clientSession.getClientSessionId() + ", It's user session " + clientSession.getUserSessionId() +
+ " Wanted user session: " + userSessionId);
+ }
+
+ OfflineUserSessionModel userSession = user.getOfflineUserSession(userSessionId);
+ if (userSession == null) {
+ throw new ModelException("Found clientSession " + clientSessionId + " but not userSession " + userSessionId);
+ }
+
+ OfflineUserSessionAdapter userSessionAdapter = new OfflineUserSessionAdapter(userSession, user);
+
+ ClientModel client = realm.getClientById(clientSession.getClientId());
+ OfflineClientSessionAdapter clientSessionAdapter = new OfflineClientSessionAdapter(clientSession, realm, client, userSessionAdapter);
+
+ return clientSessionAdapter;
+ }
+
+ public static Set<ClientModel> findClientsWithOfflineToken(RealmModel realm, UserModel user) {
+ Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
+ Set<ClientModel> clients = new HashSet<>();
+ for (OfflineClientSessionModel clientSession : clientSessions) {
+ ClientModel client = realm.getClientById(clientSession.getClientId());
+ clients.add(client);
+ }
+ return clients;
+ }
+
+ public static boolean revokeOfflineToken(UserModel user, ClientModel client) {
+ Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
+ boolean anyRemoved = false;
+ for (OfflineClientSessionModel clientSession : clientSessions) {
+ if (clientSession.getClientId().equals(client.getId())) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Removing existing offline token for user '%s' and client '%s' . ClientSessionID was '%s' .",
+ user.getUsername(), client.getClientId(), clientSession.getClientSessionId());
+ }
+
+ user.removeOfflineClientSession(clientSession.getClientSessionId());
+ checkUserSessionHasClientSessions(user, clientSession.getUserSessionId());
+ anyRemoved = true;
+ }
+ }
+
+ return anyRemoved;
+ }
+
+ public static boolean isOfflineTokenAllowed(RealmModel realm, ClientSessionModel clientSession) {
+ RoleModel offlineAccessRole = realm.getRole(Constants.OFFLINE_ACCESS_ROLE);
+ if (offlineAccessRole == null) {
+ logger.warnf("Role '%s' not available in realm", Constants.OFFLINE_ACCESS_ROLE);
+ return false;
+ }
+
+ return clientSession.getRoles().contains(offlineAccessRole.getId());
+ }
+
+ private static void createOfflineUserSession(UserModel user, UserSessionModel userSession) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Creating new offline user session. UserSessionID: '%s' , Username: '%s'", userSession.getId(), user.getUsername());
+ }
+ OfflineUserSessionAdapter.OfflineUserSessionData rep = new OfflineUserSessionAdapter.OfflineUserSessionData();
+ rep.setBrokerUserId(userSession.getBrokerUserId());
+ rep.setBrokerSessionId(userSession.getBrokerSessionId());
+ rep.setIpAddress(userSession.getIpAddress());
+ rep.setAuthMethod(userSession.getAuthMethod());
+ rep.setRememberMe(userSession.isRememberMe());
+ rep.setStarted(userSession.getStarted());
+ rep.setNotes(userSession.getNotes());
+
+ try {
+ String stringRep = JsonSerialization.writeValueAsString(rep);
+ OfflineUserSessionModel sessionModel = new OfflineUserSessionModel();
+ sessionModel.setUserSessionId(userSession.getId());
+ sessionModel.setData(stringRep);
+ user.addOfflineUserSession(sessionModel);
+ } catch (IOException ioe) {
+ throw new ModelException(ioe);
+ }
+ }
+
+ private static void createOfflineClientSession(UserModel user, ClientSessionModel clientSession, UserSessionModel userSession) {
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Creating new offline token client session. ClientSessionId: '%s', UserSessionID: '%s' , Username: '%s', Client: '%s'" ,
+ clientSession.getId(), userSession.getId(), user.getUsername(), clientSession.getClient().getClientId());
+ }
+ OfflineClientSessionAdapter.OfflineClientSessionData rep = new OfflineClientSessionAdapter.OfflineClientSessionData();
+ rep.setAuthMethod(clientSession.getAuthMethod());
+ rep.setRedirectUri(clientSession.getRedirectUri());
+ rep.setProtocolMappers(clientSession.getProtocolMappers());
+ rep.setRoles(clientSession.getRoles());
+ rep.setNotes(clientSession.getNotes());
+ rep.setAuthenticatorStatus(clientSession.getExecutionStatus());
+
+ try {
+ String stringRep = JsonSerialization.writeValueAsString(rep);
+ OfflineClientSessionModel clsModel = new OfflineClientSessionModel();
+ clsModel.setClientSessionId(clientSession.getId());
+ clsModel.setClientId(clientSession.getClient().getId());
+ clsModel.setUserSessionId(userSession.getId());
+ clsModel.setData(stringRep);
+ user.addOfflineClientSession(clsModel);
+ } catch (IOException ioe) {
+ throw new ModelException(ioe);
+ }
+ }
+
+ // Check if userSession has any offline clientSessions attached to it. Remove userSession if not
+ private static void checkUserSessionHasClientSessions(UserModel user, String userSessionId) {
+ Collection<OfflineClientSessionModel> clientSessions = user.getOfflineClientSessions();
+
+ for (OfflineClientSessionModel clientSession : clientSessions) {
+ if (clientSession.getUserSessionId().equals(userSessionId)) {
+ return;
+ }
+ }
+
+ if (logger.isTraceEnabled()) {
+ logger.tracef("Removing offline userSession for user %s as it doesn't have any client sessions attached. UserSessionID: %s", user.getUsername(), userSessionId);
+ }
+ user.removeOfflineUserSession(userSessionId);
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/offline/OfflineUserSessionAdapter.java b/services/src/main/java/org/keycloak/services/offline/OfflineUserSessionAdapter.java
new file mode 100644
index 0000000..bd01d38
--- /dev/null
+++ b/services/src/main/java/org/keycloak/services/offline/OfflineUserSessionAdapter.java
@@ -0,0 +1,215 @@
+package org.keycloak.services.offline;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Map;
+
+import org.codehaus.jackson.annotate.JsonProperty;
+import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.ModelException;
+import org.keycloak.models.OfflineUserSessionModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.UserSessionModel;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineUserSessionAdapter implements UserSessionModel {
+
+ private final OfflineUserSessionModel model;
+ private final UserModel user;
+
+ private OfflineUserSessionData data;
+
+ public OfflineUserSessionAdapter(OfflineUserSessionModel model, UserModel user) {
+ this.model = model;
+ this.user = user;
+ }
+
+ // lazily init representation
+ private OfflineUserSessionData getData() {
+ if (data == null) {
+ try {
+ data = JsonSerialization.readValue(model.getData(), OfflineUserSessionData.class);
+ } catch (IOException ioe) {
+ throw new ModelException(ioe);
+ }
+ }
+
+ return data;
+ }
+
+ @Override
+ public String getId() {
+ return model.getUserSessionId();
+ }
+
+ @Override
+ public String getBrokerSessionId() {
+ return getData().getBrokerSessionId();
+ }
+
+ @Override
+ public String getBrokerUserId() {
+ return getData().getBrokerUserId();
+ }
+
+ @Override
+ public UserModel getUser() {
+ return user;
+ }
+
+ @Override
+ public String getLoginUsername() {
+ return user.getUsername();
+ }
+
+ @Override
+ public String getIpAddress() {
+ return getData().getIpAddress();
+ }
+
+ @Override
+ public String getAuthMethod() {
+ return getData().getAuthMethod();
+ }
+
+ @Override
+ public boolean isRememberMe() {
+ return getData().isRememberMe();
+ }
+
+ @Override
+ public int getStarted() {
+ return getData().getStarted();
+ }
+
+ @Override
+ public int getLastSessionRefresh() {
+ return 0;
+ }
+
+ @Override
+ public void setLastSessionRefresh(int seconds) {
+ // Ignore
+ }
+
+ @Override
+ public List<ClientSessionModel> getClientSessions() {
+ throw new IllegalStateException("Not yet supported");
+ }
+
+ @Override
+ public String getNote(String name) {
+ return getData().getNotes()==null ? null : getData().getNotes().get(name);
+ }
+
+ @Override
+ public void setNote(String name, String value) {
+ throw new IllegalStateException("Illegal to set note offline session");
+
+ }
+
+ @Override
+ public void removeNote(String name) {
+ throw new IllegalStateException("Illegal to remove note from offline session");
+ }
+
+ @Override
+ public Map<String, String> getNotes() {
+ return getData().getNotes();
+ }
+
+ @Override
+ public State getState() {
+ return null;
+ }
+
+ @Override
+ public void setState(State state) {
+ throw new IllegalStateException("Illegal to set state on offline session");
+ }
+
+
+ protected static class OfflineUserSessionData {
+
+ @JsonProperty("brokerSessionId")
+ private String brokerSessionId;
+
+ @JsonProperty("brokerUserId")
+ private String brokerUserId;
+
+ @JsonProperty("ipAddress")
+ private String ipAddress;
+
+ @JsonProperty("authMethod")
+ private String authMethod;
+
+ @JsonProperty("rememberMe")
+ private boolean rememberMe;
+
+ @JsonProperty("started")
+ private int started;
+
+ @JsonProperty("notes")
+ private Map<String, String> notes;
+
+ public String getBrokerSessionId() {
+ return brokerSessionId;
+ }
+
+ public void setBrokerSessionId(String brokerSessionId) {
+ this.brokerSessionId = brokerSessionId;
+ }
+
+ public String getBrokerUserId() {
+ return brokerUserId;
+ }
+
+ public void setBrokerUserId(String brokerUserId) {
+ this.brokerUserId = brokerUserId;
+ }
+
+ public String getIpAddress() {
+ return ipAddress;
+ }
+
+ public void setIpAddress(String ipAddress) {
+ this.ipAddress = ipAddress;
+ }
+
+ public String getAuthMethod() {
+ return authMethod;
+ }
+
+ public void setAuthMethod(String authMethod) {
+ this.authMethod = authMethod;
+ }
+
+ public boolean isRememberMe() {
+ return rememberMe;
+ }
+
+ public void setRememberMe(boolean rememberMe) {
+ this.rememberMe = rememberMe;
+ }
+
+ public int getStarted() {
+ return started;
+ }
+
+ public void setStarted(int started) {
+ this.started = started;
+ }
+
+ public Map<String, String> getNotes() {
+ return notes;
+ }
+
+ public void setNotes(Map<String, String> notes) {
+ this.notes = notes;
+ }
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/resources/AccountService.java b/services/src/main/java/org/keycloak/services/resources/AccountService.java
index 1756f12..3b6edea 100755
--- a/services/src/main/java/org/keycloak/services/resources/AccountService.java
+++ b/services/src/main/java/org/keycloak/services/resources/AccountService.java
@@ -46,7 +46,6 @@ import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.CredentialValidation;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.models.utils.ModelToRepresentation;
-import org.keycloak.models.utils.TimeBasedOTP;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.utils.RedirectUtils;
import org.keycloak.representations.idm.CredentialRepresentation;
@@ -58,6 +57,7 @@ import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
+import org.keycloak.services.offline.OfflineTokenUtils;
import org.keycloak.services.util.ResolveRelative;
import org.keycloak.services.validation.Validation;
import org.keycloak.util.UriUtils;
@@ -486,6 +486,7 @@ public class AccountService extends AbstractSecuredLocalService {
// Revoke grant in UserModel
UserModel user = auth.getUser();
user.revokeConsentForClient(client.getId());
+ OfflineTokenUtils.revokeOfflineToken(user, client);
// Logout clientSessions for this user and client
AuthenticationManager.backchannelUserFromClient(session, realm, user, client, uriInfo, headers);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
index bab74d6..fda0ee3 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmAdminResource.java
@@ -111,7 +111,7 @@ public class RealmAdminResource {
* @return
*/
@Path("attack-detection")
- public AttackDetectionResource getClientImporter() {
+ public AttackDetectionResource getAttackDetection() {
AttackDetectionResource resource = new AttackDetectionResource(auth, realm, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource);
return resource;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
index d0fb521..18e6df5 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RealmsAdminResource.java
@@ -144,51 +144,6 @@ public class RealmsAdminResource {
}
}
- /**
- * Import a realm from uploaded JSON file
- *
- * The posted represenation is expected to be a multipart/form-data encapsulation
- * of a JSON file. The same format a browser would use when uploading a file.
- *
- * @param uriInfo
- * @param input multipart/form data
- * @return
- * @throws IOException
- */
- @POST
- @Consumes(MediaType.MULTIPART_FORM_DATA)
- public Response uploadRealm(@Context final UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
- RealmManager realmManager = new RealmManager(session);
- realmManager.setContextPath(keycloak.getContextPath());
- if (!auth.getRealm().equals(realmManager.getKeycloakAdminstrationRealm())) {
- throw new ForbiddenException();
- }
- if (!auth.hasRealmRole(AdminRoles.CREATE_REALM)) {
- throw new ForbiddenException();
- }
-
- Map<String, List<InputPart>> uploadForm = input.getFormDataMap();
- List<InputPart> inputParts = uploadForm.get("file");
- RealmRepresentation rep = null;
-
- for (InputPart inputPart : inputParts) {
- // inputPart.getBody doesn't work as content-type is wrong, and inputPart.setMediaType is not supported on AS7 (RestEasy 2.3.2.Final)
- rep = JsonSerialization.readValue(inputPart.getBodyAsString(), RealmRepresentation.class);
-
- RealmModel realm = realmManager.importRealm(rep);
-
- grantPermissionsToRealmCreator(realm);
-
- URI location = null;
- if (inputParts.size() == 1) {
- location = AdminRoot.realmsUrl(uriInfo).path(realm.getName()).build();
- return Response.created(location).build();
- }
- }
-
- return Response.noContent().build();
- }
-
private void grantPermissionsToRealmCreator(RealmModel realm) {
if (auth.hasRealmRole(AdminRoles.ADMIN)) {
return;
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
index 168ff47..879ff6f 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleContainerResource.java
@@ -82,6 +82,8 @@ public class RoleContainerResource extends RoleResource {
try {
RoleModel role = roleContainer.addRole(rep.getName());
role.setDescription(rep.getDescription());
+ boolean scopeParamRequired = rep.isScopeParamRequired()==null ? false : rep.isScopeParamRequired();
+ role.setScopeParamRequired(scopeParamRequired);
adminEvent.operation(OperationType.CREATE).resourcePath(uriInfo, role.getId()).representation(rep).success();
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
index 827b817..3635da8 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/RoleResource.java
@@ -38,6 +38,7 @@ public abstract class RoleResource {
protected void updateRole(RoleRepresentation rep, RoleModel role) {
role.setName(rep.getName());
role.setDescription(rep.getDescription());
+ if (rep.isScopeParamRequired() != null) role.setScopeParamRequired(rep.isScopeParamRequired());
}
protected void addComposites(AdminEventBuilder adminEvent, UriInfo uriInfo, List<RoleRepresentation> roles, RoleModel role) {
testsuite/integration/pom.xml 2(+1 -1)
diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml
index 166fa4b..599d0b2 100755
--- a/testsuite/integration/pom.xml
+++ b/testsuite/integration/pom.xml
@@ -151,7 +151,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
index 95d644e..9458998 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -646,7 +646,7 @@ public class AccountTest {
}
}
- // More tests (including revoke) are in OAuthGrantTest
+ // More tests (including revoke) are in OAuthGrantTest and OfflineTokenTest
@Test
public void applications() {
applicationsPage.open();
@@ -668,7 +668,8 @@ public class AccountTest {
Assert.assertTrue(accountEntry.getProtocolMappersGranted().contains("Full Access"));
AccountApplicationsPage.AppEntry testAppEntry = apps.get("test-app");
- Assert.assertEquals(4, testAppEntry.getRolesAvailable().size());
+ Assert.assertEquals(5, testAppEntry.getRolesAvailable().size());
+ Assert.assertTrue(testAppEntry.getRolesAvailable().contains("Offline access"));
Assert.assertTrue(testAppEntry.getRolesGranted().contains("Full Access"));
Assert.assertTrue(testAppEntry.getProtocolMappersGranted().contains("Full Access"));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
index 868a76c..47b4e1d 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionMultipleActionsTest.java
@@ -33,11 +33,8 @@ import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
-import org.keycloak.testsuite.pages.AppPage;
+import org.keycloak.testsuite.pages.*;
import org.keycloak.testsuite.pages.AppPage.RequestType;
-import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
-import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.KeycloakRule.KeycloakSetup;
import org.keycloak.testsuite.rule.WebResource;
@@ -83,7 +80,7 @@ public class RequiredActionMultipleActionsTest {
protected LoginPasswordUpdatePage changePasswordPage;
@WebResource
- protected LoginUpdateProfilePage updateProfilePage;
+ protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
@Test
public void updateProfileAndPassword() throws Exception {
@@ -121,7 +118,7 @@ public class RequiredActionMultipleActionsTest {
}
public String updateProfile(String sessionId) {
- updateProfilePage.update("New first", "New last", "new@email.com");
+ updateProfilePage.update("New first", "New last", "new@email.com", "test-user@localhost");
AssertEvents.ExpectedEvent expectedEvent = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com");
if (sessionId != null) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
index e3d6ac9..492cd4b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/actions/RequiredActionUpdateProfileTest.java
@@ -21,11 +21,7 @@
*/
package org.keycloak.testsuite.actions;
-import org.junit.Assert;
-import org.junit.Before;
-import org.junit.ClassRule;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.*;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.models.RealmModel;
@@ -36,7 +32,7 @@ import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.pages.LoginUpdateProfilePage;
+import org.keycloak.testsuite.pages.LoginUpdateProfileEditUsernameAllowedPage;
import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule;
@@ -66,7 +62,7 @@ public class RequiredActionUpdateProfileTest {
protected LoginPage loginPage;
@WebResource
- protected LoginUpdateProfilePage updateProfilePage;
+ protected LoginUpdateProfileEditUsernameAllowedPage updateProfilePage;
@Before
public void before() {
@@ -75,6 +71,8 @@ public class RequiredActionUpdateProfileTest {
public void config(RealmManager manager, RealmModel defaultRealm, RealmModel appRealm) {
UserModel user = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
user.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
+ UserModel anotherUser = manager.getSession().users().getUserByEmail("john-doh@localhost", appRealm);
+ anotherUser.addRequiredAction(UserModel.RequiredAction.UPDATE_PROFILE);
}
});
}
@@ -87,7 +85,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("New first", "New last", "new@email.com");
+ updateProfilePage.update("New first", "New last", "new@email.com", "test-user@localhost");
String sessionId = events.expectRequiredAction(EventType.UPDATE_EMAIL).detail(Details.PREVIOUS_EMAIL, "test-user@localhost").detail(Details.UPDATED_EMAIL, "new@email.com").assertEvent().getSessionId();
events.expectRequiredAction(EventType.UPDATE_PROFILE).session(sessionId).assertEvent();
@@ -101,6 +99,41 @@ public class RequiredActionUpdateProfileTest {
Assert.assertEquals("New first", user.getFirstName());
Assert.assertEquals("New last", user.getLastName());
Assert.assertEquals("new@email.com", user.getEmail());
+ Assert.assertEquals("test-user@localhost", user.getUsername());
+ }
+
+ @Test
+ public void updateUsername() {
+ loginPage.open();
+
+ loginPage.login("john-doh@localhost", "password");
+
+ String userId = keycloakRule.getUser("test", "john-doh@localhost").getId();
+
+ updateProfilePage.assertCurrent();
+
+ updateProfilePage.update("New first", "New last", "john-doh@localhost", "new");
+
+ String sessionId = events
+ .expectLogin()
+ .event(EventType.UPDATE_PROFILE)
+ .detail(Details.USERNAME, "john-doh@localhost")
+ .user(userId)
+ .session(AssertEvents.isUUID())
+ .removeDetail(Details.CONSENT)
+ .assertEvent()
+ .getSessionId();
+
+ Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
+
+ events.expectLogin().detail(Details.USERNAME, "john-doh@localhost").user(userId).session(sessionId).assertEvent();
+
+ // assert user is really updated in persistent store
+ UserRepresentation user = keycloakRule.getUser("test", "new");
+ Assert.assertEquals("New first", user.getFirstName());
+ Assert.assertEquals("New last", user.getLastName());
+ Assert.assertEquals("john-doh@localhost", user.getEmail());
+ Assert.assertEquals("new", user.getUsername());
}
@Test
@@ -111,7 +144,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("", "New last", "new@email.com");
+ updateProfilePage.update("", "New last", "new@email.com", "new");
updateProfilePage.assertCurrent();
@@ -133,7 +166,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("New first", "", "new@email.com");
+ updateProfilePage.update("New first", "", "new@email.com", "new");
updateProfilePage.assertCurrent();
@@ -155,7 +188,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("New first", "New last", "");
+ updateProfilePage.update("New first", "New last", "", "new");
updateProfilePage.assertCurrent();
@@ -177,7 +210,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("New first", "New last", "invalidemail");
+ updateProfilePage.update("New first", "New last", "invalidemail", "invalid");
updateProfilePage.assertCurrent();
@@ -192,6 +225,52 @@ public class RequiredActionUpdateProfileTest {
}
@Test
+ public void updateProfileMissingUsername() {
+ loginPage.open();
+
+ loginPage.login("john-doh@localhost", "password");
+
+ updateProfilePage.assertCurrent();
+
+ updateProfilePage.update("New first", "New last", "new@email.com", "");
+
+ updateProfilePage.assertCurrent();
+
+ // assert that form holds submitted values during validation error
+ Assert.assertEquals("New first", updateProfilePage.getFirstName());
+ Assert.assertEquals("New last", updateProfilePage.getLastName());
+ Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
+ Assert.assertEquals("", updateProfilePage.getUsername());
+
+ Assert.assertEquals("Please specify username.", updateProfilePage.getError());
+
+ events.assertEmpty();
+ }
+
+ @Test
+ public void updateProfileDuplicateUsername() {
+ loginPage.open();
+
+ loginPage.login("john-doh@localhost", "password");
+
+ updateProfilePage.assertCurrent();
+
+ updateProfilePage.update("New first", "New last", "new@email.com", "test-user@localhost");
+
+ updateProfilePage.assertCurrent();
+
+ // assert that form holds submitted values during validation error
+ Assert.assertEquals("New first", updateProfilePage.getFirstName());
+ Assert.assertEquals("New last", updateProfilePage.getLastName());
+ Assert.assertEquals("new@email.com", updateProfilePage.getEmail());
+ Assert.assertEquals("test-user@localhost", updateProfilePage.getUsername());
+
+ Assert.assertEquals("Username already exists.", updateProfilePage.getError());
+
+ events.assertEmpty();
+ }
+
+ @Test
public void updateProfileDuplicatedEmail() {
loginPage.open();
@@ -199,7 +278,7 @@ public class RequiredActionUpdateProfileTest {
updateProfilePage.assertCurrent();
- updateProfilePage.update("New first", "New last", "keycloak-user@localhost");
+ updateProfilePage.update("New first", "New last", "keycloak-user@localhost", "test-user@localhost");
updateProfilePage.assertCurrent();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
index bae16d5..aaed86e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/AdminAPITest.java
@@ -85,7 +85,7 @@ public class AdminAPITest {
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/master");
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
- AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
+ AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
index c30ee36..2097175 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ClientTest.java
@@ -108,7 +108,7 @@ public class ClientTest extends AbstractClientTest {
response.close();
String id = ApiUtil.getCreatedId(response);
- RoleRepresentation role = new RoleRepresentation("test", "test");
+ RoleRepresentation role = new RoleRepresentation("test", "test", false);
realm.clients().get(id).roles().create(role);
rep = realm.clients().get(id).toRepresentation();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
index 0de7dd6..0f1510b 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/ImpersonationTest.java
@@ -123,7 +123,7 @@ public class ImpersonationTest {
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/" + realm);
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
- AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
+ AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
index 6cf6427..8208aa2 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/admin/RealmTest.java
@@ -112,7 +112,7 @@ public class RealmTest extends AbstractClientTest {
@Test
// KEYCLOAK-1110
public void deleteDefaultRole() {
- RoleRepresentation role = new RoleRepresentation("test", "test");
+ RoleRepresentation role = new RoleRepresentation("test", "test", false);
realm.roles().create(role);
assertNotNull(realm.roles().get("test").toRepresentation());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
index 88c1ccc..77b6d19 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java
@@ -30,6 +30,7 @@ import org.keycloak.protocol.oidc.endpoints.AuthorizationEndpoint;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.util.RefreshTokenUtil;
import java.util.HashMap;
import java.util.HashSet;
@@ -156,6 +157,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
.detail(Details.CODE_ID, codeId)
.detail(Details.TOKEN_ID, isUUID())
.detail(Details.REFRESH_TOKEN_ID, isUUID())
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
}
@@ -164,6 +166,7 @@ public class AssertEvents implements TestRule, EventListenerProviderFactory {
return expect(EventType.REFRESH_TOKEN)
.detail(Details.TOKEN_ID, isUUID())
.detail(Details.REFRESH_TOKEN_ID, refreshTokenId)
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_REFRESH)
.detail(Details.UPDATED_REFRESH_TOKEN_ID, isUUID())
.detail(Details.CLIENT_AUTH_METHOD, ClientIdAndSecretAuthenticator.PROVIDER_ID)
.session(sessionId);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index fb2b5df..d7084c1 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -17,7 +17,6 @@
*/
package org.keycloak.testsuite.broker;
-import org.codehaus.jackson.map.ObjectMapper;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPTestConfiguration.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPTestConfiguration.java
index 31b8d2b..6e433f2 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPTestConfiguration.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/LDAPTestConfiguration.java
@@ -20,6 +20,7 @@ public class LDAPTestConfiguration {
private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class);
private String connectionPropertiesLocation;
+ private int sleepTime;
private boolean startEmbeddedLdapLerver = true;
private Map<String, String> config;
@@ -109,6 +110,7 @@ public class LDAPTestConfiguration {
}
startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true"));
+ sleepTime = Integer.parseInt(p.getProperty("idm.test.ldap.sleepTime", "1000"));
log.info("Start embedded server: " + startEmbeddedLdapLerver);
log.info("Read config: " + config);
}
@@ -125,4 +127,8 @@ public class LDAPTestConfiguration {
return startEmbeddedLdapLerver;
}
+ public int getSleepTime() {
+ return sleepTime;
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
index f946825..819ee41 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/SyncProvidersTest.java
@@ -56,7 +56,6 @@ public class SyncProvidersTest {
Map<String,String> ldapConfig = ldapRule.getConfig();
ldapConfig.put(LDAPConstants.SYNC_REGISTRATIONS, "false");
ldapConfig.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.WRITABLE.toString());
-
ldapModel = appRealm.addUserFederationProvider(LDAPFederationProviderFactory.PROVIDER_NAME, ldapConfig, 0, "test-ldap",
-1, -1, 0);
@@ -91,7 +90,7 @@ public class SyncProvidersTest {
UsersSyncManager usersSyncManager = new UsersSyncManager();
// wait a bit
- sleep(1000);
+ sleep(ldapRule.getSleepTime());
KeycloakSession session = keycloakRule.startSession();
try {
@@ -125,7 +124,7 @@ public class SyncProvidersTest {
}
// wait a bit
- sleep(1000);
+ sleep(ldapRule.getSleepTime());
// Add user to LDAP and update 'user5' in LDAP
LDAPFederationProvider ldapFedProvider = FederationTestUtils.getLdapProvider(session, ldapModel);
@@ -391,9 +390,9 @@ public class SyncProvidersTest {
}
private void assertSyncEquals(UserFederationSyncResult syncResult, int expectedAdded, int expectedUpdated, int expectedRemoved, int expectedFailed) {
- Assert.assertEquals(syncResult.getAdded(), expectedAdded);
- Assert.assertEquals(syncResult.getUpdated(), expectedUpdated);
- Assert.assertEquals(syncResult.getRemoved(), expectedRemoved);
- Assert.assertEquals(syncResult.getFailed(), expectedFailed);
+ Assert.assertEquals(expectedAdded, syncResult.getAdded());
+ Assert.assertEquals(expectedUpdated, syncResult.getUpdated());
+ Assert.assertEquals(expectedRemoved, syncResult.getRemoved());
+ Assert.assertEquals(expectedFailed, syncResult.getFailed());
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
index a3e0976..d6d8724 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AbstractModelTest.java
@@ -117,6 +117,7 @@ public class AbstractModelTest {
Assert.assertEquals(expected.getId(), actual.getId());
Assert.assertEquals(expected.getName(), actual.getName());
Assert.assertEquals(expected.getDescription(), actual.getDescription());
+ Assert.assertEquals(expected.isScopeParamRequired(), actual.isScopeParamRequired());
Assert.assertEquals(expected.getContainer(), actual.getContainer());
Assert.assertEquals(expected.getComposites().size(), actual.getComposites().size());
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
index 48ed318..4e771df 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/AdapterTest.java
@@ -68,8 +68,8 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertArrayEquals(realmModel.getPrivateKey().getEncoded(), keyPair.getPrivate().getEncoded());
Assert.assertArrayEquals(realmModel.getPublicKey().getEncoded(), keyPair.getPublic().getEncoded());
- Assert.assertEquals(1, realmModel.getDefaultRoles().size());
- Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
+ Assert.assertEquals(2, realmModel.getDefaultRoles().size());
+ Assert.assertTrue(realmModel.getDefaultRoles().contains("foo"));
}
@Test
@@ -94,8 +94,8 @@ public class AdapterTest extends AbstractModelTest {
Assert.assertEquals(realmModel.getName(), "JUGGLER");
Assert.assertArrayEquals(realmModel.getPrivateKey().getEncoded(), keyPair.getPrivate().getEncoded());
Assert.assertArrayEquals(realmModel.getPublicKey().getEncoded(), keyPair.getPublic().getEncoded());
- Assert.assertEquals(1, realmModel.getDefaultRoles().size());
- Assert.assertEquals("foo", realmModel.getDefaultRoles().get(0));
+ Assert.assertEquals(2, realmModel.getDefaultRoles().size());
+ Assert.assertTrue(realmModel.getDefaultRoles().contains("foo"));
realmModel.getId();
@@ -444,7 +444,7 @@ public class AdapterTest extends AbstractModelTest {
realmModel.addRole("admin");
realmModel.addRole("user");
Set<RoleModel> roles = realmModel.getRoles();
- Assert.assertEquals(3, roles.size());
+ Assert.assertEquals(4, roles.size());
UserModel user = realmManager.getSession().users().addUser(realmModel, "bburke");
RoleModel realmUserRole = realmModel.getRole("user");
user.grantRole(realmUserRole);
@@ -470,7 +470,7 @@ public class AdapterTest extends AbstractModelTest {
user.grantRole(application.getRole("user"));
roles = user.getRealmRoleMappings();
- Assert.assertEquals(roles.size(), 2);
+ Assert.assertEquals(roles.size(), 3);
assertRolesContains(realmUserRole, roles);
Assert.assertTrue(user.hasRole(realmUserRole));
// Role "foo" is default realm role
@@ -485,13 +485,13 @@ public class AdapterTest extends AbstractModelTest {
// Test that application role 'user' don't clash with realm role 'user'
Assert.assertNotEquals(realmModel.getRole("user").getId(), application.getRole("user").getId());
- Assert.assertEquals(6, user.getRoleMappings().size());
+ Assert.assertEquals(7, user.getRoleMappings().size());
// Revoke some roles
user.deleteRoleMapping(realmModel.getRole("foo"));
user.deleteRoleMapping(appBarRole);
roles = user.getRoleMappings();
- Assert.assertEquals(4, roles.size());
+ Assert.assertEquals(5, roles.size());
assertRolesContains(realmUserRole, roles);
assertRolesContains(application.getRole("user"), roles);
Assert.assertFalse(user.hasRole(appBarRole));
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
index 5bc3556..597e7dd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/CacheTest.java
@@ -1,6 +1,7 @@
package org.keycloak.testsuite.model;
import java.util.List;
+import java.util.Set;
import org.junit.Assert;
import org.junit.ClassRule;
@@ -89,4 +90,51 @@ public class CacheTest {
}
}
+ // KEYCLOAK-1842
+ @Test
+ public void testRoleMappingsInvalidatedWhenClientRemoved() {
+ KeycloakSession session = kc.startSession();
+ try {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserModel user = session.users().addUser(realm, "joel");
+ ClientModel client = realm.addClient("foo");
+ RoleModel fooRole = client.addRole("foo-role");
+ user.grantRole(fooRole);
+ } finally {
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ // Remove client
+ session = kc.startSession();
+ int grantedRolesCount;
+ try {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserModel user = session.users().getUserByUsername("joel", realm);
+ grantedRolesCount = user.getRoleMappings().size();
+
+ ClientModel client = realm.getClientByClientId("foo");
+ realm.removeClient(client.getId());
+ } finally {
+ session.getTransaction().commit();
+ session.close();
+ }
+
+ // Assert role mappings was removed from user as well
+ session = kc.startSession();
+ try {
+ RealmModel realm = session.realms().getRealmByName("test");
+ UserModel user = session.users().getUserByUsername("joel", realm);
+ Set<RoleModel> roles = user.getRoleMappings();
+ for (RoleModel role : roles) {
+ Assert.assertNotNull(role.getContainer());
+ }
+
+ Assert.assertEquals(roles.size(), grantedRolesCount - 1);
+ } finally {
+ session.getTransaction().commit();
+ session.close();
+ }
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
index 2f33e43..6f42fd5 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/ImportTest.java
@@ -79,7 +79,7 @@ public class ImportTest extends AbstractModelTest {
Assert.assertEquals(1, creds.size());
RequiredCredentialModel cred = creds.get(0);
Assert.assertEquals("password", cred.getFormLabel());
- Assert.assertEquals(2, realm.getDefaultRoles().size());
+ Assert.assertEquals(3, realm.getDefaultRoles().size());
Assert.assertNotNull(realm.getRole("foo"));
Assert.assertNotNull(realm.getRole("bar"));
@@ -132,6 +132,10 @@ public class ImportTest extends AbstractModelTest {
Assert.assertTrue(allRoles.contains(application.getRole("app-admin")));
Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-admin")));
+ Assert.assertTrue(application.getRole("app-admin").isScopeParamRequired());
+ Assert.assertFalse(otherApp.getRole("otherapp-admin").isScopeParamRequired());
+ Assert.assertFalse(otherApp.getRole("otherapp-user").isScopeParamRequired());
+
UserModel wburke = session.users().getUserByUsername("wburke", realm);
// user with creation timestamp in import
Assert.assertEquals(new Long(123654), wburke.getCreatedTimestamp());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
index 258dd3c..f0118c2 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
@@ -4,6 +4,8 @@ import org.junit.Assert;
import org.junit.Test;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.OfflineClientSessionModel;
+import org.keycloak.models.OfflineUserSessionModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
@@ -283,6 +285,59 @@ public class UserModelTest extends AbstractModelTest {
Assert.assertNull(session.users().getUserByUsername("user1", realm));
}
+ @Test
+ public void testOfflineSessionsRemoved() {
+ RealmModel realm = realmManager.createRealm("original");
+ ClientModel fooClient = realm.addClient("foo");
+ ClientModel barClient = realm.addClient("bar");
+
+ UserModel user1 = session.users().addUser(realm, "user1");
+ addOfflineUserSession(user1, "123", "something1");
+ addOfflineClientSession(user1, "456", "123", fooClient.getId(), "something2");
+ addOfflineClientSession(user1, "789", "123", barClient.getId(), "something3");
+
+ commit();
+
+ realm = realmManager.getRealmByName("original");
+ realm.removeClient(barClient.getId());
+
+ commit();
+
+ realm = realmManager.getRealmByName("original");
+ user1 = session.users().getUserByUsername("user1", realm);
+ Assert.assertEquals("something1", user1.getOfflineUserSession("123").getData());
+ Assert.assertEquals("something2", user1.getOfflineClientSession("456").getData());
+ Assert.assertNull(user1.getOfflineClientSession("789"));
+
+ realm.removeClient(fooClient.getId());
+
+ commit();
+
+ realm = realmManager.getRealmByName("original");
+ user1 = session.users().getUserByUsername("user1", realm);
+ Assert.assertNull(user1.getOfflineClientSession("456"));
+ Assert.assertNull(user1.getOfflineClientSession("789"));
+ Assert.assertNull(user1.getOfflineUserSession("123"));
+ Assert.assertEquals(0, user1.getOfflineUserSessions().size());
+ Assert.assertEquals(0, user1.getOfflineClientSessions().size());
+ }
+
+ private void addOfflineUserSession(UserModel user, String userSessionId, String data) {
+ OfflineUserSessionModel model = new OfflineUserSessionModel();
+ model.setUserSessionId(userSessionId);
+ model.setData(data);
+ user.addOfflineUserSession(model);
+ }
+
+ private void addOfflineClientSession(UserModel user, String clientSessionId, String userSessionId, String clientId, String data) {
+ OfflineClientSessionModel model = new OfflineClientSessionModel();
+ model.setClientSessionId(clientSessionId);
+ model.setUserSessionId(userSessionId);
+ model.setClientId(clientId);
+ model.setData(data);
+ user.addOfflineClientSession(model);
+ }
+
public static void assertEquals(UserModel expected, UserModel actual) {
Assert.assertEquals(expected.getUsername(), actual.getUsername());
Assert.assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
index cbfc76f..24f4911 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/AccessTokenTest.java
@@ -180,7 +180,11 @@ public class AccessTokenTest {
Assert.assertEquals("invalid_grant", response.getError());
Assert.assertEquals("Incorrect redirect_uri", response.getErrorDescription());
- events.expectCodeToToken(codeId, loginEvent.getSessionId()).error("invalid_code").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).assertEvent();
+ events.expectCodeToToken(codeId, loginEvent.getSessionId()).error("invalid_code")
+ .removeDetail(Details.TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_TYPE)
+ .assertEvent();
}
@Test
@@ -201,7 +205,13 @@ public class AccessTokenTest {
assertNull(tokenResponse.getAccessToken());
assertNull(tokenResponse.getRefreshToken());
- events.expectCodeToToken(codeId, sessionId).removeDetail(Details.TOKEN_ID).user((String) null).session((String) null).removeDetail(Details.REFRESH_TOKEN_ID).error(Errors.INVALID_CODE).assertEvent();
+ events.expectCodeToToken(codeId, sessionId)
+ .removeDetail(Details.TOKEN_ID)
+ .user((String) null)
+ .session((String) null)
+ .removeDetail(Details.REFRESH_TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_TYPE)
+ .error(Errors.INVALID_CODE).assertEvent();
events.clear();
}
@@ -230,7 +240,11 @@ public class AccessTokenTest {
Assert.assertEquals(400, response.getStatusCode());
AssertEvents.ExpectedEvent expectedEvent = events.expectCodeToToken(codeId, null);
- expectedEvent.error("invalid_code").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).user((String) null);
+ expectedEvent.error("invalid_code")
+ .removeDetail(Details.TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_TYPE)
+ .user((String) null);
expectedEvent.assertEvent();
events.clear();
@@ -264,7 +278,11 @@ public class AccessTokenTest {
Assert.assertEquals(400, response.getStatusCode());
AssertEvents.ExpectedEvent expectedEvent = events.expectCodeToToken(codeId, null);
- expectedEvent.error("invalid_code").removeDetail(Details.TOKEN_ID).removeDetail(Details.REFRESH_TOKEN_ID).user((String) null);
+ expectedEvent.error("invalid_code")
+ .removeDetail(Details.TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_ID)
+ .removeDetail(Details.REFRESH_TOKEN_TYPE)
+ .user((String) null);
expectedEvent.assertEvent();
events.clear();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
index 2363305..059cdd7 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OAuthGrantTest.java
@@ -34,6 +34,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.protocol.oidc.OIDCLoginProtocolFactory;
@@ -290,4 +291,71 @@ public class OAuthGrantTest {
});
}
+ @Test
+ public void oauthGrantScopeParamRequired() throws Exception {
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ ClientModel thirdParty = appRealm.getClientByClientId("third-party");
+ RoleModel barAppRole = thirdParty.addRole("bar-role");
+ barAppRole.setScopeParamRequired(true);
+
+ RoleModel fooRole = appRealm.addRole("foo-role");
+ fooRole.setScopeParamRequired(true);
+ thirdParty.addScopeMapping(fooRole);
+
+ UserModel testUser = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm);
+ testUser.grantRole(fooRole);
+ testUser.grantRole(barAppRole);
+ }
+
+ });
+
+ // Assert roles not on grant screen when not requested
+ oauth.clientId("third-party");
+ oauth.doLoginGrant("test-user@localhost", "password");
+ grantPage.assertCurrent();
+ Assert.assertFalse(driver.getPageSource().contains("foo-role"));
+ Assert.assertFalse(driver.getPageSource().contains("bar-role"));
+ grantPage.cancel();
+
+ events.expectLogin()
+ .client("third-party")
+ .error("rejected_by_user")
+ .removeDetail(Details.CONSENT)
+ .assertEvent();
+
+ oauth.scope("foo-role third-party/bar-role");
+ oauth.doLoginGrant("test-user@localhost", "password");
+ grantPage.assertCurrent();
+ Assert.assertTrue(driver.getPageSource().contains("foo-role"));
+ Assert.assertTrue(driver.getPageSource().contains("bar-role"));
+ grantPage.accept();
+
+ events.expectLogin()
+ .client("third-party")
+ .detail(Details.CONSENT, Details.CONSENT_VALUE_CONSENT_GRANTED)
+ .assertEvent();
+
+ // Revoke
+ accountAppsPage.open();
+ accountAppsPage.revokeGrant("third-party");
+ events.expect(EventType.REVOKE_GRANT)
+ .client("account").detail(Details.REVOKED_CLIENT, "third-party").assertEvent();
+
+ // cleanup
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.removeRole(appRealm.getRole("foo-role"));
+ ClientModel thirdparty = appRealm.getClientByClientId("third-party");
+ thirdparty.removeRole(thirdparty.getRole("bar-role"));
+ }
+
+ });
+
+ }
+
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
new file mode 100644
index 0000000..8eab300
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
@@ -0,0 +1,552 @@
+package org.keycloak.testsuite.oauth;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.constants.ServiceAccountConstants;
+import org.keycloak.events.Details;
+import org.keycloak.events.Errors;
+import org.keycloak.events.Event;
+import org.keycloak.events.EventType;
+import org.keycloak.jose.jws.JWSInput;
+import org.keycloak.models.ClientModel;
+import org.keycloak.models.Constants;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.AccessToken;
+import org.keycloak.representations.RefreshToken;
+import org.keycloak.services.managers.ClientManager;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.AccountApplicationsPage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.OAuthGrantPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
+import org.keycloak.util.RefreshTokenUtil;
+import org.keycloak.util.Time;
+import org.keycloak.util.UriUtils;
+import org.openqa.selenium.WebDriver;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class OfflineTokenTest {
+
+ @ClassRule
+ public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ // For testing
+ appRealm.setAccessTokenLifespan(10);
+ appRealm.setSsoSessionIdleTimeout(30);
+
+ ClientModel app = new ClientManager(manager).createClient(appRealm, "offline-client");
+ app.setSecret("secret1");
+ String testAppRedirectUri = appRealm.getClientByClientId("test-app").getRedirectUris().iterator().next();
+ offlineClientAppUri = UriUtils.getOrigin(testAppRedirectUri) + "/offline-client";
+ app.setRedirectUris(new HashSet<>(Arrays.asList(offlineClientAppUri)));
+ app.setManagementUrl(offlineClientAppUri);
+
+ new ClientManager(manager).enableServiceAccount(app);
+ UserModel serviceAccountUser = manager.getSession().users().getUserByUsername(ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client", appRealm);
+ RoleModel customerUserRole = appRealm.getClientByClientId("test-app").getRole("customer-user");
+ serviceAccountUser.grantRole(customerUserRole);
+
+ userId = manager.getSession().users().getUserByUsername("test-user@localhost", appRealm).getId();
+
+ URL url = getClass().getResource("/oidc/offline-client-keycloak.json");
+ keycloakRule.createApplicationDeployment()
+ .name("offline-client").contextPath("/offline-client")
+ .servletClass(OfflineTokenServlet.class).adapterConfigPath(url.getPath())
+ .role("user").deployApplication();
+ }
+
+ });
+
+ private static String userId;
+ private static String offlineClientAppUri;
+
+ @Rule
+ public WebRule webRule = new WebRule(this);
+
+ @WebResource
+ protected WebDriver driver;
+
+ @WebResource
+ protected OAuthClient oauth;
+
+ @WebResource
+ protected LoginPage loginPage;
+
+ @WebResource
+ protected OAuthGrantPage oauthGrantPage;
+
+ @WebResource
+ protected AccountApplicationsPage accountAppPage;
+
+ @Rule
+ public AssertEvents events = new AssertEvents(keycloakRule);
+
+
+// @Test
+// public void testSleep() throws Exception {
+// Thread.sleep(9999000);
+// }
+
+ @Test
+ public void offlineTokenDisabledForClient() throws Exception {
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.getClientByClientId("offline-client").setFullScopeAllowed(false);
+ }
+
+ });
+
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client");
+ oauth.redirectUri(offlineClientAppUri);
+ oauth.doLogin("test-user@localhost", "password");
+
+ Event loginEvent = events.expectLogin()
+ .client("offline-client")
+ .detail(Details.REDIRECT_URI, offlineClientAppUri)
+ .assertEvent();
+
+ String sessionId = loginEvent.getSessionId();
+ String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret1");
+
+ assertEquals(400, tokenResponse.getStatusCode());
+ assertEquals("not_allowed", tokenResponse.getError());
+
+ events.expectCodeToToken(codeId, sessionId)
+ .client("offline-client")
+ .error("not_allowed")
+ .clearDetails()
+ .assertEvent();
+
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.getClientByClientId("offline-client").setFullScopeAllowed(true);
+ }
+
+ });
+ }
+
+ @Test
+ public void offlineTokenUserNotAllowed() throws Exception {
+ String userId = keycloakRule.getUser("test", "keycloak-user@localhost").getId();
+
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client");
+ oauth.redirectUri(offlineClientAppUri);
+ oauth.doLogin("keycloak-user@localhost", "password");
+
+ Event loginEvent = events.expectLogin()
+ .client("offline-client")
+ .user(userId)
+ .detail(Details.REDIRECT_URI, offlineClientAppUri)
+ .assertEvent();
+
+ String sessionId = loginEvent.getSessionId();
+ String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret1");
+
+ assertEquals(400, tokenResponse.getStatusCode());
+ assertEquals("not_allowed", tokenResponse.getError());
+
+ events.expectCodeToToken(codeId, sessionId)
+ .client("offline-client")
+ .user(userId)
+ .error("not_allowed")
+ .clearDetails()
+ .assertEvent();
+ }
+
+ @Test
+ public void offlineTokenBrowserFlow() throws Exception {
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client");
+ oauth.redirectUri(offlineClientAppUri);
+ oauth.doLogin("test-user@localhost", "password");
+
+ Event loginEvent = events.expectLogin()
+ .client("offline-client")
+ .detail(Details.REDIRECT_URI, offlineClientAppUri)
+ .assertEvent();
+
+ final String sessionId = loginEvent.getSessionId();
+ String codeId = loginEvent.getDetails().get(Details.CODE_ID);
+
+ String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doAccessTokenRequest(code, "secret1");
+ AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
+ String offlineTokenString = tokenResponse.getRefreshToken();
+ RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
+
+ events.expectCodeToToken(codeId, sessionId)
+ .client("offline-client")
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .assertEvent();
+
+ Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
+ Assert.assertEquals(0, offlineToken.getExpiration());
+
+ testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, sessionId, userId);
+ }
+
+ private void testRefreshWithOfflineToken(AccessToken oldToken, RefreshToken offlineToken, String offlineTokenString,
+ final String sessionId, String userId) {
+ // Change offset to big value to ensure userSession expired
+ Time.setOffset(99999);
+ Assert.assertFalse(oldToken.isActive());
+ Assert.assertTrue(offlineToken.isActive());
+
+ // Assert userSession expired
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ manager.getSession().sessions().removeExpiredUserSessions(appRealm);
+ }
+
+ });
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ Assert.assertNull(manager.getSession().sessions().getUserSession(appRealm, sessionId));
+ }
+
+ });
+
+
+ OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(offlineTokenString, "secret1");
+ AccessToken refreshedToken = oauth.verifyToken(response.getAccessToken());
+ Assert.assertEquals(200, response.getStatusCode());
+ Assert.assertEquals(sessionId, refreshedToken.getSessionState());
+
+ // Assert no refreshToken in the response
+ Assert.assertNull(response.getRefreshToken());
+ Assert.assertNotEquals(oldToken.getId(), refreshedToken.getId());
+
+ Assert.assertEquals(userId, refreshedToken.getSubject());
+
+ Assert.assertEquals(2, refreshedToken.getRealmAccess().getRoles().size());
+ Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole("user"));
+ Assert.assertTrue(refreshedToken.getRealmAccess().isUserInRole(Constants.OFFLINE_ACCESS_ROLE));
+
+ Assert.assertEquals(1, refreshedToken.getResourceAccess("test-app").getRoles().size());
+ Assert.assertTrue(refreshedToken.getResourceAccess("test-app").isUserInRole("customer-user"));
+
+ Event refreshEvent = events.expectRefresh(offlineToken.getId(), sessionId)
+ .client("offline-client")
+ .user(userId)
+ .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .assertEvent();
+ Assert.assertNotEquals(oldToken.getId(), refreshEvent.getDetails().get(Details.TOKEN_ID));
+
+ Time.setOffset(0);
+ }
+
+ @Test
+ public void offlineTokenDirectGrantFlow() throws Exception {
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client");
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "test-user@localhost", "password");
+
+ AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
+ String offlineTokenString = tokenResponse.getRefreshToken();
+ RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
+
+ events.expectLogin()
+ .client("offline-client")
+ .user(userId)
+ .session(token.getSessionState())
+ .detail(Details.RESPONSE_TYPE, "token")
+ .detail(Details.TOKEN_ID, token.getId())
+ .detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .detail(Details.USERNAME, "test-user@localhost")
+ .removeDetail(Details.CODE_ID)
+ .removeDetail(Details.REDIRECT_URI)
+ .removeDetail(Details.CONSENT)
+ .assertEvent();
+
+ Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
+ Assert.assertEquals(0, offlineToken.getExpiration());
+
+ testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), userId);
+ }
+
+ @Test
+ public void offlineTokenServiceAccountFlow() throws Exception {
+ oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+ oauth.clientId("offline-client");
+ OAuthClient.AccessTokenResponse tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
+
+ AccessToken token = oauth.verifyToken(tokenResponse.getAccessToken());
+ String offlineTokenString = tokenResponse.getRefreshToken();
+ RefreshToken offlineToken = oauth.verifyRefreshToken(offlineTokenString);
+
+ String serviceAccountUserId = keycloakRule.getUser("test", ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client").getId();
+
+ events.expectClientLogin()
+ .client("offline-client")
+ .user(serviceAccountUserId)
+ .session(token.getSessionState())
+ .detail(Details.TOKEN_ID, token.getId())
+ .detail(Details.REFRESH_TOKEN_ID, offlineToken.getId())
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client")
+ .assertEvent();
+
+ Assert.assertEquals(RefreshTokenUtil.TOKEN_TYPE_OFFLINE, offlineToken.getType());
+ Assert.assertEquals(0, offlineToken.getExpiration());
+
+ testRefreshWithOfflineToken(token, offlineToken, offlineTokenString, token.getSessionState(), serviceAccountUserId);
+
+
+ // Now retrieve another offline token and verify that previous offline token is not valid anymore
+ tokenResponse = oauth.doClientCredentialsGrantAccessTokenRequest("secret1");
+
+ AccessToken token2 = oauth.verifyToken(tokenResponse.getAccessToken());
+ String offlineTokenString2 = tokenResponse.getRefreshToken();
+ RefreshToken offlineToken2 = oauth.verifyRefreshToken(offlineTokenString2);
+
+ events.expectClientLogin()
+ .client("offline-client")
+ .user(serviceAccountUserId)
+ .session(token2.getSessionState())
+ .detail(Details.TOKEN_ID, token2.getId())
+ .detail(Details.REFRESH_TOKEN_ID, offlineToken2.getId())
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .detail(Details.USERNAME, ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + "offline-client")
+ .assertEvent();
+
+ // Refresh with old offline token should fail
+ OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(offlineTokenString, "secret1");
+ Assert.assertEquals(400, response.getStatusCode());
+ Assert.assertEquals("invalid_grant", response.getError());
+
+ events.expectRefresh(offlineToken.getId(), offlineToken.getSessionState())
+ .error(Errors.INVALID_TOKEN)
+ .client("offline-client")
+ .user(serviceAccountUserId)
+ .removeDetail(Details.UPDATED_REFRESH_TOKEN_ID)
+ .removeDetail(Details.TOKEN_ID)
+ .detail(Details.REFRESH_TOKEN_TYPE, RefreshTokenUtil.TOKEN_TYPE_OFFLINE)
+ .assertEvent();
+
+ // Refresh with new offline token is ok
+ testRefreshWithOfflineToken(token2, offlineToken2, offlineTokenString2, token2.getSessionState(), serviceAccountUserId);
+ }
+
+ @Test
+ public void testServlet() {
+ OfflineTokenServlet.tokenInfo = null;
+
+ String servletUri = UriBuilder.fromUri(offlineClientAppUri)
+ .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
+ .build().toString();
+ driver.navigate().to(servletUri);
+ loginPage.login("test-user@localhost", "password");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+
+ Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
+ Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getExpiration(), 0);
+
+ String accessTokenId = OfflineTokenServlet.tokenInfo.accessToken.getId();
+ String refreshTokenId = OfflineTokenServlet.tokenInfo.refreshToken.getId();
+
+ // Assert access token will be refreshed, but offline token will be still the same
+ Time.setOffset(9999);
+ driver.navigate().to(offlineClientAppUri);
+ Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+ Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getId(), refreshTokenId);
+ Assert.assertNotEquals(OfflineTokenServlet.tokenInfo.accessToken.getId(), accessTokenId);
+
+ // Ensure that logout works for webapp (even if offline token will be still valid in Keycloak DB)
+ driver.navigate().to(offlineClientAppUri + "/logout");
+ loginPage.assertCurrent();
+ driver.navigate().to(offlineClientAppUri);
+ loginPage.assertCurrent();
+
+ Time.setOffset(0);
+ events.clear();
+ }
+
+ @Test
+ public void testServletWithRevoke() {
+ // Login to servlet first with offline token
+ String servletUri = UriBuilder.fromUri(offlineClientAppUri)
+ .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
+ .build().toString();
+ driver.navigate().to(servletUri);
+ loginPage.login("test-user@localhost", "password");
+ Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+
+ Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
+
+ // Assert refresh works with increased time
+ Time.setOffset(9999);
+ driver.navigate().to(offlineClientAppUri);
+ Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+ Time.setOffset(0);
+
+ events.clear();
+
+ // Go to account service and revoke grant
+ accountAppPage.open();
+ List<String> additionalGrants = accountAppPage.getApplications().get("offline-client").getAdditionalGrants();
+ Assert.assertEquals(additionalGrants.size(), 1);
+ Assert.assertEquals(additionalGrants.get(0), "Offline Token");
+ accountAppPage.revokeGrant("offline-client");
+ Assert.assertEquals(accountAppPage.getApplications().get("offline-client").getAdditionalGrants().size(), 0);
+
+ events.expect(EventType.REVOKE_GRANT)
+ .client("account").detail(Details.REVOKED_CLIENT, "offline-client").assertEvent();
+
+ // Assert refresh doesn't work now (increase time one more time)
+ Time.setOffset(9999);
+ driver.navigate().to(offlineClientAppUri);
+ Assert.assertFalse(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+ loginPage.assertCurrent();
+ Time.setOffset(0);
+ }
+
+ @Test
+ public void testServletWithConsent() {
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.getClientByClientId("offline-client").setConsentRequired(true);
+ }
+
+ });
+
+ // Assert grant page doesn't have 'Offline Access' role when offline token is not requested
+ driver.navigate().to(offlineClientAppUri);
+ loginPage.login("test-user@localhost", "password");
+ oauthGrantPage.assertCurrent();
+ Assert.assertFalse(driver.getPageSource().contains("Offline access"));
+ oauthGrantPage.cancel();
+
+ // Assert grant page has 'Offline Access' role now
+ String servletUri = UriBuilder.fromUri(offlineClientAppUri)
+ .queryParam(OAuth2Constants.SCOPE, OAuth2Constants.OFFLINE_ACCESS)
+ .build().toString();
+ driver.navigate().to(servletUri);
+ loginPage.login("test-user@localhost", "password");
+ oauthGrantPage.assertCurrent();
+ Assert.assertTrue(driver.getPageSource().contains("Offline access"));
+ oauthGrantPage.accept();
+
+ Assert.assertTrue(driver.getCurrentUrl().startsWith(offlineClientAppUri));
+ Assert.assertEquals(OfflineTokenServlet.tokenInfo.refreshToken.getType(), RefreshTokenUtil.TOKEN_TYPE_OFFLINE);
+
+ accountAppPage.open();
+ AccountApplicationsPage.AppEntry offlineClient = accountAppPage.getApplications().get("offline-client");
+ Assert.assertTrue(offlineClient.getRolesGranted().contains("Offline access"));
+ Assert.assertTrue(offlineClient.getAdditionalGrants().contains("Offline Token"));
+
+ events.clear();
+
+ // Revert change
+ keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+ @Override
+ public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+ appRealm.getClientByClientId("offline-client").setConsentRequired(false);
+ }
+
+ });
+ }
+
+ public static class OfflineTokenServlet extends HttpServlet {
+
+ private static TokenInfo tokenInfo;
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (req.getRequestURI().endsWith("logout")) {
+
+ UriBuilder redirectUriBuilder = UriBuilder.fromUri(offlineClientAppUri);
+ if (req.getParameter(OAuth2Constants.SCOPE) != null) {
+ redirectUriBuilder.queryParam(OAuth2Constants.SCOPE, req.getParameter(OAuth2Constants.SCOPE));
+ }
+ String redirectUri = redirectUriBuilder.build().toString();
+
+ String origin = UriUtils.getOrigin(req.getRequestURL().toString());
+ String serverLogoutRedirect = UriBuilder.fromUri(origin + "/auth/realms/test/protocol/openid-connect/logout")
+ .queryParam("redirect_uri", redirectUri)
+ .build().toString();
+
+ resp.sendRedirect(serverLogoutRedirect);
+ return;
+ }
+
+ StringBuilder response = new StringBuilder("<html><head><title>Offline token servlet</title></head><body><pre>");
+ RefreshableKeycloakSecurityContext ctx = (RefreshableKeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
+ String accessTokenPretty = JsonSerialization.writeValueAsPrettyString(ctx.getToken());
+ RefreshToken refreshToken = new JWSInput(ctx.getRefreshToken()).readJsonContent(RefreshToken.class);
+ String refreshTokenPretty = JsonSerialization.writeValueAsPrettyString(refreshToken);
+
+ response = response.append(accessTokenPretty)
+ .append(refreshTokenPretty)
+ .append("</pre></body></html>");
+ resp.getWriter().println(response.toString());
+
+ tokenInfo = new TokenInfo(ctx.getToken(), refreshToken);
+ }
+
+ }
+
+ private static class TokenInfo {
+
+ private final AccessToken accessToken;
+ private final RefreshToken refreshToken;
+
+ public TokenInfo(AccessToken accessToken, RefreshToken refreshToken) {
+ this.accessToken = accessToken;
+ this.refreshToken = refreshToken;
+ }
+ }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index b06e433..087f82f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -76,6 +76,8 @@ public class OAuthClient {
private String state = "mystate";
+ private String scope;
+
private String uiLocales = null;
private PublicKey realmPublicKey;
@@ -192,6 +194,9 @@ public class OAuthClient {
if (clientSessionHost != null) {
parameters.add(new BasicNameValuePair(AdapterConstants.CLIENT_SESSION_HOST, clientSessionHost));
}
+ if (scope != null) {
+ parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
+ }
UrlEncodedFormEntity formEntity;
try {
@@ -218,6 +223,10 @@ public class OAuthClient {
List<NameValuePair> parameters = new LinkedList<NameValuePair>();
parameters.add(new BasicNameValuePair(OAuth2Constants.GRANT_TYPE, OAuth2Constants.CLIENT_CREDENTIALS));
+ if (scope != null) {
+ parameters.add(new BasicNameValuePair(OAuth2Constants.SCOPE, scope));
+ }
+
UrlEncodedFormEntity formEntity;
try {
formEntity = new UrlEncodedFormEntity(parameters, "UTF-8");
@@ -390,6 +399,9 @@ public class OAuthClient {
if(uiLocales != null){
b.queryParam(LocaleHelper.UI_LOCALES_PARAM, uiLocales);
}
+ if (scope != null) {
+ b.queryParam(OAuth2Constants.SCOPE, scope);
+ }
return b.build(realm).toString();
}
@@ -452,6 +464,11 @@ public class OAuthClient {
return this;
}
+ public OAuthClient scope(String scope) {
+ this.scope = scope;
+ return this;
+ }
+
public OAuthClient uiLocales(String uiLocales){
this.uiLocales = uiLocales;
return this;
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
index f88db27..b48559c 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AccountApplicationsPage.java
@@ -77,6 +77,15 @@ public class AccountApplicationsPage extends AbstractAccountPage {
currentEntry.addMapper(protMapper);
}
break;
+ case 5:
+ String additionalGrant = col.getText();
+ if (additionalGrant.isEmpty()) break;
+ String[] grants = additionalGrant.split(",");
+ for (String grant : grants) {
+ grant = grant.trim();
+ currentEntry.addAdditionalGrant(grant);
+ }
+ break;
}
}
}
@@ -89,6 +98,7 @@ public class AccountApplicationsPage extends AbstractAccountPage {
private final List<String> rolesAvailable = new ArrayList<String>();
private final List<String> rolesGranted = new ArrayList<String>();
private final List<String> protocolMappersGranted = new ArrayList<String>();
+ private final List<String> additionalGrants = new ArrayList<>();
private void addAvailableRole(String role) {
rolesAvailable.add(role);
@@ -102,6 +112,10 @@ public class AccountApplicationsPage extends AbstractAccountPage {
protocolMappersGranted.add(protocolMapper);
}
+ private void addAdditionalGrant(String grant) {
+ additionalGrants.add(grant);
+ }
+
public List<String> getRolesGranted() {
return rolesGranted;
}
@@ -113,5 +127,9 @@ public class AccountApplicationsPage extends AbstractAccountPage {
public List<String> getProtocolMappersGranted() {
return protocolMappersGranted;
}
+
+ public List<String> getAdditionalGrants() {
+ return additionalGrants;
+ }
}
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java
new file mode 100644
index 0000000..bba3f93
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginUpdateProfileEditUsernameAllowedPage.java
@@ -0,0 +1,51 @@
+/*
+ * 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.pages;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+public class LoginUpdateProfileEditUsernameAllowedPage extends LoginUpdateProfilePage {
+
+ @FindBy(id = "username")
+ private WebElement usernameInput;
+
+ public void update(String firstName, String lastName, String email, String username) {
+ usernameInput.clear();
+ usernameInput.sendKeys(username);
+ update(firstName, lastName, email);
+ }
+
+ public String getUsername() {
+ return usernameInput.getAttribute("value");
+ }
+
+ public boolean isCurrent() {
+ return driver.getTitle().equals("Update Account Information");
+ }
+
+ @Override
+ public void open() {
+ throw new UnsupportedOperationException();
+ }
+
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
index add9708..438938f 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/LDAPRule.java
@@ -57,4 +57,8 @@ public class LDAPRule extends ExternalResource {
public Map<String, String> getConfig() {
return ldapTestConfiguration.getLDAPConfig();
}
+
+ public int getSleepTime() {
+ return ldapTestConfiguration.getSleepTime();
+ }
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
index d556a83..2d2ab44 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/saml/SamlBindingTest.java
@@ -457,7 +457,7 @@ public class SamlBindingTest {
ClientSessionModel clientSession = session.sessions().createClientSession(adminRealm, adminConsole);
clientSession.setNote(OIDCLoginProtocol.ISSUER, "http://localhost:8081/auth/realms/master");
UserSessionModel userSession = session.sessions().createUserSession(adminRealm, admin, "admin", null, "form", false, null, null);
- AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
+ AccessToken token = tm.createClientAccessToken(session, tm.getAccess(null, true, adminConsole, admin), adminRealm, adminConsole, admin, userSession, clientSession);
return tm.encodeToken(adminRealm, token);
} finally {
keycloakRule.stopSession(session, true);
diff --git a/testsuite/integration/src/test/resources/model/testrealm.json b/testsuite/integration/src/test/resources/model/testrealm.json
index acbbdf5..f7c8cdd 100755
--- a/testsuite/integration/src/test/resources/model/testrealm.json
+++ b/testsuite/integration/src/test/resources/model/testrealm.json
@@ -198,7 +198,8 @@
"application" : {
"Application" : [
{
- "name": "app-admin"
+ "name": "app-admin",
+ "scopeParamRequired": true
},
{
"name": "app-user"
@@ -206,7 +207,8 @@
],
"OtherApp" : [
{
- "name": "otherapp-admin"
+ "name": "otherapp-admin",
+ "scopeParamRequired": false
},
{
"name": "otherapp-user"
diff --git a/testsuite/integration/src/test/resources/oidc/offline-client-keycloak.json b/testsuite/integration/src/test/resources/oidc/offline-client-keycloak.json
new file mode 100644
index 0000000..bc7be17
--- /dev/null
+++ b/testsuite/integration/src/test/resources/oidc/offline-client-keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm": "test",
+ "resource": "offline-client",
+ "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8081/auth",
+ "ssl-required" : "external",
+ "credentials": {
+ "secret": "secret1"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index 8ad29cc..e16e3e2 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -5,6 +5,7 @@
"sslRequired": "external",
"registrationAllowed": true,
"resetPasswordAllowed": true,
+ "editUsernameAllowed" : true,
"privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
"publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
"requiredCredentials": [ "password" ],
@@ -25,6 +26,22 @@
{ "type" : "password",
"value" : "password" }
],
+ "realmRoles": ["user", "offline_access"],
+ "clientRoles": {
+ "test-app": [ "customer-user" ],
+ "account": [ "view-profile", "manage-account" ]
+ }
+ },
+ {
+ "username" : "john-doh@localhost",
+ "enabled": true,
+ "email" : "john-doh@localhost",
+ "firstName": "John",
+ "lastName": "Doh",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
"realmRoles": ["user"],
"clientRoles": {
"test-app": [ "customer-user" ],
@@ -32,7 +49,7 @@
}
},
{
- "username" : "keycloak-user@localhost",
+ "username" : "keycloak-user@localhost",
"enabled": true,
"email" : "keycloak-user@localhost",
"credentials" : [
testsuite/integration-arquillian/pom.xml 216(+35 -181)
diff --git a/testsuite/integration-arquillian/pom.xml b/testsuite/integration-arquillian/pom.xml
index 2ef2ac3..064e065 100644
--- a/testsuite/integration-arquillian/pom.xml
+++ b/testsuite/integration-arquillian/pom.xml
@@ -2,190 +2,44 @@
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<parent>
- <artifactId>keycloak-testsuite-pom</artifactId>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-testsuite-pom</artifactId>
<version>1.6.0.Final-SNAPSHOT</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
-
- <artifactId>keycloak-testsuite-integration-arquillian</artifactId>
- <name>KeyCloak Arquillian TestSuite</name>
-
- <properties>
- <browser>phantomjs</browser>
-
- <arquillian-core.version>1.1.5.Final</arquillian-core.version>
- <selenium.version>2.45.0</selenium.version>
- <arquillian-drone.version>1.3.1.Final</arquillian-drone.version>
- <arquillian-phantomjs.version>1.1.4.Final</arquillian-phantomjs.version>
- <arquillian-graphene.version>2.0.3.Final</arquillian-graphene.version>
- <arquillian-wildfly-container.version>8.1.0.Final</arquillian-wildfly-container.version>
-
- <!-- Used in profile "wildfly-8-remote".
- Set to "false" if admin password has already been updated after first login. -->
- <firstAdminLogin>true</firstAdminLogin>
- </properties>
-
- <dependencyManagement>
- <dependencies>
- <dependency>
- <groupId>org.jboss.arquillian.selenium</groupId>
- <artifactId>selenium-bom</artifactId>
- <version>${selenium.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.arquillian</groupId>
- <artifactId>arquillian-bom</artifactId>
- <version>${arquillian-core.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.arquillian.extension</groupId>
- <artifactId>arquillian-drone-bom</artifactId>
- <version>${arquillian-drone.version}</version>
- <type>pom</type>
- <scope>import</scope>
- </dependency>
- <dependency>
- <groupId>org.wildfly</groupId>
- <artifactId>wildfly-arquillian-container-remote</artifactId>
- <version>${arquillian-wildfly-container.version}</version>
- </dependency>
- <dependency>
- <groupId>org.wildfly</groupId>
- <artifactId>wildfly-arquillian-container-managed</artifactId>
- <version>${arquillian-wildfly-container.version}</version>
- </dependency>
- </dependencies>
- </dependencyManagement>
-
- <dependencies>
- <dependency>
- <groupId>junit</groupId>
- <artifactId>junit</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.arquillian.junit</groupId>
- <artifactId>arquillian-junit-container</artifactId>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.arquillian.graphene</groupId>
- <artifactId>graphene-webdriver</artifactId>
- <version>${arquillian-graphene.version}</version>
- <type>pom</type>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.jboss.arquillian.extension</groupId>
- <artifactId>arquillian-phantom-driver</artifactId>
- <version>${arquillian-phantomjs.version}</version>
- <scope>test</scope>
- </dependency>
- <dependency>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-server-dist</artifactId>
- <scope>test</scope>
- <type>zip</type>
- </dependency>
- <dependency>
- <groupId>org.slf4j</groupId>
- <artifactId>slf4j-log4j12</artifactId>
- </dependency>
- </dependencies>
-
- <profiles>
- <profile>
- <id>wildfly-8-remote</id>
- <dependencies>
- <dependency>
- <groupId>org.wildfly</groupId>
- <artifactId>wildfly-arquillian-container-remote</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <systemPropertyVariables>
- <shouldDeploy>false</shouldDeploy>
- <arquillian.launch>wildfly-8-remote</arquillian.launch>
- <browser>${browser}</browser>
- <firstAdminLogin>${first.login}</firstAdminLogin>
- </systemPropertyVariables>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
-
- <profile>
- <id>wildfly-8-managed</id>
- <activation>
- <activeByDefault>true</activeByDefault>
- </activation>
- <dependencies>
- <dependency>
- <groupId>org.wildfly</groupId>
- <artifactId>wildfly-arquillian-container-managed</artifactId>
- <scope>test</scope>
- </dependency>
- </dependencies>
- <properties>
- <install.directory>${project.build.directory}/install</install.directory>
- <jbossHome>${install.directory}/keycloak-${project.version}</jbossHome>
- </properties>
- <build>
- <plugins>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-dependency-plugin</artifactId>
- <version>2.10</version>
- <executions>
- <execution>
- <id>unpack</id>
- <phase>process-test-resources</phase>
- <goals>
- <goal>unpack</goal>
- </goals>
- <configuration>
- <artifactItems>
- <artifactItem>
- <groupId>org.keycloak</groupId>
- <artifactId>keycloak-server-dist</artifactId>
- <type>zip</type>
- <overWrite>false</overWrite>
- </artifactItem>
- </artifactItems>
- <outputDirectory>${install.directory}</outputDirectory>
- <overWriteReleases>false</overWriteReleases>
- <overWriteSnapshots>true</overWriteSnapshots>
- </configuration>
- </execution>
- </executions>
- </plugin>
- <plugin>
- <groupId>org.apache.maven.plugins</groupId>
- <artifactId>maven-surefire-plugin</artifactId>
- <configuration>
- <systemPropertyVariables>
- <shouldDeploy>false</shouldDeploy>
- <arquillian.launch>wildfly-8-managed</arquillian.launch>
- <browser>${browser}</browser>
- <jbossHome>${jbossHome}</jbossHome>
- </systemPropertyVariables>
- </configuration>
- </plugin>
- </plugins>
- </build>
- </profile>
- </profiles>
+
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak Integration TestSuite with Arquillian</name>
+
+ <modules>
+ <module>servers</module>
+ <module>tests</module>
+ </modules>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <version>1.0</version>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+
</project>
testsuite/integration-arquillian/README.md 66(+53 -13)
diff --git a/testsuite/integration-arquillian/README.md b/testsuite/integration-arquillian/README.md
index a81701d..7620d98 100644
--- a/testsuite/integration-arquillian/README.md
+++ b/testsuite/integration-arquillian/README.md
@@ -1,21 +1,61 @@
-Testing admin console with Arquillian
-=====================================
+# Keycloak Integration Testsuite with Arquillian
-There are currently two ways of running the tests with help of Arquillian.
+## Structure
-Remote mode
-----------
+```
+integration-arquillian
+│
+├──servers (submodules enabled via profiles)
+│ ├──wildfly
+│ └──eap6
+│
+└──tests
+ ├──base
+ └──adapters (submodules enabled via profiles, all depend on base)
+ ├──wildfly
+ ├──wildfly-relative (needs servers/wildfly)
+ ├──wildfly8
+ ├──as7
+ ├──tomcat
+ └──karaf
-Just simply typle `mvn verify` and you are all set. This requires the instance of Wildfly with embedded Keycloak to be already running.
+```
-Managed mode
-------------
+## General Concepts
-You need to pass two arguments to Maven, first is location of your Wildfly server with embedded Keycloak and the other is name of the profile.
+The testsuite supports **multiple server runtimes** for the Keycloak server.
+The **default is Undertow** which is the fastest and easiest option, and runs in the same JVM as the tests.
- mvn verify -Pwildfly-8-managed -DjbossHome=/your/server/location
+Other options are **Wildfly 9** and **EAP 6**. These have some additional requirements and limitations:
+1. The selected server module must be built before any tests can be run.
+All server-side configuration is done during this build (e.g. datasource configuration).
+Once server artifact is built the tests modules can unpack it via `maven-dependency-plugin` into their working directory before running.
+2. Before the selected server module can be built the `keycloak/distribution` module also needs to be built.
-Browser
--------
+### Server Runtimes
-There are currently two supported browsers - PhantomJS and Firefox. PhantomJS is the default one, in order to use Firefox just specify `-Dbrowser=firefox` parameter in the Maven command.
+TODO: explain why separate module, list config options, note on migration modules
+
+### Base Testsuite
+
+login flows + account management
+
+admin ui
+
+abstract adapter tests
+
+### Adapter Tests
+
+test servlets: demo, session
+
+examples
+
+## Running the Tests
+
+### Undertow
+
+### Wildfly or EAP 6
+
+### Adapters
+
+### Supported Browsers
\ No newline at end of file
testsuite/integration-arquillian/README_old.md 189(+189 -0)
diff --git a/testsuite/integration-arquillian/README_old.md b/testsuite/integration-arquillian/README_old.md
new file mode 100644
index 0000000..76357ce
--- /dev/null
+++ b/testsuite/integration-arquillian/README_old.md
@@ -0,0 +1,189 @@
+# Keycloak Integration Testsuite with Arquillian
+
+*OUT OF DATE - NEEDS REWRITE*
+
+## Usage
+
+Running the tests: `mvn test` or `mvn clean test`
+
+## Test suite
+
+### Selecting container for Keycloak Server
+
+The testsuite requires a container for Keycloak Server to be selected.
+This container is used by all tests in the suite during a single test execution.
+
+*By default* the tests run with server on embedded *Undertow*.
+A different container can be selected with profile, e.g. `-Pauth-server-wildfly`.
+
+### Containers Supported for Keycloak Server
+
+| Container | Arquillian Qualifier | Maven | Dependencies |
+| --- | --- | --- | --- |
+| **Undertow** | `auth-server-undertow` | **default** | `undertow-core`, `resteasy-undertow` |
+| **Wildfly 9** | `auth-server-wildfly` | `-Pauth-server-wildfly` | `keycloak-server-dist` or `wildfly-dist`+`keycloak-server-overlay` |
+| **EAP 6.4** | `auth-server-eap6` | `-Pauth-server-eap6` | `keycloak-server-dist` or `eap6-dist`+`keycloak-server-overlay` |
+
+See the relevant container definitions in `arquillian.xml` located in the **test resources** folder.
+
+### Test Class Hierarchy
+```
+AbstractKeycloakTest
+├── AbstractAdminConsoleTest
+└── AbstractAdapterTest
+```
+
+### AbstractKeycloakTest
+
+Handles test realms. Provides Admin Client for REST operations.
+
+* **@BeforeClass**
+ 1. Updates the admin password to enable the admin user.
+* **@Before**
+ 1. Initiates admin client
+ 2. Imports test realms. (Loading test realms is overriden in subclasses.)
+* **@After**
+ 1. Removes test realms.
+ 2. Closes admin client.
+
+### ContainersTestEnricher
+
+Manages *container lifecycles*.
+
+`ContainersTestEnricher` is a custom Arquillian observer that handles lifecycles of auth server and app server containers for each test class.
+Containers are started during `@BeforeClass` and shut down during `@AfterClass` event.
+
+*Optionally* each test class can be annotated with `@AuthServerContainer("qualifier")` and `@AppServerConatiner("qualifier")` annotations
+to indicate containers required for the test.
+
+* In case `@AuthServerContainer` is not present the *auth server qualifier* is loaded from `auth.server.container` property.
+* In case `@AppServerContainer` is not present or it's value is the same as *auth server qualifier*, the app server isn't started for the test class.
+
+## Admin Console Tests
+
+Tests for admin console are located in `org/keycloak/testsuite/console`.
+Related non-test classes are located on the same path in the **main sources**.
+
+Admin console tests are **ENABLED by default**. They can be disabled by `-P no-console`.
+
+
+## Adapter Tests
+
+Adapter tests are located in `org/keycloak/testsuite/adapter`.
+Related non-test classes can be found on the same path in the **main sources**.
+
+Adapter tests are **DISABLED by default**. They can be enabled by profiles.
+Multiple profiles can be enabled for a single test execution.
+
+*Note:* When testing adapter with multiple containers in a single run it is better
+to use the `--fail-at-end` (`-fae`) strategy instead of the default `--fail-fast` one.
+This will allow Maven to continue building other modules even if some of them have test failures.
+
+### Containers Supported for Adapter Tests
+
+| Container | Arquillian Qualifier | Maven | Dependencies |
+| --- | --- | --- | --- |
+| **Wildfly 9** Relative | `auth-server-wildfly` | `-Pauth-server-wildfly` | `keycloak-server-dist` or `wildfly-dist`+`keycloak-server-overlay`, `keycloak-adapter-dist-wf9` |
+| **Wildfly 9** | `app-server-wildfly` | `-Papp-server-wildfly` | `wildfly-dist`, `keycloak-adapter-dist-wf9` |
+| **Wildfly 8** | `app-server-wildfly` | `-Papp-server-wildfly8` | `wildfly-dist:8.2.1.Final`, `keycloak-adapter-dist-wf8` |
+| **JBoss AS 7** | `app-server-as7` | `-Papp-server-as7` | `jboss-as-dist`, `keycloak-adapter-dist-as7` |
+| **Tomcat 8** | `app-server-tomcat` | `-Papp-server-tomcat` | `tomcat`, `keycloak-tomcat8-adapter-dist` |
+| **Karaf 3** | `app-server-karaf` | `-Papp-server-karaf` | `apache-camel`, `apache-cxf`, `keycloak-osgi-features`, `keycloak-fuse-example-features` |
+
+See the relevant container definitions in `tests/adapter/<container>/src/main/xslt/arquillian.xsl`.
+
+***Important:*** Arquillian cannot load multiple controllers for JBossAS/Wildfly containers in a single run (because same class name)
+but a different controller is required for JBossAS7/EAP6 than for WF8/9. Because of this:
+
+ - Adapter tests for *Wildfly 8/9* cannot be run against server on *EAP 6*. `-Papp-server-wildfly*` ⇒ `!auth-server-eap6`
+ - Adapter tests for *JBossAS 7* can only be run against server on *EAP 6*. `-Papp-server-as7,auth-server-eap6`
+
+### Adapter Test Types
+
+1. Using **test servlets**.
+2. Using **example/demo wars**.
+
+```
+AbstractKeycloakTest
+└── AbstractAdapterTest
+ ├── AbstractServletsAdapterTest
+ | ├── Relative…
+ | ├── Wildfly…
+ | ├── Tomcat…
+ | …
+ └── AbstractExampleAdapterTest
+ ├── AbstractDemoExampleAdapterTest
+ | ├── Relative…
+ | ├── Wildfly…
+ | ├── Tomcat…
+ | …
+ ├── AbstractBasicAuthExampleAdapterTest
+ | ├── Relative…
+ | ├── Wildfly…
+ | ├── Tomcat…
+ | …
+ …
+```
+
+### Relative vs Non-relative scenario
+
+The test suite can handle both types.
+It automatically modifies imported test realms and deployments' adapter configs based on scenario type.
+
+| Scenario | Description | Realm config (server side) | Adapter config (client side) |
+| --- | --- | --- | --- |
+| **Relative** | Both Keycloak Server and test apps running in the same container. | client `baseUrl`, `adminUrl` and `redirect-uris` can be relative | `auth-server-url` can be relative |
+| **Non-relative** | Test apps run in a different container than Keycloak Server. | client `baseUrl`, `adminUrl` and `redirect-uris` need to include FQDN of the app server | `auth-server-url` needs to include FQDN of the auth server|
+
+### Adapter Libraries Mode
+
+1. **Provided.** By container, e.g. as a subsystem. *Default.*
+2. **Bundled.** In the deployed war in `/WEB-INF/libs`. Enable with `-Dadapter.libs.bundled`. *Wildfly only*.
+
+### Adapter Config Mode
+
+1. ~~**Provided.** In `standalone.xml` using `secure-deployment`. *Wildfly only.*~~ WIP
+2. **Bundled.** In the deployed war in `/WEB-INF/keycloak.json`. *Default.*
+
+### Adapters Test Coverage
+
+| Module | Coverage | Supported Containers |
+| --- | --- | --- |
+| ***Test Servlets*** | Good | All |
+| **Demo** | Minimal, WIP | `auth-server-wildfly` (relative) |
+| **Admin Client** | |
+| **Cordova** | |
+| **CORS** | |
+| **JS Console** | Good | `auth-server-wildfly` (relative) |
+| **Providers** | |
+| Themes | |
+| Multitenancy | WIP | |
+| **Basic Auth** | Good | All |
+| **Fuse** | Good | `app-server-karaf` |
+| SAML | |
+| LDAP | |
+| Kerberos | |
+
+## Supported Browsers
+
+| Browser | Maven |
+| --- | --- |
+| **PhantomJS** | `-Dbrowser=phantomjs` **default** |
+| **Firefox** | `-Dbrowser=firefox` |
+
+
+## Custom Arquillian Extensions
+
+Custom extensions are registered in `META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension`.
+
+* Multiple containers extension
+ * Replaces Arquillian's default container handling.
+ * Allows to manage multiple container instances of different types within a single test run.
+ * Allows to skip loading disabled containers based on `enabled` config property in `arquillian.xml`.
+* Custom extension
+ * `ContainersTestEnricher` - Handles lifecycles of auth-server and app-server.
+ * `CustomUndertowContainer` - A custom container controller for JAX-RS-enabled Undertow with Keycloak Server.
+ * `DeploymentArchiveProcessor` - Modifies adapter config before deployment on app server based on relative/non-relative scenario.
+ * `URLProvider` - Fixes URLs injected by Arquillian which contain 127.0.0.1 instead of localhost.
+ * `JiraTestExecutionDecider` - Skipping tests for unresolved JIRAs.
+
diff --git a/testsuite/integration-arquillian/servers/eap6/assembly.xml b/testsuite/integration-arquillian/servers/eap6/assembly.xml
new file mode 100644
index 0000000..537dd4e
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap6/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+
+ <id>auth-server-eap6</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-${project.version}</outputDirectory>
+ <excludes>
+ <exclude>**/*.sh</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-${project.version}</outputDirectory>
+ <includes>
+ <include>**/*.sh</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/testsuite/integration-arquillian/servers/eap6/pom.xml b/testsuite/integration-arquillian/servers/eap6/pom.xml
new file mode 100644
index 0000000..4bab815
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap6/pom.xml
@@ -0,0 +1,184 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-server-eap6</artifactId>
+ <packaging>pom</packaging>
+ <name>Server on EAP 6</name>
+
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/jboss-eap-6.4</keycloak.server.home>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${jboss.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-overlay-eap6</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-eap6-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-eap6-and-server-overlay</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${jboss.version}</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-overlay-eap6</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${keycloak.server.home}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>move-standalone-keycloak-xml</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <move file="${keycloak.server.home}/standalone/configuration/standalone-keycloak.xml"
+ tofile="${keycloak.server.home}/standalone/configuration/standalone.xml"/>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-zip</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-eap6-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${keycloak.server.home}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/servers/eap6/src/main/xslt/datasource.xsl b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/datasource.xsl
new file mode 100644
index 0000000..c06899f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/datasource.xsl
@@ -0,0 +1,94 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+
+ <!-- Remove keycloak datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name='KeycloakDS']">
+ </xsl:template>
+
+ <xsl:param name="jdbc.url" select="'jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE'"/>
+ <xsl:param name="driver" select="'h2'"/>
+
+ <xsl:param name="username" select="'sa'"/>
+ <xsl:param name="password" select="'sa'"/>
+
+ <xsl:param name="min.poolsize" select="'10'"/>
+ <xsl:param name="max.poolsize" select="'50'"/>
+ <xsl:param name="pool.prefill" select="'true'"/>
+
+ <xsl:variable name="newDatasourceDefinition">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>
+ <xsl:value-of select="$jdbc.url"/>
+ </connection-url>
+ <driver>
+ <xsl:value-of select="$driver"/>
+ </driver>
+ <security>
+ <user-name>
+ <xsl:value-of select="$username"/>
+ </user-name>
+ <password>
+ <xsl:value-of select="$password"/>
+ </password>
+ </security>
+ <pool>
+ <min-pool-size>
+ <xsl:value-of select="$min.poolsize"/>
+ </min-pool-size>
+ <max-pool-size>
+ <xsl:value-of select="$max.poolsize"/>
+ </max-pool-size>
+ <prefill>
+ <xsl:value-of select="$pool.prefill"/>
+ </prefill>
+ </pool>
+ </datasource>
+ </xsl:variable>
+
+ <xsl:variable name="newDriverDefinition">
+ <xsl:if test="$driver != 'h2'">
+ <driver name="{$driver}" module="com.{$driver}" />
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Add new datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDatasourceDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Add new driver definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='drivers' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDriverDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/eap6/src/main/xslt/module.xsl b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/module.xsl
new file mode 100644
index 0000000..88ac56b
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/module.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:m="urn:jboss:module:1.3"
+ version="2.0"
+ exclude-result-prefixes="xalan m">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
+
+
+ <xsl:param name="database" select="''"/>
+ <xsl:param name="version" select="''"/>
+
+ <xsl:variable name="newModuleDefinition">
+ <module xmlns="urn:jboss:module:1.3" name="com.{$database}">
+ <resources>
+ <resource-root path="{$database}-{$version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+ </module>
+ </xsl:variable>
+
+ <!-- clear whole document -->
+ <xsl:template match="/*" />
+
+ <!-- Copy new module definition. -->
+ <xsl:template match="/*">
+ <xsl:copy-of select="$newModuleDefinition"/>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/eap6/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..4ffc2c6
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/eap6/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:1.7"
+ xmlns:ds="urn:jboss:domain:datasources:1.2"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.1"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/pom.xml b/testsuite/integration-arquillian/servers/pom.xml
new file mode 100644
index 0000000..254e40e
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/pom.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-servers</artifactId>
+ <packaging>pom</packaging>
+ <name>Servers</name>
+
+ <profiles>
+ <profile>
+ <id>auth-server-wildfly</id>
+ <modules>
+ <module>wildfly</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>auth-server-eap6</id>
+ <modules>
+ <!--doesn't work yet, WIP-->
+ <module>eap6</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>migration-kc14</id>
+ <modules>
+ <module>wildfly_kc14</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>migration-kc13</id>
+ <modules>
+ <module>wildfly_kc13</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>migration-kc12</id>
+ <modules>
+ <module>wildfly_kc12</module>
+ </modules>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/servers/wildfly/assembly.xml b/testsuite/integration-arquillian/servers/wildfly/assembly.xml
new file mode 100644
index 0000000..bfcad35
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+
+ <id>auth-server-wildfly</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-${project.version}</outputDirectory>
+ <excludes>
+ <exclude>**/*.sh</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-${project.version}</outputDirectory>
+ <includes>
+ <include>**/*.sh</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/testsuite/integration-arquillian/servers/wildfly/pom.xml b/testsuite/integration-arquillian/servers/wildfly/pom.xml
new file mode 100644
index 0000000..e2b91e7
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/pom.xml
@@ -0,0 +1,372 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-server-wildfly</artifactId>
+ <packaging>pom</packaging>
+ <name>Server on Wildfly</name>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-zip</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>server-overlay</id>
+ <activation>
+ <property>
+ <name>server-overlay</name>
+ </property>
+ </activation>
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/wildfly-${wildfly.version}</keycloak.server.home>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-wildfly-and-server-overlay</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-dist</artifactId>
+ <version>${wildfly.version}</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-overlay</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${keycloak.server.home}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-antrun-plugin</artifactId>
+ <version>1.8</version>
+ <executions>
+ <execution>
+ <id>move-standalone-keycloak-xml</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>run</goal>
+ </goals>
+ <configuration>
+ <tasks>
+ <move file="${keycloak.server.home}/standalone/configuration/standalone-keycloak.xml"
+ tofile="${keycloak.server.home}/standalone/configuration/standalone.xml"/>
+ </tasks>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>server-dist</id>
+ <activation>
+ <property>
+ <name>!server-overlay</name>
+ </property>
+ </activation>
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/keycloak-${project.version}</keycloak.server.home>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-server</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${keycloak.server.home}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ <profile>
+ <id>jpa</id>
+ <properties>
+ <jdbc.mvn.driver.deployment.dir>${keycloak.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>enforce-properties</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireProperty>
+ <property>jdbc.mvn.groupId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.artifactId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.version</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.url</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.user</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.password</property>
+ </requireProperty>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>jdbc-driver</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${jdbc.mvn.groupId}</groupId>
+ <artifactId>${jdbc.mvn.artifactId}</artifactId>
+ <version>${jdbc.mvn.version}</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${jdbc.mvn.driver.deployment.dir}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-wildfly-datasource</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <!-- create module.xml in modules -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
+ <stylesheet>src/main/xslt/module.xsl</stylesheet>
+ <includes>
+ <include>module.xml</include>
+ </includes>
+ <outputDir>${jdbc.mvn.driver.deployment.dir}</outputDir>
+ <parameters>
+ <parameter>
+ <name>database</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>version</name>
+ <value>${jdbc.mvn.version}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add datasource to standalone.xml -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/datasource.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ <parameters>
+ <parameter>
+ <name>jdbc.url</name>
+ <value>${keycloak.connectionsJpa.url}</value>
+ </parameter>
+ <parameter>
+ <name>driver</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>${keycloak.connectionsJpa.user}</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>${keycloak.connectionsJpa.password}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add logger for org.hibernate.dialect.Dialect -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/add-dialect-logger.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/add-dialect-logger.xsl b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/add-dialect-logger.xsl
new file mode 100644
index 0000000..b5dc8c4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/add-dialect-logger.xsl
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ version="2.0"
+ exclude-result-prefixes="xalan">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:logging:'"/>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='root-logger' and starts-with(namespace-uri(), $nsDS)]">
+ <logger category="org.hibernate.dialect.Dialect">
+ <level name="ALL"/>
+ </logger>
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/datasource.xsl b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/datasource.xsl
new file mode 100644
index 0000000..c06899f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/datasource.xsl
@@ -0,0 +1,94 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+
+ <!-- Remove keycloak datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name='KeycloakDS']">
+ </xsl:template>
+
+ <xsl:param name="jdbc.url" select="'jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE'"/>
+ <xsl:param name="driver" select="'h2'"/>
+
+ <xsl:param name="username" select="'sa'"/>
+ <xsl:param name="password" select="'sa'"/>
+
+ <xsl:param name="min.poolsize" select="'10'"/>
+ <xsl:param name="max.poolsize" select="'50'"/>
+ <xsl:param name="pool.prefill" select="'true'"/>
+
+ <xsl:variable name="newDatasourceDefinition">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>
+ <xsl:value-of select="$jdbc.url"/>
+ </connection-url>
+ <driver>
+ <xsl:value-of select="$driver"/>
+ </driver>
+ <security>
+ <user-name>
+ <xsl:value-of select="$username"/>
+ </user-name>
+ <password>
+ <xsl:value-of select="$password"/>
+ </password>
+ </security>
+ <pool>
+ <min-pool-size>
+ <xsl:value-of select="$min.poolsize"/>
+ </min-pool-size>
+ <max-pool-size>
+ <xsl:value-of select="$max.poolsize"/>
+ </max-pool-size>
+ <prefill>
+ <xsl:value-of select="$pool.prefill"/>
+ </prefill>
+ </pool>
+ </datasource>
+ </xsl:variable>
+
+ <xsl:variable name="newDriverDefinition">
+ <xsl:if test="$driver != 'h2'">
+ <driver name="{$driver}" module="com.{$driver}" />
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Add new datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDatasourceDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Add new driver definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='drivers' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDriverDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/module.xsl b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/module.xsl
new file mode 100644
index 0000000..88ac56b
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/module.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:m="urn:jboss:module:1.3"
+ version="2.0"
+ exclude-result-prefixes="xalan m">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
+
+
+ <xsl:param name="database" select="''"/>
+ <xsl:param name="version" select="''"/>
+
+ <xsl:variable name="newModuleDefinition">
+ <module xmlns="urn:jboss:module:1.3" name="com.{$database}">
+ <resources>
+ <resource-root path="{$database}-{$version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+ </module>
+ </xsl:variable>
+
+ <!-- clear whole document -->
+ <xsl:template match="/*" />
+
+ <!-- Copy new module definition. -->
+ <xsl:template match="/*">
+ <xsl:copy-of select="$newModuleDefinition"/>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..a483717
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc12/assembly.xml b/testsuite/integration-arquillian/servers/wildfly_kc12/assembly.xml
new file mode 100644
index 0000000..b3e9e20
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc12/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+
+ <id>auth-server-wildfly-kc14</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.2.0.Final</outputDirectory>
+ <excludes>
+ <exclude>**/*.sh</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.2.0.Final</outputDirectory>
+ <includes>
+ <include>**/*.sh</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc12/pom.xml b/testsuite/integration-arquillian/servers/wildfly_kc12/pom.xml
new file mode 100644
index 0000000..2d98af1
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc12/pom.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-server-wildfly-kc12 </artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak 1.2.0.Final on Wildfly</name>
+
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/keycloak-1.2.0.Final</keycloak.server.home>
+ <jdbc.mvn.driver.deployment.dir>${keycloak.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>enforce-properties</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireProperty>
+ <property>jdbc.mvn.groupId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.artifactId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.version</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.url</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.user</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.password</property>
+ </requireProperty>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-server</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-dist</artifactId>
+ <version>1.2.0.Final</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>jdbc-driver</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${jdbc.mvn.groupId}</groupId>
+ <artifactId>${jdbc.mvn.artifactId}</artifactId>
+ <version>${jdbc.mvn.version}</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${jdbc.mvn.driver.deployment.dir}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-wildfly-datasource</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <!-- create module.xml in modules -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
+ <stylesheet>src/main/xslt/module.xsl</stylesheet>
+ <includes>
+ <include>module.xml</include>
+ </includes>
+ <outputDir>${jdbc.mvn.driver.deployment.dir}</outputDir>
+ <parameters>
+ <parameter>
+ <name>database</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>version</name>
+ <value>${jdbc.mvn.version}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add datasource to standalone.xml -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/datasource.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ <parameters>
+ <parameter>
+ <name>jdbc.url</name>
+ <value>${keycloak.connectionsJpa.url}</value>
+ </parameter>
+ <parameter>
+ <name>driver</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>${keycloak.connectionsJpa.user}</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>${keycloak.connectionsJpa.password}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add logger for org.hibernate.dialect.Dialect to standalone.xml-->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/add-dialect-logger.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-zip</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/add-dialect-logger.xsl b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/add-dialect-logger.xsl
new file mode 100644
index 0000000..b5dc8c4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/add-dialect-logger.xsl
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ version="2.0"
+ exclude-result-prefixes="xalan">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:logging:'"/>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='root-logger' and starts-with(namespace-uri(), $nsDS)]">
+ <logger category="org.hibernate.dialect.Dialect">
+ <level name="ALL"/>
+ </logger>
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/datasource.xsl b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/datasource.xsl
new file mode 100644
index 0000000..c06899f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/datasource.xsl
@@ -0,0 +1,94 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+
+ <!-- Remove keycloak datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name='KeycloakDS']">
+ </xsl:template>
+
+ <xsl:param name="jdbc.url" select="'jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE'"/>
+ <xsl:param name="driver" select="'h2'"/>
+
+ <xsl:param name="username" select="'sa'"/>
+ <xsl:param name="password" select="'sa'"/>
+
+ <xsl:param name="min.poolsize" select="'10'"/>
+ <xsl:param name="max.poolsize" select="'50'"/>
+ <xsl:param name="pool.prefill" select="'true'"/>
+
+ <xsl:variable name="newDatasourceDefinition">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>
+ <xsl:value-of select="$jdbc.url"/>
+ </connection-url>
+ <driver>
+ <xsl:value-of select="$driver"/>
+ </driver>
+ <security>
+ <user-name>
+ <xsl:value-of select="$username"/>
+ </user-name>
+ <password>
+ <xsl:value-of select="$password"/>
+ </password>
+ </security>
+ <pool>
+ <min-pool-size>
+ <xsl:value-of select="$min.poolsize"/>
+ </min-pool-size>
+ <max-pool-size>
+ <xsl:value-of select="$max.poolsize"/>
+ </max-pool-size>
+ <prefill>
+ <xsl:value-of select="$pool.prefill"/>
+ </prefill>
+ </pool>
+ </datasource>
+ </xsl:variable>
+
+ <xsl:variable name="newDriverDefinition">
+ <xsl:if test="$driver != 'h2'">
+ <driver name="{$driver}" module="com.{$driver}" />
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Add new datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDatasourceDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Add new driver definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='drivers' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDriverDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/module.xsl b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/module.xsl
new file mode 100644
index 0000000..88ac56b
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc12/src/main/xslt/module.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:m="urn:jboss:module:1.3"
+ version="2.0"
+ exclude-result-prefixes="xalan m">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
+
+
+ <xsl:param name="database" select="''"/>
+ <xsl:param name="version" select="''"/>
+
+ <xsl:variable name="newModuleDefinition">
+ <module xmlns="urn:jboss:module:1.3" name="com.{$database}">
+ <resources>
+ <resource-root path="{$database}-{$version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+ </module>
+ </xsl:variable>
+
+ <!-- clear whole document -->
+ <xsl:template match="/*" />
+
+ <!-- Copy new module definition. -->
+ <xsl:template match="/*">
+ <xsl:copy-of select="$newModuleDefinition"/>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc13/assembly.xml b/testsuite/integration-arquillian/servers/wildfly_kc13/assembly.xml
new file mode 100644
index 0000000..08e3ebf
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc13/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+
+ <id>auth-server-wildfly-kc14</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.3.1.Final</outputDirectory>
+ <excludes>
+ <exclude>**/*.sh</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.3.1.Final</outputDirectory>
+ <includes>
+ <include>**/*.sh</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc13/pom.xml b/testsuite/integration-arquillian/servers/wildfly_kc13/pom.xml
new file mode 100644
index 0000000..58be6cc
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc13/pom.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-server-wildfly-kc13</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak 1.3.1.Final on Wildfly</name>
+
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/keycloak-1.3.1.Final</keycloak.server.home>
+ <jdbc.mvn.driver.deployment.dir>${keycloak.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>enforce-properties</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireProperty>
+ <property>jdbc.mvn.groupId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.artifactId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.version</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.url</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.user</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.password</property>
+ </requireProperty>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-server</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-dist</artifactId>
+ <version>1.3.1.Final</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>jdbc-driver</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${jdbc.mvn.groupId}</groupId>
+ <artifactId>${jdbc.mvn.artifactId}</artifactId>
+ <version>${jdbc.mvn.version}</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${jdbc.mvn.driver.deployment.dir}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-wildfly-datasource</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <!-- create module.xml in modules -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
+ <stylesheet>src/main/xslt/module.xsl</stylesheet>
+ <includes>
+ <include>module.xml</include>
+ </includes>
+ <outputDir>${jdbc.mvn.driver.deployment.dir}</outputDir>
+ <parameters>
+ <parameter>
+ <name>database</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>version</name>
+ <value>${jdbc.mvn.version}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add datasource to standalone.xml -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/datasource.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ <parameters>
+ <parameter>
+ <name>jdbc.url</name>
+ <value>${keycloak.connectionsJpa.url}</value>
+ </parameter>
+ <parameter>
+ <name>driver</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>${keycloak.connectionsJpa.user}</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>${keycloak.connectionsJpa.password}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add logger for org.hibernate.dialect.Dialect to standalone.xml-->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/add-dialect-logger.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-zip</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/add-dialect-logger.xsl b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/add-dialect-logger.xsl
new file mode 100644
index 0000000..b5dc8c4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/add-dialect-logger.xsl
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ version="2.0"
+ exclude-result-prefixes="xalan">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:logging:'"/>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='root-logger' and starts-with(namespace-uri(), $nsDS)]">
+ <logger category="org.hibernate.dialect.Dialect">
+ <level name="ALL"/>
+ </logger>
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/datasource.xsl b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/datasource.xsl
new file mode 100644
index 0000000..c06899f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/datasource.xsl
@@ -0,0 +1,94 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+
+ <!-- Remove keycloak datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name='KeycloakDS']">
+ </xsl:template>
+
+ <xsl:param name="jdbc.url" select="'jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE'"/>
+ <xsl:param name="driver" select="'h2'"/>
+
+ <xsl:param name="username" select="'sa'"/>
+ <xsl:param name="password" select="'sa'"/>
+
+ <xsl:param name="min.poolsize" select="'10'"/>
+ <xsl:param name="max.poolsize" select="'50'"/>
+ <xsl:param name="pool.prefill" select="'true'"/>
+
+ <xsl:variable name="newDatasourceDefinition">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>
+ <xsl:value-of select="$jdbc.url"/>
+ </connection-url>
+ <driver>
+ <xsl:value-of select="$driver"/>
+ </driver>
+ <security>
+ <user-name>
+ <xsl:value-of select="$username"/>
+ </user-name>
+ <password>
+ <xsl:value-of select="$password"/>
+ </password>
+ </security>
+ <pool>
+ <min-pool-size>
+ <xsl:value-of select="$min.poolsize"/>
+ </min-pool-size>
+ <max-pool-size>
+ <xsl:value-of select="$max.poolsize"/>
+ </max-pool-size>
+ <prefill>
+ <xsl:value-of select="$pool.prefill"/>
+ </prefill>
+ </pool>
+ </datasource>
+ </xsl:variable>
+
+ <xsl:variable name="newDriverDefinition">
+ <xsl:if test="$driver != 'h2'">
+ <driver name="{$driver}" module="com.{$driver}" />
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Add new datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDatasourceDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Add new driver definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='drivers' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDriverDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/module.xsl b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/module.xsl
new file mode 100644
index 0000000..88ac56b
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc13/src/main/xslt/module.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:m="urn:jboss:module:1.3"
+ version="2.0"
+ exclude-result-prefixes="xalan m">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
+
+
+ <xsl:param name="database" select="''"/>
+ <xsl:param name="version" select="''"/>
+
+ <xsl:variable name="newModuleDefinition">
+ <module xmlns="urn:jboss:module:1.3" name="com.{$database}">
+ <resources>
+ <resource-root path="{$database}-{$version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+ </module>
+ </xsl:variable>
+
+ <!-- clear whole document -->
+ <xsl:template match="/*" />
+
+ <!-- Copy new module definition. -->
+ <xsl:template match="/*">
+ <xsl:copy-of select="$newModuleDefinition"/>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc14/assembly.xml b/testsuite/integration-arquillian/servers/wildfly_kc14/assembly.xml
new file mode 100644
index 0000000..da4b459
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc14/assembly.xml
@@ -0,0 +1,29 @@
+<assembly>
+
+ <id>auth-server-wildfly-kc14</id>
+
+ <formats>
+ <format>zip</format>
+ </formats>
+
+ <includeBaseDirectory>false</includeBaseDirectory>
+
+ <fileSets>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.4.0.Final</outputDirectory>
+ <excludes>
+ <exclude>**/*.sh</exclude>
+ </excludes>
+ </fileSet>
+ <fileSet>
+ <directory>${keycloak.server.home}</directory>
+ <outputDirectory>keycloak-1.4.0.Final</outputDirectory>
+ <includes>
+ <include>**/*.sh</include>
+ </includes>
+ <fileMode>0755</fileMode>
+ </fileSet>
+ </fileSets>
+
+</assembly>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc14/pom.xml b/testsuite/integration-arquillian/servers/wildfly_kc14/pom.xml
new file mode 100644
index 0000000..ba4ff50
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc14/pom.xml
@@ -0,0 +1,199 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-servers</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-server-wildfly-kc14</artifactId>
+ <packaging>pom</packaging>
+ <name>Keycloak 1.4.0.Final on Wildfly</name>
+
+ <properties>
+ <keycloak.server.home>${project.build.directory}/unpacked/keycloak-1.4.0.Final</keycloak.server.home>
+ <jdbc.mvn.driver.deployment.dir>${keycloak.server.home}/modules/system/layers/base/com/${jdbc.mvn.artifactId}/main</jdbc.mvn.driver.deployment.dir>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-enforcer-plugin</artifactId>
+ <version>1.4</version>
+ <executions>
+ <execution>
+ <id>enforce-properties</id>
+ <goals>
+ <goal>enforce</goal>
+ </goals>
+ <configuration>
+ <rules>
+ <requireProperty>
+ <property>jdbc.mvn.groupId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.artifactId</property>
+ </requireProperty>
+ <requireProperty>
+ <property>jdbc.mvn.version</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.url</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.user</property>
+ </requireProperty>
+ <requireProperty>
+ <property>keycloak.connectionsJpa.password</property>
+ </requireProperty>
+ </rules>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-server</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-server-dist</artifactId>
+ <version>1.4.0.Final</version>
+ <type>zip</type>
+ <outputDirectory>${project.build.directory}/unpacked</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ <execution>
+ <id>jdbc-driver</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>${jdbc.mvn.groupId}</groupId>
+ <artifactId>${jdbc.mvn.artifactId}</artifactId>
+ <version>${jdbc.mvn.version}</version>
+ <type>jar</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${jdbc.mvn.driver.deployment.dir}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-wildfly-datasource</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <!-- create module.xml in modules -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/modules/system/layers/base/com/h2database/h2/main</dir>
+ <stylesheet>src/main/xslt/module.xsl</stylesheet>
+ <includes>
+ <include>module.xml</include>
+ </includes>
+ <outputDir>${jdbc.mvn.driver.deployment.dir}</outputDir>
+ <parameters>
+ <parameter>
+ <name>database</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>version</name>
+ <value>${jdbc.mvn.version}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add datasource to standalone.xml -->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/datasource.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ <parameters>
+ <parameter>
+ <name>jdbc.url</name>
+ <value>${keycloak.connectionsJpa.url}</value>
+ </parameter>
+ <parameter>
+ <name>driver</name>
+ <value>${jdbc.mvn.artifactId}</value>
+ </parameter>
+ <parameter>
+ <name>username</name>
+ <value>${keycloak.connectionsJpa.user}</value>
+ </parameter>
+ <parameter>
+ <name>password</name>
+ <value>${keycloak.connectionsJpa.password}</value>
+ </parameter>
+ </parameters>
+ </transformationSet>
+ <!-- add logger for org.hibernate.dialect.Dialect to standalone.xml-->
+ <transformationSet>
+ <dir>${keycloak.server.home}/standalone/configuration</dir>
+ <stylesheet>src/main/xslt/add-dialect-logger.xsl</stylesheet>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <outputDir>${keycloak.server.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <artifactId>maven-assembly-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>create-zip</id>
+ <phase>package</phase>
+ <goals>
+ <goal>single</goal>
+ </goals>
+ <configuration>
+ <descriptors>
+ <descriptor>assembly.xml</descriptor>
+ </descriptors>
+ <appendAssemblyId>false</appendAssemblyId>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/add-dialect-logger.xsl b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/add-dialect-logger.xsl
new file mode 100644
index 0000000..b5dc8c4
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/add-dialect-logger.xsl
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ version="2.0"
+ exclude-result-prefixes="xalan">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:logging:'"/>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='root-logger' and starts-with(namespace-uri(), $nsDS)]">
+ <logger category="org.hibernate.dialect.Dialect">
+ <level name="ALL"/>
+ </logger>
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@* | node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/datasource.xsl b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/datasource.xsl
new file mode 100644
index 0000000..c06899f
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/datasource.xsl
@@ -0,0 +1,94 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+
+ <xsl:variable name="nsDS" select="'urn:jboss:domain:datasources:'"/>
+
+ <!-- Remove keycloak datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasource' and starts-with(namespace-uri(), $nsDS) and @pool-name='KeycloakDS']">
+ </xsl:template>
+
+ <xsl:param name="jdbc.url" select="'jdbc:h2:${jboss.server.data.dir}/keycloak;AUTO_SERVER=TRUE'"/>
+ <xsl:param name="driver" select="'h2'"/>
+
+ <xsl:param name="username" select="'sa'"/>
+ <xsl:param name="password" select="'sa'"/>
+
+ <xsl:param name="min.poolsize" select="'10'"/>
+ <xsl:param name="max.poolsize" select="'50'"/>
+ <xsl:param name="pool.prefill" select="'true'"/>
+
+ <xsl:variable name="newDatasourceDefinition">
+ <datasource jndi-name="java:jboss/datasources/KeycloakDS" pool-name="KeycloakDS" enabled="true" use-java-context="true">
+ <connection-url>
+ <xsl:value-of select="$jdbc.url"/>
+ </connection-url>
+ <driver>
+ <xsl:value-of select="$driver"/>
+ </driver>
+ <security>
+ <user-name>
+ <xsl:value-of select="$username"/>
+ </user-name>
+ <password>
+ <xsl:value-of select="$password"/>
+ </password>
+ </security>
+ <pool>
+ <min-pool-size>
+ <xsl:value-of select="$min.poolsize"/>
+ </min-pool-size>
+ <max-pool-size>
+ <xsl:value-of select="$max.poolsize"/>
+ </max-pool-size>
+ <prefill>
+ <xsl:value-of select="$pool.prefill"/>
+ </prefill>
+ </pool>
+ </datasource>
+ </xsl:variable>
+
+ <xsl:variable name="newDriverDefinition">
+ <xsl:if test="$driver != 'h2'">
+ <driver name="{$driver}" module="com.{$driver}" />
+ </xsl:if>
+ </xsl:variable>
+
+ <!-- Add new datasource definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDatasourceDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Add new driver definition. -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='datasources' and starts-with(namespace-uri(), $nsDS)]
+ /*[local-name()='drivers' and starts-with(namespace-uri(), $nsDS)]">
+ <xsl:copy>
+ <xsl:copy-of select="$newDriverDefinition"/>
+ <xsl:apply-templates select="@* | node()" />
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Copy everything else. -->
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/module.xsl b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/module.xsl
new file mode 100644
index 0000000..88ac56b
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/wildfly_kc14/src/main/xslt/module.xsl
@@ -0,0 +1,33 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:m="urn:jboss:module:1.3"
+ version="2.0"
+ exclude-result-prefixes="xalan m">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" />
+
+
+ <xsl:param name="database" select="''"/>
+ <xsl:param name="version" select="''"/>
+
+ <xsl:variable name="newModuleDefinition">
+ <module xmlns="urn:jboss:module:1.3" name="com.{$database}">
+ <resources>
+ <resource-root path="{$database}-{$version}.jar"/>
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="javax.transaction.api"/>
+ </dependencies>
+ </module>
+ </xsl:variable>
+
+ <!-- clear whole document -->
+ <xsl:template match="/*" />
+
+ <!-- Copy new module definition. -->
+ <xsl:template match="/*">
+ <xsl:copy-of select="$newModuleDefinition"/>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/pom.xml b/testsuite/integration-arquillian/tests/adapters/as7/pom.xml
new file mode 100644
index 0000000..701a6ef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/as7/pom.xml
@@ -0,0 +1,137 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-as7</artifactId>
+ <name>Adapter Tests on JBossAS 7</name>
+
+ <properties>
+ <as7.version>7.1.1.Final</as7.version>
+ <app.server.as7.home>${containers.home}/jboss-as-${as7.version}</app.server.as7.home>
+ <adapter.libs.as7>${containers.home}/keycloak-as7-adapter-dist</adapter.libs.as7>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${as7.version}</version>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-arquillian-container-managed</artifactId>
+ <version>7.2.0.Final</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-as7-and-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-dist</artifactId>
+ <version>${as7.version}</version>
+ <type>zip</type>
+ <outputDirectory>${containers.home}</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-as7-adapter-dist</artifactId>
+ <type>zip</type>
+ <outputDirectory>${adapter.libs.as7}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.as7>true</app.server.as7>
+ <app.server.as7.home>${app.server.as7.home}</app.server.as7.home>
+ <adapter.libs.as7>${adapter.libs.as7}</adapter.libs.as7>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <properties>
+ <adapter.libs.as7>${app.server.as7.home}</adapter.libs.as7>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${app.server.as7.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${app.server.as7.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..8970850
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/arquillian.xsl
@@ -0,0 +1,36 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-as7" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.as7}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${app.server.as7.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${app.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementAddress">localhost</property>
+ <property name="managementPort">${app.server.management.port.jmx}</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..5aac0f0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:1.2"
+ xmlns:ds="urn:jboss:domain:datasources:1.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.1"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java
new file mode 100644
index 0000000..f0258b2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7DemoServletsAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-as7")
+public class AS7DemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java
new file mode 100644
index 0000000..9362ecd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/as7/src/test/java/org/keycloak/testsuite/adapter/servlet/AS7SessionServletAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-as7")
+public class AS7SessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/karaf/pom.xml b/testsuite/integration-arquillian/tests/adapters/karaf/pom.xml
new file mode 100644
index 0000000..06328fc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/karaf/pom.xml
@@ -0,0 +1,121 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-karaf</artifactId>
+ <name>Adapter Tests on Karaf</name>
+
+ <properties>
+ <karaf.version>3.0.3</karaf.version>
+ <karaf.home>${project.build.directory}/assembly</karaf.home>
+
+ <!--fuse examples expect auth server on 8080-->
+ <auth.server.port.offset>0</auth.server.port.offset>
+ <auth.server.http.port>8080</auth.server.http.port>
+ <auth.server.management.port>9990</auth.server.management.port>
+ <!--fuse examples expect default karaf http port 8181-->
+ <app.server.http.port>8181</app.server.http.port>
+
+ </properties>
+
+ <dependencies>
+ <!-- for karaf-maven-plugin -->
+ <dependency>
+ <groupId>org.apache.karaf.features</groupId>
+ <artifactId>framework</artifactId>
+ <version>${karaf.version}</version>
+ <type>kar</type>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.camel.karaf</groupId>
+ <artifactId>apache-camel</artifactId>
+ <version>2.12.5</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.cxf.karaf</groupId>
+ <artifactId>apache-cxf</artifactId>
+ <version>2.7.14</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-osgi-features</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>keycloak-fuse-example-features</artifactId>
+ <version>${project.version}</version>
+ <classifier>features</classifier>
+ <type>xml</type>
+ <scope>runtime</scope>
+ </dependency>
+ <!-- for arquillian -->
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-container-karaf-managed</artifactId>
+ <version>2.1.0.CR18</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.aries.jmx</groupId>
+ <artifactId>org.apache.aries.jmx</artifactId>
+ <version>1.1.1</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.karaf.tooling</groupId>
+ <artifactId>karaf-maven-plugin</artifactId>
+ <version>${karaf.version}</version>
+ <extensions>true</extensions>
+ <executions>
+ <execution>
+ <id>prepare-karaf-with-examples</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <!-- creates custom karaf distro in ${project.build.directory}/assembly -->
+ <goal>install-kars</goal>
+ </goals>
+ </execution>
+ </executions>
+ <configuration>
+ <bootFeatures>
+ <!-- this installs all fuse examples -->
+ <feature>keycloak-fuse-example</feature>
+ </bootFeatures>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.karaf>true</app.server.karaf>
+ <karaf.home>${karaf.home}</karaf.home>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/karaf/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/karaf/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..fabd47b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/karaf/src/main/xslt/arquillian.xsl
@@ -0,0 +1,38 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-karaf" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.karaf}</property>
+ <property name="adapterImplClass">org.jboss.arquillian.container.osgi.karaf.managed.KarafManagedDeployableContainer</property>
+ <property name="autostartBundle">false</property>
+ <property name="karafHome">${karaf.home}</property>
+ <property name="javaVmArguments">-agentlib:jdwp=transport=dt_socket,address=5005,server=y,suspend=n ${adapter.test.props}</property>
+ <property name="jmxServiceURL">service:jmx:rmi://127.0.0.1:44444/jndi/rmi://127.0.0.1:1099/karaf-root</property>
+ <property name="jmxUsername">karaf</property>
+ <property name="jmxPassword">karaf</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/karaf/src/test/java/org/keycloak/testsuite/adapter/example/KarafFuseExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/karaf/src/test/java/org/keycloak/testsuite/adapter/example/KarafFuseExampleAdapterTest.java
new file mode 100644
index 0000000..eaf5f19
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/karaf/src/test/java/org/keycloak/testsuite/adapter/example/KarafFuseExampleAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-karaf")
+public class KarafFuseExampleAdapterTest extends AbstractFuseExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/pom.xml b/testsuite/integration-arquillian/tests/adapters/pom.xml
new file mode 100644
index 0000000..bcdf397
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/pom.xml
@@ -0,0 +1,348 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <packaging>pom</packaging>
+ <name>Adapter Tests</name>
+
+ <properties>
+ <app.server.port.offset>200</app.server.port.offset>
+ <app.server.http.port>8280</app.server.http.port>
+ <app.server.management.port>10190</app.server.management.port>
+ <app.server.management.port.jmx>10199</app.server.management.port.jmx>
+ <adapter.test.props>-Dapp.server.base.url=http://localhost:${app.server.http.port} -Dmy.host.name=localhost</adapter.test.props>
+ <exclude.adapters>-</exclude.adapters>
+ </properties>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-common-arquillian-xml</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-base</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ <includes>**/arquillian.xml</includes>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <version>1.0</version>
+ <executions>
+ <execution>
+ <id>add-app-server-to-arquillian-xml</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${project.build.directory}/dependency</dir>
+ <includes>
+ <include>arquillian.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/arquillian.xsl</stylesheet>
+ <outputDir>${project.build.directory}/dependency</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <arquillian.xml>${project.build.directory}/dependency/arquillian.xml</arquillian.xml>
+
+ <app.server.port.offset>${app.server.port.offset}</app.server.port.offset>
+ <app.server.http.port>${app.server.http.port}</app.server.http.port>
+ <app.server.management.port>${app.server.management.port}</app.server.management.port>
+ <app.server.management.port.jmx>${app.server.management.port.jmx}</app.server.management.port.jmx>
+
+ <adapter.test.props>${adapter.test.props}</adapter.test.props>
+
+ <adapter.libs.mode>bundled</adapter.libs.mode>
+ <adapter.config.mode>provided</adapter.config.mode>
+
+ </systemPropertyVariables>
+ <excludes>
+ <exclude>${exclude.adapters}</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>common-for-adapter-tests</id>
+ <activation>
+ <file>
+ <exists>src</exists>
+ </file>
+ </activation>
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-base</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-base</artifactId>
+ <version>${project.version}</version>
+ <type>test-jar</type>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <adapter.libs.mode>provided</adapter.libs.mode>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <id>examples</id>
+ <activation>
+ <property>
+ <name>!skipTests</name>
+ </property>
+ </activation>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>example-wars</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>product-portal-example</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>customer-portal-example</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>database-service</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>js-console</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>examples-multitenant</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>examples-basicauth</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>cors-angular-product-example</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak.example.demo</groupId>
+ <artifactId>cors-database-service</artifactId>
+ <version>${project.version}</version>
+ <type>war</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${examples.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ <execution>
+ <id>example-realms</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-examples-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <includes>**/*realm.json</includes>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${examples.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <examples.home>${examples.home}</examples.home>
+ <examples.version.suffix>${project.version}</examples.version.suffix>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ <plugin>
+ <artifactId>maven-resources-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>copy-resources</id>
+ <phase>validate</phase>
+ <goals>
+ <goal>copy-resources</goal>
+ </goals>
+ <configuration>
+ <outputDirectory>${examples.home}</outputDirectory>
+ <resources>
+ <resource>
+ <directory>${basedir}/src/test/resources</directory>
+ <filtering>true</filtering>
+ </resource>
+ </resources>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <id>auth-server-wildfly</id>
+ <modules>
+ <module>wildfly-relative</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>app-server-wildfly</id>
+ <modules>
+ <module>wildfly</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>app-server-wildfly8</id>
+ <modules>
+ <module>wildfly8</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>app-server-as7</id>
+ <modules>
+ <module>as7</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>app-server-tomcat</id>
+ <modules>
+ <module>tomcat</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>app-server-karaf</id>
+ <modules>
+ <module>karaf</module>
+ </modules>
+ </profile>
+ <profile>
+ <id>no-adapter-tests</id>
+ <properties>
+ <!-- Exclude all adapters tests. -->
+ <exclude.adapters>**/adapter/**/*Test.java</exclude.adapters>
+ </properties>
+ </profile>
+
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/pom.xml b/testsuite/integration-arquillian/tests/adapters/tomcat/pom.xml
new file mode 100644
index 0000000..3d29d9f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-tomcat</artifactId>
+ <name>Adapter Tests on Tomcat</name>
+
+ <properties>
+ <tomcat.version>8.0.23</tomcat.version>
+ <tomcat.home>${containers.home}/apache-tomcat-${tomcat.version}</tomcat.home>
+ <!-- FIXME disabled port-offset because tomcat doesn't support it -->
+ <app.server.port.offset>0</app.server.port.offset>
+ <app.server.http.port>8080</app.server.http.port>
+ <app.server.management.port>9990</app.server.management.port>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>arquillian-tomcat-managed-7</artifactId>
+ <version>1.0.0.CR7</version>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack-tomcat-and-adapter</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.apache.tomcat</groupId>
+ <artifactId>tomcat</artifactId>
+ <version>${tomcat.version}</version>
+ <type>zip</type>
+ <outputDirectory>${containers.home}</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-tomcat8-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${tomcat.home}/lib</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ <execution>
+ <id>libs-for-tomcat</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>copy</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-client</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>jaxrs-api</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ </artifactItem>
+ <artifactItem>
+ <groupId>commons-io</groupId>
+ <artifactId>commons-io</artifactId>
+ <version>1.4</version>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${tomcat.home}/lib</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>add-tomcat-manager-user</id>
+ <phase>process-test-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${tomcat.home}/conf</dir>
+ <stylesheet>src/main/xslt/tomcat-users.xsl</stylesheet>
+ <includes>
+ <include>tomcat-users.xml</include>
+ </includes>
+ <outputDir>${tomcat.home}/conf</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.tomcat>true</app.server.tomcat>
+ <tomcat.home>${tomcat.home}</tomcat.home>
+ <!-- TODO: implement port-offset for tomcat server.xml so we can shift from 8080 -->
+ <app.server.management.port.tomcat>8089</app.server.management.port.tomcat>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..cd61687
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/arquillian.xsl
@@ -0,0 +1,39 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-tomcat" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.tomcat}</property>
+ <property name="adapterImplClass">org.jboss.arquillian.container.tomcat.managed_7.TomcatManagedContainer</property>
+ <property name="catalinaHome">${tomcat.home}</property>
+ <property name="catalinaBase">${tomcat.home}</property>
+ <property name="bindHttpPort">${app.server.http.port}</property>
+ <property name="jmxPort">${app.server.management.port.tomcat}</property>
+ <property name="user">manager</property>
+ <property name="pass">arquillian</property>
+ <property name="javaVmArguments">${adapter.test.props}</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/tomcat-users.xsl b/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/tomcat-users.xsl
new file mode 100644
index 0000000..59b61b3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/main/xslt/tomcat-users.xsl
@@ -0,0 +1,23 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:tu="http://tomcat.apache.org/xml"
+ version="2.0"
+ exclude-result-prefixes="xalan tu">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no" />
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//tu:tomcat-users">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <user username="manager" password="arquillian" roles="manager-script"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatBasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatBasicAuthExampleAdapterTest.java
new file mode 100644
index 0000000..95cfac0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatBasicAuthExampleAdapterTest.java
@@ -0,0 +1,15 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.junit.Ignore;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-tomcat")
+@Ignore
+public class TomcatBasicAuthExampleAdapterTest extends AbstractBasicAuthExampleAdapterTest {
+
+ // TODO find out how to add context.xml dependent on app context (web.xml/module-name)
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatDemoExampleAdapterTest.java
new file mode 100644
index 0000000..99b219d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/example/TomcatDemoExampleAdapterTest.java
@@ -0,0 +1,16 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.junit.Ignore;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-tomcat")
+@Ignore
+public class TomcatDemoExampleAdapterTest extends AbstractDemoExampleAdapterTest {
+
+ // TODO find out how to add context.xml dependent on app context (web.xml/module-name)
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatDemoServletsAdapterTest.java
new file mode 100644
index 0000000..822a4db
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatDemoServletsAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-tomcat")
+public class TomcatDemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatSessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatSessionServletAdapterTest.java
new file mode 100644
index 0000000..7dae041
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/tomcat/src/test/java/org/keycloak/testsuite/adapter/servlet/TomcatSessionServletAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-tomcat")
+public class TomcatSessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/pom.xml b/testsuite/integration-arquillian/tests/adapters/wildfly/pom.xml
new file mode 100644
index 0000000..776ae8a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/pom.xml
@@ -0,0 +1,134 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-wildfly</artifactId>
+ <name>Adapter Tests on Wildfly</name>
+
+ <properties>
+ <app.server.wildfly.home>${containers.home}/wildfly-${wildfly.version}</app.server.wildfly.home>
+ <adapter.libs.wildfly>${containers.home}/keycloak-wf9-adapter-dist</adapter.libs.wildfly>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-wildfly-and-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-dist</artifactId>
+ <version>${wildfly.version}</version>
+ <type>zip</type>
+ <outputDirectory>${containers.home}</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${adapter.libs.wildfly}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.wildfly>true</app.server.wildfly>
+ <app.server.wildfly.home>${app.server.wildfly.home}</app.server.wildfly.home>
+ <adapter.libs.wildfly>${adapter.libs.wildfly}</adapter.libs.wildfly>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <properties>
+ <adapter.libs.wildfly>${app.server.wildfly.home}</adapter.libs.wildfly>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${app.server.wildfly.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${app.server.wildfly.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..32017cb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/arquillian.xsl
@@ -0,0 +1,35 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-wildfly" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.wildfly}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${app.server.wildfly.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${app.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementPort">${app.server.management.port}</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..a483717
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyBasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyBasicAuthExampleAdapterTest.java
new file mode 100644
index 0000000..e16c864
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/example/WildflyBasicAuthExampleAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyBasicAuthExampleAdapterTest extends AbstractBasicAuthExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyDemoServletsAdapterTest.java
new file mode 100644
index 0000000..eaa24fc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyDemoServletsAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyDemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflySessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflySessionServletAdapterTest.java
new file mode 100644
index 0000000..0b2b489
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflySessionServletAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflySessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/pom.xml b/testsuite/integration-arquillian/tests/adapters/wildfly8/pom.xml
new file mode 100644
index 0000000..7c0a0dd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/pom.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-wildfly8</artifactId>
+ <name>Adapter Tests on Wildfly 8</name>
+
+ <properties>
+ <wildfly.version>8.2.1.Final</wildfly.version>
+
+ <app.server.wildfly.home>${containers.home}/wildfly-${wildfly.version}</app.server.wildfly.home>
+ <adapter.libs.wildfly>${containers.home}/keycloak-wf8-adapter-dist</adapter.libs.wildfly>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf8-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-deploy-plugin</artifactId>
+ <configuration>
+ <skip>true</skip>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-wildfly-and-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-dist</artifactId>
+ <version>${wildfly.version}</version>
+ <type>zip</type>
+ <outputDirectory>${containers.home}</outputDirectory>
+ </artifactItem>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf8-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${adapter.libs.wildfly}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <version>2.18.1</version>
+ <configuration>
+ <systemPropertyVariables>
+ <app.server.wildfly>true</app.server.wildfly>
+ <app.server.wildfly.home>${app.server.wildfly.home}</app.server.wildfly.home>
+ <adapter.libs.wildfly>${adapter.libs.wildfly}</adapter.libs.wildfly>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-provided</id>
+ <activation>
+ <property>
+ <name>!adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <properties>
+ <adapter.libs.wildfly>${app.server.wildfly.home}</adapter.libs.wildfly>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-subsystem</id>
+ <phase>process-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${app.server.wildfly.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>src/main/xslt/standalone.xsl</stylesheet>
+ <outputDir>${app.server.wildfly.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..32017cb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/arquillian.xsl
@@ -0,0 +1,35 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="/a:arquillian">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+
+ <container qualifier="app-server-wildfly" mode="manual" >
+ <configuration>
+ <property name="enabled">${app.server.wildfly}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${app.server.wildfly.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${app.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementPort">${app.server.management.port}</property>
+ </configuration>
+ </container>
+
+ </xsl:copy>
+ </xsl:template>
+
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..364d803
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:2.2"
+ xmlns:ds="urn:jboss:domain:datasources:2.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8BasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8BasicAuthExampleAdapterTest.java
new file mode 100644
index 0000000..e746dd0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/example/Wildfly8BasicAuthExampleAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class Wildfly8BasicAuthExampleAdapterTest extends AbstractBasicAuthExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8DemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8DemoServletsAdapterTest.java
new file mode 100644
index 0000000..469c6ca
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8DemoServletsAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class Wildfly8DemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8SessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8SessionServletAdapterTest.java
new file mode 100644
index 0000000..a342403
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly8/src/test/java/org/keycloak/testsuite/adapter/servlet/Wildfly8SessionServletAdapterTest.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer("app-server-wildfly")
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class Wildfly8SessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/pom.xml b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/pom.xml
new file mode 100644
index 0000000..7d3ef2d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/pom.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests-adapters</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-adapters-wildfly-relative</artifactId>
+ <name>Adapter Tests on Wildfly Relative</name>
+
+ <properties>
+
+ <adapter.libs.wildfly>${containers.home}/keycloak-wf9-adapter-dist</adapter.libs.wildfly>
+
+ <!--this is needed for adapter tests that load system properties in adapter config-->
+ <app.server.http.port>${auth.server.http.port}</app.server.http.port>
+
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <type>zip</type>
+ </dependency>
+ </dependencies>
+
+ <profiles>
+ <profile>
+ <id>adapter-libs-bundled</id>
+ <activation>
+ <property>
+ <name>adapter.libs.bundled</name>
+ </property>
+ </activation>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>unpack-adapter</id>
+ <phase>generate-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wf9-adapter-dist</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ <outputDirectory>${adapter.libs.wildfly}</outputDirectory>
+ </artifactItem>
+ </artifactItems>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <adapter.libs.wildfly>${adapter.libs.wildfly}</adapter.libs.wildfly>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+ </profiles>
+</project>
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/arquillian.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/arquillian.xsl
new file mode 100644
index 0000000..ce09796
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/arquillian.xsl
@@ -0,0 +1,16 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:a="http://jboss.org/schema/arquillian"
+ version="2.0"
+ exclude-result-prefixes="xalan a">
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/standalone.xsl b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/standalone.xsl
new file mode 100644
index 0000000..a483717
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/main/xslt/standalone.xsl
@@ -0,0 +1,51 @@
+<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
+ xmlns:xalan="http://xml.apache.org/xalan"
+ xmlns:j="urn:jboss:domain:3.0"
+ xmlns:ds="urn:jboss:domain:datasources:3.0"
+ xmlns:k="urn:jboss:domain:keycloak:1.1"
+ xmlns:sec="urn:jboss:domain:security:1.2"
+ version="2.0"
+ exclude-result-prefixes="xalan j ds k sec">
+
+ <xsl:param name="config"/>
+
+ <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes" xalan:indent-amount="4" standalone="no"/>
+ <xsl:strip-space elements="*"/>
+
+ <xsl:template match="//j:extensions">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//j:profile">
+ <xsl:copy>
+ <xsl:apply-templates select="node()|@*"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//sec:security-domains">
+ <xsl:copy>
+ <xsl:apply-templates select="node()[name(.)='security-domain']"/>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ <security-domain name="sp" cache-type="default">
+ <authentication>
+ <login-module code="org.picketlink.identity.federation.bindings.wildfly.SAML2LoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()" />
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeBasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeBasicAuthExampleAdapterTest.java
new file mode 100644
index 0000000..5f45ebe
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeBasicAuthExampleAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeBasicAuthExampleAdapterTest extends AbstractBasicAuthExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeCorsExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeCorsExampleAdapterTest.java
new file mode 100644
index 0000000..42ad346
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeCorsExampleAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author fkiss
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeCorsExampleAdapterTest extends AbstractCorsExampleAdapterTest {
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeDemoExampleAdapterTest.java
new file mode 100644
index 0000000..afae885
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeDemoExampleAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeDemoExampleAdapterTest extends AbstractDemoExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..c0026d0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/example/WildflyRelativeJSConsoleExampleAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeJSConsoleExampleAdapterTest extends AbstractJSConsoleExampleAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeDemoServletsAdapterTest.java
new file mode 100644
index 0000000..b23cfc5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeDemoServletsAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeDemoServletsAdapterTest extends AbstractDemoServletsAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeSessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeSessionServletAdapterTest.java
new file mode 100644
index 0000000..17a3ae5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/java/org/keycloak/testsuite/adapter/servlet/WildflyRelativeSessionServletAdapterTest.java
@@ -0,0 +1,12 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.arquillian.annotation.AdapterLibsLocationProperty;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AdapterLibsLocationProperty("adapter.libs.wildfly")
+public class WildflyRelativeSessionServletAdapterTest extends AbstractSessionServletAdapterTest {
+
+}
diff --git a/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/resources/web.xml b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/resources/web.xml
new file mode 100644
index 0000000..1b6dc5e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/adapters/wildfly-relative/src/test/resources/web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>%CONTEXT_PATH%</module-name>
+
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/pom.xml b/testsuite/integration-arquillian/tests/base/pom.xml
new file mode 100644
index 0000000..4ee4118
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/pom.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-tests</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-tests-base</artifactId>
+ <name>Base Test Suite</name>
+
+ <properties>
+ <exclude.console>-</exclude.console>
+ <exclude.account>-</exclude.account>
+ </properties>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-jar-plugin</artifactId>
+ <version>2.2</version>
+ <executions>
+ <execution>
+ <goals>
+ <goal>test-jar</goal>
+ </goals>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <excludes>
+ <exclude>${exclude.console}</exclude>
+ <exclude>${exclude.account}</exclude>
+ </excludes>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+ <profiles>
+ <profile>
+ <id>no-console</id>
+ <properties>
+ <!-- Exclude all admin console tests. -->
+ <exclude.console>**/console/**/*Test.java</exclude.console>
+ </properties>
+ </profile>
+ <profile>
+ <id>no-account</id>
+ <properties>
+ <!-- Exclude all account management tests. -->
+ <exclude.account>**/account/**/*Test.java</exclude.account>
+ </properties>
+ </profile>
+ <profile>
+ <id>adapters-only</id>
+ <properties>
+ <exclude.console>**/console/**/*Test.java</exclude.console>
+ <exclude.account>**/account/**/*Test.java</exclude.account>
+ </properties>
+ </profile>
+ </profiles>
+
+</project>
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/AdapterLibsMode.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/AdapterLibsMode.java
new file mode 100644
index 0000000..148a8d2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/AdapterLibsMode.java
@@ -0,0 +1,30 @@
+package org.keycloak.testsuite.adapter;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public enum AdapterLibsMode {
+
+ PROVIDED("provided"),
+ BUNDLED("bundled");
+
+ private final String type;
+
+ private AdapterLibsMode(String type) {
+ this.type = type;
+ }
+
+ public String getType() {
+ return type;
+ }
+
+ public static AdapterLibsMode getByType(String type) {
+ for (AdapterLibsMode s : AdapterLibsMode.values()) {
+ if (s.getType().equals(type)) {
+ return s;
+ }
+ }
+ return null;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AngularCorsProductExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AngularCorsProductExample.java
new file mode 100644
index 0000000..3520a3e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AngularCorsProductExample.java
@@ -0,0 +1,77 @@
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.openqa.selenium.WebElement;
+
+import java.net.URL;
+
+/**
+ * Created by fkiss.
+ */
+public class AngularCorsProductExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "cors-angular-product-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @FindByJQuery("button:contains('Reload')")
+ private WebElement reloadDataButton;
+
+ @FindByJQuery("button:contains('load Roles')")
+ private WebElement loadRolesButton;
+
+ @FindByJQuery("button:contains('Add Role')")
+ private WebElement addRoleButton;
+
+ @FindByJQuery("button:contains('Delete Role')")
+ private WebElement deleteRoleButton;
+
+ @FindByJQuery("button:contains('load available social providers')")
+ private WebElement loadAvailableSocialProvidersButton;
+
+ @FindByJQuery("button:contains('Load public realm info')")
+ private WebElement loadPublicRealmInfoButton;
+
+ @FindByJQuery("button:contains('Load version')")
+ private WebElement loadVersionButton;
+
+ public void reloadData() {
+ reloadDataButton.click();
+ }
+
+ public void loadRoles() {
+ loadRolesButton.click();
+ }
+
+ public void addRole() {
+ addRoleButton.click();
+ }
+
+ public void deleteRole() {
+ deleteRoleButton.click();
+ }
+
+ public void loadAvailableSocialProviders() {
+ loadAvailableSocialProvidersButton.click();
+ }
+
+ public void loadPublicRealmInfo() {
+ loadPublicRealmInfoButton.click();
+ }
+
+ public void loadVersion() {
+ loadVersionButton.click();
+ }
+
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AppServerContextRoot.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AppServerContextRoot.java
new file mode 100644
index 0000000..0b68e05
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/AppServerContextRoot.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.adapter.page;
+
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import java.net.URL;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContext;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AppServerContextRoot extends AbstractPageWithInjectedUrl {
+
+ @ArquillianResource
+ @AppServerContext
+ private URL appServerContextRoot;
+
+ @Override
+ public URL getInjectedUrl() {
+ return appServerContextRoot;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuthExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuthExample.java
new file mode 100644
index 0000000..f5cbcfb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/BasicAuthExample.java
@@ -0,0 +1,41 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class BasicAuthExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "basic-auth-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .userInfo("{user}:{password}")
+ .path("service/echo")
+ .queryParam("value", "{value}");
+ }
+
+ public BasicAuthExample setTemplateValues(String user, String password, String value) {
+ setUriParameter("user", user);
+ setUriParameter("password", password);
+ setUriParameter("value", value);
+ return this;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CorsDatabaseServiceExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CorsDatabaseServiceExample.java
new file mode 100644
index 0000000..4b72d6a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CorsDatabaseServiceExample.java
@@ -0,0 +1,26 @@
+package org.keycloak.testsuite.adapter.page;
+
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+import java.net.URL;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CorsDatabaseServiceExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "cors-database-service";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDb.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDb.java
new file mode 100644
index 0000000..0e9c387
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDb.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerDb extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "customer-db";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDbErrorPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDbErrorPage.java
new file mode 100644
index 0000000..82d72e1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerDbErrorPage.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerDbErrorPage extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "customer-db-error-page";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortal.java
new file mode 100644
index 0000000..b3dcdf0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortal.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerPortal extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "customer-portal";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalExample.java
new file mode 100644
index 0000000..beaa8fa
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/CustomerPortalExample.java
@@ -0,0 +1,80 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerPortalExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "customer-portal-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @FindByJQuery("h1:contains('Customer Portal')")
+ private WebElement title;
+
+ @FindByJQuery("a:contains('Customer Listing')")
+ private WebElement customerListingLink;
+ @FindByJQuery("h1:contains('Customer Listing')")
+ private WebElement customerListingHeader;
+
+ @FindByJQuery("h1:contains('Customer Session')")
+ private WebElement customerSessionHeader;
+
+ @FindByJQuery("a:contains('Customer Admin Interface')")
+ private WebElement customerAdminInterfaceLink;
+
+ @FindByJQuery("a:contains('Customer Session')")
+ private WebElement customerSessionLink;
+
+ @FindByJQuery("a:contains('products')")
+ private WebElement productsLink;
+
+ @FindByJQuery("a:contains('logout')")
+ private WebElement logOutButton;
+
+ public void goToProducts() {
+ productsLink.click();
+ }
+
+ public void customerListing() {
+ customerListingLink.click();
+ }
+
+ public void customerAdminInterface() {
+ customerAdminInterfaceLink.click();
+ }
+
+ public void customerSession() {
+ WaitUtils.waitGuiForElement(customerSessionLink);
+ customerSessionLink.click();
+ }
+
+ public void logOut() {
+ logOutButton.click();
+ }
+
+ public void waitForCustomerListingHeader() {
+ WaitUtils.waitGuiForElementNotPresent(customerListingHeader);
+ }
+
+ public void waitForCustomerSessionHeader() {
+ WaitUtils.waitGuiForElementNotPresent(customerSessionHeader);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/DatabaseServiceExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/DatabaseServiceExample.java
new file mode 100644
index 0000000..1bbc4c7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/DatabaseServiceExample.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class DatabaseServiceExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "database-service-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AbstractFuseExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AbstractFuseExample.java
new file mode 100644
index 0000000..18e821a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AbstractFuseExample.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.adapter.page.fuse;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractFuseExample extends AppServerContextRoot {
+
+ public abstract String getContext();
+
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ if (url == null) {
+ try {
+ url = new URL(super.getInjectedUrl().toExternalForm() + "/" + getContext());
+ } catch (MalformedURLException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AdminInterface.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AdminInterface.java
new file mode 100644
index 0000000..2921a5b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/AdminInterface.java
@@ -0,0 +1,17 @@
+package org.keycloak.testsuite.adapter.page.fuse;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminInterface extends CustomerPortalFuseExample {
+
+ @Override
+ public String getContext() {
+ return super.getContext() + "/customers/camel.jsp";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerListing.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerListing.java
new file mode 100644
index 0000000..01f3f3d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerListing.java
@@ -0,0 +1,36 @@
+package org.keycloak.testsuite.adapter.page.fuse;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerListing extends CustomerPortalFuseExample {
+
+ @Override
+ public String getContext() {
+ return super.getContext() + "/customers/cxf-rs.jsp";
+ }
+
+ @FindBy(linkText = "products")
+ protected WebElement productsLink;
+ @FindBy(linkText = "logout")
+ protected WebElement logOutLink;
+ @FindBy(linkText = "manage acct")
+ protected WebElement accountManagementLink;
+
+ public void clickProducts() {
+ productsLink.click();
+ }
+
+ public void clickLogOut() {
+ logOutLink.click();
+ }
+
+ public void clickAccountManagement() {
+ accountManagementLink.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerPortalFuseExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerPortalFuseExample.java
new file mode 100644
index 0000000..6d1f656
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/CustomerPortalFuseExample.java
@@ -0,0 +1,34 @@
+package org.keycloak.testsuite.adapter.page.fuse;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CustomerPortalFuseExample extends AbstractFuseExample {
+
+ public static final String DEPLOYMENT_NAME = "customer-portal-fuse-example";
+ public static final String DEPLOYMENT_CONTEXT = "customer-portal";
+
+ @Override
+ public String getContext() {
+ return DEPLOYMENT_CONTEXT;
+ }
+
+ @FindBy(linkText = "Customer Listing - CXF RS endpoint")
+ protected WebElement customerListingLink;
+
+ @FindBy(linkText = "Admin Interface - Apache Camel endpoint")
+ protected WebElement adminInterfaceLink;
+
+ public void clickCustomerListingLink() {
+ customerListingLink.click();
+ }
+
+ public void clickAdminInterfaceLink() {
+ adminInterfaceLink.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/ProductPortalFuseExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/ProductPortalFuseExample.java
new file mode 100644
index 0000000..b61222e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/fuse/ProductPortalFuseExample.java
@@ -0,0 +1,50 @@
+package org.keycloak.testsuite.adapter.page.fuse;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ProductPortalFuseExample extends AbstractFuseExample {
+
+ public static final String DEPLOYMENT_NAME = "product-portal-fuse-example";
+ public static final String DEPLOYMENT_CONTEXT = "product-portal";
+
+ @Override
+ public String getContext() {
+ return DEPLOYMENT_CONTEXT;
+ }
+
+ @FindBy(linkText = "products")
+ protected WebElement productsLink;
+ @FindBy(linkText = "logout")
+ protected WebElement logOutLink;
+ @FindBy(linkText = "manage acct")
+ protected WebElement accountManagementLink;
+
+ @FindBy(xpath = "//p[contains(text(),'Product with ID 1 - unsecured request')]")
+ protected WebElement product1Unsecured;
+ @FindBy(xpath = "//p[contains(text(),'Product with ID 1 - secured request')]")
+ protected WebElement product1Secured;
+ @FindBy(xpath = "//p[contains(text(),'Product with ID 2 - secured request')]")
+ protected WebElement product2Secured;
+
+ public String getProduct1UnsecuredText() {
+ return product1Unsecured.getText();
+ }
+
+ public String getProduct1SecuredText() {
+ return product1Secured.getText();
+ }
+
+ public String getProduct2SecuredText() {
+ return product2Secured.getText();
+ }
+
+ public void clickLogOutLink() {
+ logOutLink.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
new file mode 100644
index 0000000..564272f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/InputPortal.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class InputPortal extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "input-portal";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @FindBy(id = "parameter")
+ private WebElement parameter;
+
+ @FindBy(name = "submit")
+ private WebElement submit;
+
+ public void execute(String param) {
+ parameter.clear();
+ parameter.sendKeys(param);
+ submit.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
new file mode 100644
index 0000000..787be4d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/JSConsoleExample.java
@@ -0,0 +1,70 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class JSConsoleExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "js-console-example";
+ public static final String CLIENT_ID = "js-console";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @FindBy(xpath = "//button[text() = 'Login']")
+ private WebElement logInButton;
+ @FindBy(xpath = "//button[text() = 'Logout']")
+ private WebElement logOutButton;
+ @FindBy(xpath = "//button[text() = 'Refresh Token']")
+ private WebElement refreshTokenButton;
+ @FindBy(xpath = "//button[contains(text(),'Refresh Token (if <30s')]")
+ private WebElement refreshTokenIfUnder30sButton;
+ @FindBy(xpath = "//button[text() = 'Get Profile']")
+ private WebElement getProfileButton;
+
+ @FindBy(xpath = "//button[text() = 'Show Token']")
+ private WebElement showTokenButton;
+ @FindBy(xpath = "//button[text() = 'Show Refresh Token']")
+ private WebElement showRefreshTokenButton;
+ @FindBy(xpath = "//button[text() = 'Show ID Token']")
+ private WebElement showIdTokenButton;
+ @FindBy(xpath = "//button[text() = 'Show Expires']")
+ private WebElement showExpiresButton;
+ @FindBy(xpath = "//button[text() = 'Show Details']")
+ private WebElement showDetailsButton;
+
+ public void logIn() {
+ logInButton.click();
+ }
+
+ public void logOut() {
+ logOutButton.click();
+ }
+
+ public void refreshToken() {
+ refreshTokenButton.click();
+ }
+
+ public void refreshTokenIfUnder30s() {
+ refreshTokenIfUnder30sButton.click();
+ }
+
+ public void getProfile() {
+ getProfileButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant.java
new file mode 100644
index 0000000..3cb7fb6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenant.java
@@ -0,0 +1,46 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class MultiTenant extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "multi-tenant";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("/").queryParam("realm", "{tenantRealm}");
+ }
+
+ public URL getTenantRealmUrl(String realm) {
+ try {
+ return getUriBuilder().build(realm).toURL();
+ } catch (MalformedURLException ex) {
+ throw new IllegalStateException("Page URL is malformed.");
+ }
+ }
+
+ public void navigateToRealm(String realm) {
+ URL u = getTenantRealmUrl(realm);
+ log.info("navigate to "+u.toExternalForm());
+ driver.navigate().to(u.toExternalForm());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenantExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenantExample.java
new file mode 100644
index 0000000..a8e582d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/MultiTenantExample.java
@@ -0,0 +1,46 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class MultiTenantExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "multi-tenant-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("{tenantRealm}");
+ }
+
+ public URL getTenantRealmUrl(String realm) {
+ try {
+ return getUriBuilder().build(realm).toURL();
+ } catch (MalformedURLException ex) {
+ throw new IllegalStateException("Page URL is malformed.");
+ }
+ }
+
+ public void navigateToRealm(String realm) {
+ URL u = getTenantRealmUrl(realm);
+ log.info("navigate to "+u.toExternalForm());
+ driver.navigate().to(u.toExternalForm());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortal.java
new file mode 100644
index 0000000..96cd1b1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortal.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ProductPortal extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "product-portal";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortalExample.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortalExample.java
new file mode 100644
index 0000000..fd80e5d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/ProductPortalExample.java
@@ -0,0 +1,59 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+import org.keycloak.testsuite.util.WaitUtils;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ProductPortalExample extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "product-portal-example";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+ @FindByJQuery("h1:contains('Product Portal')")
+ private WebElement title;
+
+ @FindByJQuery("a:contains('Product Listing')")
+ private WebElement productListingLink;
+ @FindByJQuery("h1:contains('Product Listing')")
+ private WebElement productListingHeader;
+
+ @FindByJQuery("a:contains('customers')")
+ private WebElement customersLink;
+
+ @FindByJQuery("a:contains('logout')")
+ private WebElement logOutButton;
+
+ public void productListing() {
+ productListingLink.click();
+ }
+
+ public void goToCustomers() {
+ customersLink.click();
+ }
+
+ public void waitForProductListingHeader() {
+ WaitUtils.waitGuiForElementNotPresent(productListingHeader);
+ }
+
+ public void logOut() {
+ logOutButton.click();
+ }
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortal.java
new file mode 100644
index 0000000..4825fef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SecurePortal.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class SecurePortal extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "secure-portal";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SessionPortal.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SessionPortal.java
new file mode 100644
index 0000000..0e597c6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/page/SessionPortal.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.adapter.page;
+
+import java.net.URL;
+import org.jboss.arquillian.container.test.api.OperateOnDeployment;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class SessionPortal extends AbstractPageWithInjectedUrl {
+
+ public static final String DEPLOYMENT_NAME = "session-portal";
+
+ @ArquillianResource
+ @OperateOnDeployment(DEPLOYMENT_NAME)
+ private URL url;
+
+ @Override
+ public URL getInjectedUrl() {
+ return url;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CallAuthenticatedServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CallAuthenticatedServlet.java
new file mode 100644
index 0000000..b5f7f7c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CallAuthenticatedServlet.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CallAuthenticatedServlet extends HttpServlet {
+
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ if (!req.authenticate(resp)) {
+ return;
+ }
+
+ KeycloakSecurityContext sc = (KeycloakSecurityContext) req.getAttribute(KeycloakSecurityContext.class.getName());
+ if (sc == null) { // assert sc not null
+ throw new AssertionError("Keycloak security context is null.");
+ }
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Customer Portal");
+ pw.println("Stian Thorgersen");
+ pw.println("Bill Burke");
+ pw.print("</body></html>");
+ pw.flush();
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java
new file mode 100644
index 0000000..0e581cd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerDatabaseServlet.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class CustomerDatabaseServlet extends HttpServlet {
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Customer Portal");
+ pw.println("Stian Thorgersen");
+ pw.println("Bill Burke");
+ pw.print("</body></html>");
+ pw.flush();
+
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java
new file mode 100644
index 0000000..9d03c1f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/CustomerServlet.java
@@ -0,0 +1,59 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.annotation.WebServlet;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+@WebServlet("/customer-portal")
+public class CustomerServlet extends HttpServlet {
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ PrintWriter pw = resp.getWriter();
+ if (req.getRequestURI().endsWith("logout")) {
+ resp.setStatus(200);
+ pw.println("servlet logout ok");
+
+ // Call logout before pw.flush
+ req.logout();
+ pw.flush();
+ return;
+ }
+ KeycloakSecurityContext context = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
+ Client client = ClientBuilder.newClient();
+
+ try {
+ String appBase = System.getProperty("app.server.base.url", "http://localhost:8280");
+ WebTarget target = client.target(appBase + "/customer-db/");
+ Response response = target.request().get();
+ if (response.getStatus() != 401) { // assert response status == 401
+ throw new AssertionError("Response status code is not 401.");
+ }
+ response.close();
+ String html = target.request()
+ .header(HttpHeaders.AUTHORIZATION, "Bearer " + context.getTokenString())
+ .get(String.class);
+ resp.setContentType("text/html");
+ pw.println(html);
+ pw.flush();
+ } finally {
+ client.close();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ErrorServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ErrorServlet.java
new file mode 100644
index 0000000..55cac40
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ErrorServlet.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class ErrorServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+
+
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Error Page");
+ pw.print("<h1>There was an error</h1></body></html>");
+ pw.flush();
+
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
new file mode 100644
index 0000000..0834d95
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/InputServlet.java
@@ -0,0 +1,43 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import javax.servlet.annotation.WebServlet;
+
+/**
+ * @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
+ */
+@WebServlet("/input-portal")
+public class InputServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String appBase = System.getProperty("app.server.base.url", "http://localhost:8280");
+ String actionUrl = appBase + "/input-portal/secured/post";
+
+
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Input Page");
+ pw.printf("<form action=\"%s\" method=\"POST\">", actionUrl);
+ pw.println("<input id=\"parameter\" type=\"text\" name=\"parameter\">");
+ pw.println("<input name=\"submit\" type=\"submit\" value=\"Submit\"></form>");
+ pw.print("</body></html>");
+ pw.flush();
+
+
+ }
+
+ @Override
+ protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/plain");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("parameter="+req.getParameter("parameter"));
+ pw.flush();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantResolver.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantResolver.java
new file mode 100644
index 0000000..765e291
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantResolver.java
@@ -0,0 +1,46 @@
+/*
+ * 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.testsuite.adapter.servlet;
+
+import java.io.InputStream;
+import org.keycloak.adapters.HttpFacade;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+
+/**
+ *
+ * @author Juraci Paixão Kröhling <juraci at kroehling.de>
+ */
+public class MultiTenantResolver implements KeycloakConfigResolver {
+
+ @Override
+ public KeycloakDeployment resolve(HttpFacade.Request request) {
+ String realm = request.getQueryParamValue("realm");
+
+ // FIXME doesn't work - need to load resources from WEB-INF
+ InputStream is = getClass().getResourceAsStream("/" + realm + "-keycloak.json");
+
+ if (is == null) {
+ throw new IllegalStateException("Not able to find the file /" + realm + "-keycloak.json");
+ }
+
+ KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
+ return deployment;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantServlet.java
new file mode 100644
index 0000000..ccbf97a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/MultiTenantServlet.java
@@ -0,0 +1,48 @@
+/*
+ * 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.testsuite.adapter.servlet;
+
+import org.keycloak.KeycloakSecurityContext;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ *
+ * @author Juraci Paixão Kröhling <juraci at kroehling.de>
+ */
+public class MultiTenantServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ KeycloakSecurityContext context = (KeycloakSecurityContext)req.getAttribute(KeycloakSecurityContext.class.getName());
+
+ pw.print("Username: ");
+ pw.println(context.getIdToken().getPreferredUsername());
+
+ pw.print("<br/>Realm: ");
+ pw.println(context.getRealm());
+
+ pw.flush();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ProductServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ProductServlet.java
new file mode 100644
index 0000000..ce89986
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/ProductServlet.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ProductServlet extends HttpServlet {
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Product Portal");
+ pw.println("iPhone");
+ pw.println("iPad");
+ pw.print("</body></html>");
+ pw.flush();
+
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SessionServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SessionServlet.java
new file mode 100644
index 0000000..584408e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/adapter/servlet/SessionServlet.java
@@ -0,0 +1,40 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@WebServlet("/SessionServlet")
+public class SessionServlet extends HttpServlet {
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String counter = increaseAndGetCounter(req);
+
+ resp.setContentType("text/html");
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", "Session Test");
+ pw.printf("Counter=%s", counter);
+ pw.print("</body></html>");
+ pw.flush();
+
+
+ }
+
+ private String increaseAndGetCounter(HttpServletRequest req) {
+ HttpSession session = req.getSession();
+ Integer counter = (Integer)session.getAttribute("counter");
+ counter = (counter == null) ? 1 : counter + 1;
+ session.setAttribute("counter", counter);
+ return String.valueOf(counter);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
new file mode 100644
index 0000000..eb202e5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/ApiUtil.java
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.admin;
+
+import org.keycloak.admin.client.resource.RealmResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.admin.client.resource.RoleScopeResource;
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ * Created by st on 28.05.15.
+ */
+public class ApiUtil {
+
+ public static String getCreatedId(Response response) {
+ URI location = response.getLocation();
+ if (location == null) {
+ return null;
+ }
+ String path = location.getPath();
+ return path.substring(path.lastIndexOf('/') + 1);
+ }
+
+ public static ClientResource findClientResourceByClientId(RealmResource realm, String clientId) {
+ for (ClientRepresentation c : realm.clients().findAll()) {
+ if (c.getClientId().equals(clientId)) {
+ return realm.clients().get(c.getId());
+ }
+ }
+ return null;
+ }
+
+ public static ClientRepresentation findClientByClientId(RealmResource realm, String clientId) {
+ ClientRepresentation client = null;
+ for (ClientRepresentation c : realm.clients().findAll()) {
+ if (clientId.equals(c.getClientId())) {
+ client = c;
+ }
+ }
+ return client;
+ }
+
+ public static UserRepresentation findUserByUsername(RealmResource realm, String username) {
+ UserRepresentation user = null;
+ List<UserRepresentation> ur = realm.users().search(username, null, null);
+ if (ur.size() == 1) {
+ user = ur.get(0);
+ }
+ return user;
+ }
+
+ public static String createUserWithAdminClient(RealmResource realm, UserRepresentation user) {
+ Response response = realm.users().create(user);
+ String createdId = getCreatedId(response);
+ response.close();
+ return createdId;
+ }
+
+ public static String createUserAndResetPasswordWithAdminClient(RealmResource realm, UserRepresentation user, String password) {
+ String id = createUserWithAdminClient(realm, user);
+ resetUserPassword(realm.users().get(id), password, false);
+ return id;
+ }
+
+ public static void resetUserPassword(UserResource userResource, String newPassword, boolean temporary) {
+ CredentialRepresentation newCredential = new CredentialRepresentation();
+ newCredential.setType(PASSWORD);
+ newCredential.setValue(newPassword);
+ newCredential.setTemporary(temporary);
+ userResource.resetPassword(newCredential);
+ }
+
+ public static void assignClientRoles(UserResource userResource, String clientId, String... roles) {
+ RoleScopeResource rsr = userResource.roles().clientLevel(clientId);
+ List<String> rolesList = Arrays.asList(roles);
+ List<RoleRepresentation> realmMgmtRoles = new ArrayList<>();
+ for (RoleRepresentation rr : rsr.listAvailable()) {
+ if (rolesList.contains(rr.getName())) {
+ realmMgmtRoles.add(rr);
+ }
+ }
+ rsr.add(realmMgmtRoles);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/Users.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/Users.java
new file mode 100644
index 0000000..c2a2f7a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/admin/Users.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.admin;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class Users {
+
+ public static String getPasswordOf(UserRepresentation user) {
+ String value = null;
+ CredentialRepresentation password = getPasswordCredentialOf(user);
+ if (password != null) {
+ value = password.getValue();
+ }
+ return value;
+ }
+
+ public static CredentialRepresentation getPasswordCredentialOf(UserRepresentation user) {
+ CredentialRepresentation password = null;
+ if (user.getCredentials() != null) {
+ for (CredentialRepresentation c : user.getCredentials()) {
+ if (CredentialRepresentation.PASSWORD.equals(c.getType())) {
+ password = c;
+ }
+ }
+ }
+ return password;
+ }
+
+ public static void setPasswordFor(UserRepresentation user, String password) {
+ setPasswordFor(user, password, false);
+ }
+ public static void setPasswordFor(UserRepresentation user, String password, boolean temporary) {
+ List<CredentialRepresentation> credentials = new ArrayList<>();
+ CredentialRepresentation pass = new CredentialRepresentation();
+ pass.setType(PASSWORD);
+ pass.setValue(password);
+ pass.setTemporary(temporary);
+ credentials.add(pass);
+ user.setCredentials(credentials);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AdapterLibsLocationProperty.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AdapterLibsLocationProperty.java
new file mode 100644
index 0000000..ec0fe1d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AdapterLibsLocationProperty.java
@@ -0,0 +1,19 @@
+package org.keycloak.testsuite.arquillian.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.TYPE})
+public @interface AdapterLibsLocationProperty
+{
+ String value();
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContainer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContainer.java
new file mode 100644
index 0000000..b8169f8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContainer.java
@@ -0,0 +1,20 @@
+package org.keycloak.testsuite.arquillian.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.TYPE})
+public @interface AppServerContainer
+{
+ String value() default "";
+ String adapterLibsLocationProperty() default "";
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContext.java
new file mode 100644
index 0000000..203ac65
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AppServerContext.java
@@ -0,0 +1,18 @@
+package org.keycloak.testsuite.arquillian.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.FIELD})
+public @interface AppServerContext
+{
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AuthServerContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AuthServerContext.java
new file mode 100644
index 0000000..a35bbde
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/annotation/AuthServerContext.java
@@ -0,0 +1,18 @@
+package org.keycloak.testsuite.arquillian.annotation;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import java.lang.annotation.Target;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.FIELD})
+public @interface AuthServerContext
+{
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/MultipleContainersExtension.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/MultipleContainersExtension.java
new file mode 100644
index 0000000..c4476ef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/MultipleContainersExtension.java
@@ -0,0 +1,61 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.arquillian.containers;
+
+import java.util.logging.Logger;
+
+import org.jboss.arquillian.container.impl.client.ContainerDeploymentContextHandler;
+import org.jboss.arquillian.container.impl.client.container.ContainerDeployController;
+import org.jboss.arquillian.container.impl.client.container.ContainerLifecycleController;
+import org.jboss.arquillian.container.impl.client.container.DeploymentExceptionHandler;
+import org.jboss.arquillian.container.impl.client.deployment.ArchiveDeploymentExporter;
+import org.jboss.arquillian.container.impl.context.ContainerContextImpl;
+import org.jboss.arquillian.container.impl.context.DeploymentContextImpl;
+import org.jboss.arquillian.core.spi.LoadableExtension;
+
+/**
+ * Enables multiple container adapters on classpath.
+ *
+ * @author Dominik Pospisil <dpospisi@redhat.com>
+ * @author Stefan Miklosovic <smikloso@redhat.com>
+ * @author Tomas Kyjovsky <tkyjovsk@redhat.com>
+ */
+public class MultipleContainersExtension implements LoadableExtension {
+
+ private static final Logger logger = Logger.getLogger(MultipleContainersExtension.class.getName());
+
+ @Override
+ public void register(ExtensionBuilder builder) {
+
+ logger.info("Multiple containers extension registering.");
+
+ builder.context(ContainerContextImpl.class).context(DeploymentContextImpl.class);
+
+ builder.observer(RegistryCreator.class)
+ .observer(ContainerDeploymentContextHandler.class)
+ .observer(ContainerLifecycleController.class)
+ .observer(ContainerDeployController.class)
+ .observer(ArchiveDeploymentExporter.class)
+ .observer(DeploymentExceptionHandler.class);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/Registry.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/Registry.java
new file mode 100644
index 0000000..2564b7a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/Registry.java
@@ -0,0 +1,148 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.arquillian.containers;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.arquillian.config.descriptor.api.ContainerDef;
+import org.jboss.arquillian.container.impl.ContainerCreationException;
+import org.jboss.arquillian.container.impl.ContainerImpl;
+import org.jboss.arquillian.container.spi.ConfigurationException;
+import org.jboss.arquillian.container.spi.Container;
+import org.jboss.arquillian.container.spi.ContainerRegistry;
+import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
+import org.jboss.arquillian.container.spi.client.deployment.TargetDescription;
+import org.jboss.arquillian.core.api.Injector;
+import org.jboss.arquillian.core.spi.ServiceLoader;
+import org.jboss.arquillian.core.spi.Validate;
+import static org.keycloak.testsuite.arquillian.containers.RegistryCreator.getAdapterImplClassValue;
+import static org.keycloak.testsuite.arquillian.containers.RegistryCreator.getContainerAdapter;
+
+/**
+ * This class registers all adapters which are specified in the arquillian.xml.
+ *
+ * In the case there is only one adapter implementation on the classpath, it is
+ * not necessary to specify it in the container configuration since it will be
+ * used automatically. You have to specify it only in the case you are going to
+ * use more than one container.
+ *
+ * @author Dominik Pospisil <dpospisi@redhat.com>
+ * @author Stefan Miklosovic <smikloso@redhat.com>
+ * @author Tomas Kyjovsky <tkyjovsk@redhat.com>
+ */
+public class Registry implements ContainerRegistry {
+
+ private final List<Container> containers;
+
+ private Injector injector;
+
+ private static final Logger logger = Logger.getLogger(RegistryCreator.class.getName());
+
+ public Registry(Injector injector) {
+ this.containers = new ArrayList<>();
+ this.injector = injector;
+ }
+
+ @Override
+ public Container create(ContainerDef definition, ServiceLoader loader) {
+ Validate.notNull(definition, "Definition must be specified");
+
+ try {
+ logger.log(Level.INFO, "Registering container: {0}", definition.getContainerName());
+
+ @SuppressWarnings("rawtypes")
+ Collection<DeployableContainer> containerAdapters = loader.all(DeployableContainer.class);
+
+ DeployableContainer<?> dcService = null;
+
+ if (containerAdapters.size() == 1) {
+ // just one container on cp
+ dcService = containerAdapters.iterator().next();
+ } else {
+ if (dcService == null) {
+ dcService = getContainerAdapter(getAdapterImplClassValue(definition), containerAdapters);
+ }
+ if (dcService == null) {
+ throw new ConfigurationException("Unable to get container adapter from Arquillian configuration.");
+ }
+ }
+
+ // before a Container is added to a collection of containers, inject into its injection point
+ return addContainer(injector.inject(
+ new ContainerImpl(definition.getContainerName(), dcService, definition)));
+
+ } catch (Exception e) {
+ throw new ContainerCreationException("Could not create Container " + definition.getContainerName(), e);
+ }
+ }
+
+ @Override
+ public List<Container> getContainers() {
+ return Collections.unmodifiableList(new ArrayList<>(containers));
+ }
+
+ @Override
+ public Container getContainer(TargetDescription target) {
+ Validate.notNull(target, "Target must be specified");
+ if (TargetDescription.DEFAULT.equals(target)) {
+ return findDefaultContainer();
+ }
+ return findMatchingContainer(target.getName());
+ }
+
+ private Container addContainer(Container container) {
+ containers.add(container);
+ return container;
+ }
+
+ private Container findDefaultContainer() {
+ if (containers.size() == 1) {
+ return containers.get(0);
+ }
+ for (Container container : containers) {
+ if (container.getContainerConfiguration().isDefault()) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+ private Container findMatchingContainer(String name) {
+ for (Container container : containers) {
+ if (container.getName().equals(name)) {
+ return container;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Container getContainer(String name) {
+ return findMatchingContainer(name);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/RegistryCreator.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/RegistryCreator.java
new file mode 100644
index 0000000..ffcccd5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/RegistryCreator.java
@@ -0,0 +1,186 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2011, 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.arquillian.containers;
+
+import java.util.Collection;
+import java.util.Map;
+
+import org.jboss.arquillian.config.descriptor.api.ArquillianDescriptor;
+import org.jboss.arquillian.config.descriptor.api.ContainerDef;
+import org.jboss.arquillian.config.descriptor.api.GroupDef;
+import org.jboss.arquillian.container.spi.ContainerRegistry;
+import org.jboss.arquillian.container.spi.client.container.DeployableContainer;
+import org.jboss.arquillian.core.api.Injector;
+import org.jboss.arquillian.core.api.Instance;
+import org.jboss.arquillian.core.api.InstanceProducer;
+import org.jboss.arquillian.core.api.annotation.ApplicationScoped;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.core.spi.ServiceLoader;
+import org.jboss.arquillian.core.spi.Validate;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.arquillian.containers.SecurityActions.isClassPresent;
+import static org.keycloak.testsuite.arquillian.containers.SecurityActions.loadClass;
+
+/**
+ * Registers all container adapters.
+ *
+ * @author Dominik Pospisil <dpospisi@redhat.com>
+ * @author Stefan Miklosovic <smikloso@redhat.com>
+ * @author Tomas Kyjovsky <tkyjovsk@redhat.com>
+ * @author Vlasta Ramik <vramik@redhat.com>
+ */
+public class RegistryCreator {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ @Inject
+ @ApplicationScoped
+ private InstanceProducer<ContainerRegistry> registry;
+
+ @Inject
+ private Instance<Injector> injector;
+
+ @Inject
+ private Instance<ServiceLoader> loader;
+
+ private String authContainer;
+ private String migrationContainer;
+
+ public void createRegistry(@Observes ArquillianDescriptor event) {
+ ContainerRegistry reg = new Registry(injector.get());
+ ServiceLoader serviceLoader = loader.get();
+
+ @SuppressWarnings("rawtypes")
+ Collection<DeployableContainer> containers = serviceLoader.all(DeployableContainer.class);
+
+ if (containers.isEmpty()) {
+ throw new IllegalStateException("There are not any container adapters on the classpath");
+ }
+
+ for (ContainerDef container : event.getContainers()) {
+ if (isCreatingContainer(container, containers)) {
+ if (isEnabled(container)) {
+ checkMultipleEnabledContainers(container);
+ reg.create(container, serviceLoader);
+ } else {
+ log.info("Container is disabled: " + container.getContainerName());
+ }
+ }
+ }
+
+ for (GroupDef group : event.getGroups()) {
+ for (ContainerDef container : group.getGroupContainers()) {
+ if (isCreatingContainer(container, containers)) {
+ if (isEnabled(container)) {
+ //TODO add checkMultipleEnabledContainers according to groups
+ reg.create(container, serviceLoader);
+ } else {
+ log.info("Container is disabled: " + container.getContainerName());
+ }
+ }
+ }
+ }
+
+ registry.set(reg);
+ }
+
+ private static final String ENABLED = "enabled";
+
+ private boolean isEnabled(ContainerDef containerDef) {
+ Map<String, String> props = containerDef.getContainerProperties();
+ return !props.containsKey(ENABLED)
+ || (props.containsKey(ENABLED) && props.get(ENABLED).equals("true"));
+ }
+
+ private void checkMultipleEnabledContainers(ContainerDef containerDef) {
+ String containerName = containerDef.getContainerName();
+
+ if (containerName.startsWith("keycloak")) {
+ if (migrationContainer == null) {
+ migrationContainer = containerName;
+ } else {
+ throw new RuntimeException("There is more than one migration container "
+ + "enabled in arquillian.xml. It has to be enabled at most one. "
+ + "Do not activate more than one migration profile.");
+ }
+ } else if (containerName.startsWith("auth-server")) {
+ if (authContainer == null) {
+ authContainer = containerName;
+ } else {
+ throw new RuntimeException("There is more than one auth containec enabled "
+ + "in arquillian.xml. It has to be enabled exactly one. Do not "
+ + "activate more than one auth profile.");
+ }
+ }
+ }
+
+ @SuppressWarnings("rawtypes")
+ private boolean isCreatingContainer(ContainerDef containerDef, Collection<DeployableContainer> containers) {
+
+ if (hasAdapterImplClassProperty(containerDef)) {
+ if (isClassPresent(getAdapterImplClassValue(containerDef))) {
+ return DeployableContainer.class.isAssignableFrom(
+ loadClass(getAdapterImplClassValue(containerDef)));
+ }
+ }
+
+ return false;
+ }
+
+ public static boolean hasAdapterImplClassProperty(ContainerDef containerDef) {
+ for (Map.Entry<String, String> entry : containerDef.getContainerProperties().entrySet()) {
+ if (entry.getKey().equals(ADAPTER_IMPL_CONFIG_STRING)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public static String getAdapterImplClassValue(ContainerDef containerDef) {
+ return containerDef.getContainerProperties().get(ADAPTER_IMPL_CONFIG_STRING).trim();
+ }
+ public static final String ADAPTER_IMPL_CONFIG_STRING = "adapterImplClass";
+
+ @SuppressWarnings("rawtypes")
+ public static DeployableContainer<?> getContainerAdapter(String adapterImplClass, Collection<DeployableContainer> containers) {
+ Validate.notNullOrEmpty(adapterImplClass, "The value of " + ADAPTER_IMPL_CONFIG_STRING + " can not be a null object "
+ + "nor an empty string!");
+
+ Class<?> foundAdapter = null;
+
+ if (isClassPresent(adapterImplClass)) {
+ foundAdapter = loadClass(adapterImplClass);
+ } else {
+ return null;
+ }
+
+ for (DeployableContainer<?> container : containers) {
+ if (foundAdapter.isInstance(container)) {
+ return container;
+ }
+ }
+
+ return null;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/SecurityActions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/SecurityActions.java
new file mode 100644
index 0000000..941c10a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/containers/SecurityActions.java
@@ -0,0 +1,307 @@
+/*
+ * JBoss, Home of Professional Open Source
+ * Copyright 2009, Red Hat Middleware LLC, and individual contributors
+ * by the @authors tag. See the copyright.txt in the distribution for a
+ * full listing of individual contributors.
+ *
+ * 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.testsuite.arquillian.containers;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedActionException;
+import java.security.PrivilegedExceptionAction;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A set of privileged actions that are not to leak out of this package
+ *
+ * @version $Revision: $
+ */
+final class SecurityActions {
+
+ // -------------------------------------------------------------------------------||
+ // Constructor -------------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+ /**
+ * No instantiation
+ */
+ private SecurityActions() {
+ throw new UnsupportedOperationException("No instantiation");
+ }
+
+ // -------------------------------------------------------------------------------||
+ // Utility Methods ---------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+ /**
+ * Obtains the Thread Context ClassLoader
+ */
+ static ClassLoader getThreadContextClassLoader() {
+ return AccessController.doPrivileged(GetTcclAction.INSTANCE);
+ }
+
+ static boolean isClassPresent(String name) {
+ try {
+ loadClass(name);
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ static Class<?> loadClass(String className) {
+ try {
+ return Class.forName(className, true, getThreadContextClassLoader());
+ } catch (ClassNotFoundException e) {
+ try {
+ return Class.forName(className, true, SecurityActions.class.getClassLoader());
+ } catch (ClassNotFoundException e2) {
+ throw new RuntimeException("Could not load class " + className, e2);
+ }
+ }
+ }
+
+ static <T> T newInstance(final String className, final Class<?>[] argumentTypes, final Object[] arguments,
+ final Class<T> expectedType) {
+ return newInstance(className, argumentTypes, arguments, expectedType, getThreadContextClassLoader());
+ }
+
+ static <T> T newInstance(final String className, final Class<?>[] argumentTypes, final Object[] arguments,
+ final Class<T> expectedType, ClassLoader classLoader) {
+ Class<?> clazz = null;
+ try {
+ clazz = Class.forName(className, false, classLoader);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not load class " + className, e);
+ }
+ Object obj = newInstance(clazz, argumentTypes, arguments);
+ try {
+ return expectedType.cast(obj);
+ } catch (Exception e) {
+ throw new RuntimeException("Loaded class " + className + " is not of expected type " + expectedType, e);
+ }
+ }
+
+ /**
+ * Create a new instance by finding a constructor that matches the
+ * argumentTypes signature using the arguments for instantiation.
+ *
+ * @param className Full classname of class to create
+ * @param argumentTypes The constructor argument types
+ * @param arguments The constructor arguments
+ * @return a new instance
+ * @throws IllegalArgumentException if className, argumentTypes, or
+ * arguments are null
+ * @throws RuntimeException if any exceptions during creation
+ * @author <a href="mailto:aslak@conduct.no">Aslak Knutsen</a>
+ * @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
+ */
+ static <T> T newInstance(final Class<T> implClass, final Class<?>[] argumentTypes, final Object[] arguments) {
+ if (implClass == null) {
+ throw new IllegalArgumentException("ImplClass must be specified");
+ }
+ if (argumentTypes == null) {
+ throw new IllegalArgumentException("ArgumentTypes must be specified. Use empty array if no arguments");
+ }
+ if (arguments == null) {
+ throw new IllegalArgumentException("Arguments must be specified. Use empty array if no arguments");
+ }
+ final T obj;
+ try {
+ Constructor<T> constructor = getConstructor(implClass, argumentTypes);
+ if (!constructor.isAccessible()) {
+ constructor.setAccessible(true);
+ }
+ obj = constructor.newInstance(arguments);
+ } catch (Exception e) {
+ throw new RuntimeException("Could not create new instance of " + implClass, e);
+ }
+
+ return obj;
+ }
+
+ /**
+ * Obtains the Constructor specified from the given Class and argument types
+ *
+ * @param clazz
+ * @param argumentTypes
+ * @return
+ * @throws NoSuchMethodException
+ */
+ static <T> Constructor<T> getConstructor(final Class<T> clazz, final Class<?>... argumentTypes)
+ throws NoSuchMethodException {
+ try {
+ return AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<T>>() {
+ @Override
+ public Constructor<T> run() throws NoSuchMethodException {
+ return clazz.getDeclaredConstructor(argumentTypes);
+ }
+ });
+ } // Unwrap
+ catch (final PrivilegedActionException pae) {
+ final Throwable t = pae.getCause();
+ // Rethrow
+ if (t instanceof NoSuchMethodException) {
+ throw (NoSuchMethodException) t;
+ } else {
+ // No other checked Exception thrown by Class.getConstructor
+ try {
+ throw (RuntimeException) t;
+ } // Just in case we've really messed up
+ catch (final ClassCastException cce) {
+ throw new RuntimeException("Obtained unchecked Exception; this code should never be reached", t);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set a single Field value
+ *
+ * @param target The object to set it on
+ * @param fieldName The field name
+ * @param value The new value
+ */
+ public static void setFieldValue(final Class<?> source, final Object target, final String fieldName,
+ final Object value) throws NoSuchFieldException {
+ try {
+ AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
+ @Override
+ public Void run() throws Exception {
+ Field field = source.getDeclaredField(fieldName);
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ field.set(target, value);
+ return null;
+ }
+ });
+ } // Unwrap
+ catch (final PrivilegedActionException pae) {
+ final Throwable t = pae.getCause();
+ // Rethrow
+ if (t instanceof NoSuchFieldException) {
+ throw (NoSuchFieldException) t;
+ } else {
+ // No other checked Exception thrown by Class.getConstructor
+ try {
+ throw (RuntimeException) t;
+ } // Just in case we've really messed up
+ catch (final ClassCastException cce) {
+ throw new RuntimeException("Obtained unchecked Exception; this code should never be reached", t);
+ }
+ }
+ }
+ }
+
+ public static List<Field> getFieldsWithAnnotation(final Class<?> source,
+ final Class<? extends Annotation> annotationClass) {
+ List<Field> declaredAccessableFields = AccessController.doPrivileged(new PrivilegedAction<List<Field>>() {
+ @Override
+ public List<Field> run() {
+ List<Field> foundFields = new ArrayList<Field>();
+ Class<?> nextSource = source;
+ while (nextSource != Object.class) {
+ for (Field field : nextSource.getDeclaredFields()) {
+ if (field.isAnnotationPresent(annotationClass)) {
+ if (!field.isAccessible()) {
+ field.setAccessible(true);
+ }
+ foundFields.add(field);
+ }
+ }
+ nextSource = nextSource.getSuperclass();
+ }
+ return foundFields;
+ }
+ });
+ return declaredAccessableFields;
+ }
+
+ public static List<Method> getMethodsWithAnnotation(final Class<?> source,
+ final Class<? extends Annotation> annotationClass) {
+ List<Method> declaredAccessableMethods = AccessController.doPrivileged(new PrivilegedAction<List<Method>>() {
+ @Override
+ public List<Method> run() {
+ List<Method> foundMethods = new ArrayList<Method>();
+ Class<?> nextSource = source;
+ while (nextSource != Object.class) {
+ for (Method method : nextSource.getDeclaredMethods()) {
+ if (method.isAnnotationPresent(annotationClass)) {
+ if (!method.isAccessible()) {
+ method.setAccessible(true);
+ }
+ foundMethods.add(method);
+ }
+ }
+ nextSource = nextSource.getSuperclass();
+ }
+ return foundMethods;
+ }
+ });
+ return declaredAccessableMethods;
+ }
+
+ static String getProperty(final String key) {
+ try {
+ String value = AccessController.doPrivileged(new PrivilegedExceptionAction<String>() {
+ @Override
+ public String run() {
+ return System.getProperty(key);
+ }
+ });
+ return value;
+ } // Unwrap
+ catch (final PrivilegedActionException pae) {
+ final Throwable t = pae.getCause();
+ // Rethrow
+ if (t instanceof SecurityException) {
+ throw (SecurityException) t;
+ }
+ if (t instanceof NullPointerException) {
+ throw (NullPointerException) t;
+ } else if (t instanceof IllegalArgumentException) {
+ throw (IllegalArgumentException) t;
+ } else {
+ // No other checked Exception thrown by System.getProperty
+ try {
+ throw (RuntimeException) t;
+ } // Just in case we've really messed up
+ catch (final ClassCastException cce) {
+ throw new RuntimeException("Obtained unchecked Exception; this code should never be reached", t);
+ }
+ }
+ }
+ }
+
+ // -------------------------------------------------------------------------------||
+ // Inner Classes -----------------------------------------------------------------||
+ // -------------------------------------------------------------------------------||
+ /**
+ * Single instance to get the TCCL
+ */
+ private enum GetTcclAction implements PrivilegedAction<ClassLoader> {
+
+ INSTANCE;
+
+ @Override
+ public ClassLoader run() {
+ return Thread.currentThread().getContextClassLoader();
+ }
+
+ }
+}
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
new file mode 100644
index 0000000..ffb3e0c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/ContainersTestEnricher.java
@@ -0,0 +1,187 @@
+package org.keycloak.testsuite.arquillian;
+
+import java.net.MalformedURLException;
+import java.net.URL;
+import org.jboss.arquillian.container.spi.event.StartSuiteContainers;
+import org.jboss.arquillian.container.spi.event.StopSuiteContainers;
+import org.jboss.arquillian.container.test.api.ContainerController;
+import org.jboss.arquillian.core.api.Event;
+import org.jboss.arquillian.core.api.Instance;
+import org.jboss.arquillian.core.api.InstanceProducer;
+import org.jboss.arquillian.core.api.annotation.Inject;
+import org.jboss.arquillian.core.api.annotation.Observes;
+import org.jboss.arquillian.test.spi.annotation.ClassScoped;
+import org.jboss.arquillian.test.spi.annotation.SuiteScoped;
+import org.jboss.arquillian.container.spi.event.container.AfterStart;
+import org.jboss.arquillian.test.spi.event.suite.BeforeClass;
+import org.jboss.arquillian.test.spi.event.suite.BeforeSuite;
+import org.jboss.logging.Logger;
+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 static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+
+/**
+ *
+ * @author tkyjovsk
+ * @author vramik
+ */
+public class ContainersTestEnricher {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ @Inject
+ private Instance<ContainerController> containerController;
+
+ @Inject
+ private Event<StopSuiteContainers> stopSuiteContainers;
+
+ private String appServerQualifier;
+
+ private static final String AUTH_SERVER_CONTAINER_PROPERTY = "auth.server.container";
+ private static final String AUTH_SERVER_CONTAINER_DEFAULT = "auth-server-undertow";
+
+ @Inject
+ @SuiteScoped
+ private InstanceProducer<SuiteContext> suiteContext;
+
+ @Inject
+ @ClassScoped
+ private InstanceProducer<TestContext> testContext;
+
+ @Inject
+ @ClassScoped
+ private InstanceProducer<Keycloak> adminClient;
+
+ private ContainerController controller;
+
+ private final boolean migrationTests = System.getProperty("migration", "false").equals("true");
+ private boolean alreadyStopped = false;
+
+ public void startSuiteContainers(@Observes(precedence = 1) StartSuiteContainers event) {
+ if (migrationTests) {
+ log.info("\n### Starting keycloak with previous version ###\n");
+ }
+ }
+
+ public void stopMigrationContainer(@Observes AfterStart event) {
+ if (migrationTests && !alreadyStopped) {
+ log.info("\n### Stopping keycloak with previous version ###\n");
+ stopSuiteContainers.fire(new StopSuiteContainers());
+ }
+ alreadyStopped = true;
+ }
+
+ public void beforeSuite(@Observes BeforeSuite event) {
+ suiteContext.set(new SuiteContext());
+ }
+
+ public void startContainers(@Observes(precedence = -1) BeforeClass event) {
+ controller = containerController.get();
+
+ Class testClass = event.getTestClass().getJavaClass();
+ appServerQualifier = getAppServerQualifier(testClass);
+
+ if (!controller.isStarted(appServerQualifier)) {
+ log.info("\nSTARTING APP SERVER: " + appServerQualifier + "\n");
+ controller.start(appServerQualifier);
+ log.info("");
+ }
+
+ initializeTestContext(testClass);
+ initializeAdminClient();
+ }
+
+ private void initializeTestContext(Class testClass) {
+ String authServerContextRootStr = getAuthServerContextRootFromSystemProperty();
+ String appServerContextRootStr = isRelative(testClass)
+ ? authServerContextRootStr
+ : getAppServerContextRootFromSystemProperty();
+ try {
+ URL authServerContextRoot = new URL(authServerContextRootStr);
+ URL appServerContextRoot = new URL(appServerContextRootStr);
+
+ testContext.set(new TestContext(authServerContextRoot, appServerContextRoot));
+
+ } catch (MalformedURLException ex) {
+ throw new IllegalStateException("Malformed url.", ex);
+ }
+ }
+
+ private void initializeAdminClient() {
+ adminClient.set(Keycloak.getInstance(
+ getAuthServerContextRootFromSystemProperty() + "/auth",
+ MASTER, ADMIN, ADMIN, Constants.ADMIN_CONSOLE_CLIENT_ID));
+ }
+
+ /**
+ *
+ * @param testClass
+ * @param annotationClass
+ * @return testClass or the nearest superclass of testClass annotated with
+ * annotationClass
+ */
+ public static Class getNearestSuperclassWithAnnotation(Class testClass, Class annotationClass) {
+ return testClass.isAnnotationPresent(annotationClass) ? testClass
+ : (testClass.getSuperclass().equals(Object.class) ? null // stop recursion
+ : getNearestSuperclassWithAnnotation(testClass.getSuperclass(), annotationClass)); // continue recursion
+ }
+
+ public static String getAuthServerQualifier() {
+ return System.getProperty(
+ AUTH_SERVER_CONTAINER_PROPERTY,
+ AUTH_SERVER_CONTAINER_DEFAULT);
+ }
+
+ public static String getAppServerQualifier(Class testClass) {
+ Class<? extends ContainersTestEnricher> annotatedClass = getNearestSuperclassWithAnnotation(testClass, AppServerContainer.class);
+
+ String appServerQ = (annotatedClass == null ? null
+ : annotatedClass.getAnnotation(AppServerContainer.class).value());
+
+ return appServerQ == null || appServerQ.isEmpty()
+ ? getAuthServerQualifier() // app server == auth server
+ : appServerQ;
+ }
+
+ public static boolean hasAppServerContainerAnnotation(Class testClass) {
+ return getNearestSuperclassWithAnnotation(testClass, AppServerContainer.class) != null;
+ }
+
+ public static boolean isRelative(Class testClass) {
+ return getAppServerQualifier(testClass).equals(getAuthServerQualifier());
+ }
+
+ public static String getAdapterLibsLocationProperty(Class testClass) {
+ Class<? extends ContainersTestEnricher> annotatedClass = getNearestSuperclassWithAnnotation(testClass, AdapterLibsLocationProperty.class);
+ return (annotatedClass == null ? null
+ : annotatedClass.getAnnotation(AdapterLibsLocationProperty.class).value());
+ }
+
+ public static boolean isWildflyAppServer(Class testClass) {
+ return getAppServerQualifier(testClass).contains("wildfly");
+ }
+
+ public static boolean isTomcatAppServer(Class testClass) {
+ return getAppServerQualifier(testClass).contains("tomcat");
+ }
+
+ public static boolean isOSGiAppServer(Class testClass) {
+ String q = getAppServerQualifier(testClass);
+ return q.contains("karaf") || q.contains("fuse");
+ }
+
+ public static String getAuthServerContextRootFromSystemProperty() {
+ // TODO find if this can be extracted from ARQ metadata instead of System properties
+ return "http://localhost:" + Integer.parseInt(
+ System.getProperty("auth.server.http.port", "8180"));
+ }
+
+ public static String getAppServerContextRootFromSystemProperty() {
+ return "http://localhost:" + Integer.parseInt(
+ System.getProperty("app.server.http.port", "8280"));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
new file mode 100644
index 0000000..0a965d8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentArchiveProcessor.java
@@ -0,0 +1,140 @@
+package org.keycloak.testsuite.arquillian;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.apache.tools.ant.DirectoryScanner;
+import org.jboss.arquillian.container.test.spi.client.deployment.ApplicationArchiveProcessor;
+import org.jboss.arquillian.test.spi.TestClass;
+import org.jboss.logging.Logger;
+import org.jboss.logging.Logger.Level;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.keycloak.representations.adapters.config.BaseAdapterConfig;
+import static org.keycloak.testsuite.arquillian.ContainersTestEnricher.*;
+import org.keycloak.testsuite.adapter.AdapterLibsMode;
+import static org.keycloak.testsuite.util.IOUtil.loadJson;;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class DeploymentArchiveProcessor implements ApplicationArchiveProcessor {
+
+ public static final String REALM_KEY = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB";
+
+ protected final Logger log = org.jboss.logging.Logger.getLogger(this.getClass());
+
+ public static final String WEBXML_PATH = "/WEB-INF/web.xml";
+ public static final String ADAPTER_CONFIG_PATH = "/WEB-INF/keycloak.json";
+ public static final String ADAPTER_CONFIG_PATH_TENANT1 = "/WEB-INF/classes/tenant1-keycloak.json";
+ public static final String ADAPTER_CONFIG_PATH_TENANT2 = "/WEB-INF/classes/tenant2-keycloak.json";
+ public static final String ADAPTER_CONFIG_PATH_JS = "/keycloak.json";
+
+ @Override
+ public void process(Archive<?> archive, TestClass testClass) {
+ log.info("Processing archive " + archive.getName());
+// if (isAdapterTest(testClass)) {
+ modifyAdapterConfigs(archive, testClass);
+ attachKeycloakLibs(archive, testClass);
+ modifyWebXml(archive, testClass);
+// } else {
+// log.info(testClass.getJavaClass().getSimpleName() + " is not an AdapterTest");
+// }
+ }
+
+ public static boolean isAdapterTest(TestClass testClass) {
+ return hasAppServerContainerAnnotation(testClass.getJavaClass());
+ }
+
+ protected void modifyAdapterConfigs(Archive<?> archive, TestClass testClass) {
+ boolean relative = isRelative(testClass.getJavaClass());
+ modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH, relative);
+ modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_TENANT1, relative);
+ modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_TENANT2, relative);
+ modifyAdapterConfig(archive, ADAPTER_CONFIG_PATH_JS, relative);
+ }
+
+ protected void modifyAdapterConfig(Archive<?> archive, String adapterConfigPath, boolean relative) {
+ if (archive.contains(adapterConfigPath)) {
+ log.info("Modifying adapter config " + adapterConfigPath + " in " + archive.getName());
+ try {
+ BaseAdapterConfig adapterConfig = loadJson(archive.get(adapterConfigPath)
+ .getAsset().openStream(), BaseAdapterConfig.class);
+
+ log.info(" setting " + (relative ? "" : "non-") + "relative auth-server-url");
+ if (relative) {
+ adapterConfig.setAuthServerUrl("/auth");
+// ac.setRealmKey(null); // TODO verify if realm key is required for relative scneario
+ } else {
+ adapterConfig.setAuthServerUrl(getAuthServerContextRootFromSystemProperty() + "/auth");
+ adapterConfig.setRealmKey(REALM_KEY);
+ }
+
+ archive.add(new StringAsset(JsonSerialization.writeValueAsPrettyString(adapterConfig)),
+ adapterConfigPath);
+
+ } catch (IOException ex) {
+ log.log(Level.FATAL, "Cannot serialize adapter config to JSON.", ex);
+ }
+ }
+ }
+
+ protected void attachKeycloakLibs(Archive<?> archive, TestClass testClass) {
+ AdapterLibsMode adapterType = AdapterLibsMode.getByType(System.getProperty("adapter.libs.mode",
+ AdapterLibsMode.PROVIDED.getType()));
+ log.info("Adapter type: " + adapterType);
+ if (adapterType.equals(AdapterLibsMode.BUNDLED)) {
+ log.info("Attaching keycloak adapter libs to " + archive.getName());
+
+ String libsLocationProperty = getAdapterLibsLocationProperty(testClass.getJavaClass());
+ assert libsLocationProperty != null;
+ File libsLocation = new File(System.getProperty(libsLocationProperty));
+ assert libsLocation.exists();
+ log.info("Libs location: " + libsLocation.getPath());
+
+ WebArchive war = (WebArchive) archive;
+
+ for (File lib : getAdapterLibs(libsLocation)) {
+ log.info(" attaching: " + lib.getName());
+ war.addAsLibrary(lib);
+ }
+ } else {
+ log.info("Expecting keycloak adapter libs to be provided by the server.");
+ }
+ }
+
+ DirectoryScanner scanner = new DirectoryScanner();
+
+ protected List<File> getAdapterLibs(File adapterLibsLocation) {
+ assert adapterLibsLocation.exists();
+ List<File> libs = new ArrayList<>();
+ scanner.setBasedir(adapterLibsLocation);
+ scanner.setIncludes(new String[]{"**/*jar"});
+ scanner.scan();
+ for (String lib : scanner.getIncludedFiles()) {
+ libs.add(new File(adapterLibsLocation, lib));
+ }
+ return libs;
+ }
+
+ protected void modifyWebXml(Archive<?> archive, TestClass testClass) {
+ if (isTomcatAppServer(testClass.getJavaClass())) {
+ try {
+ String webXmlContent = IOUtils.toString(
+ archive.get(WEBXML_PATH).getAsset().openStream());
+
+ webXmlContent = webXmlContent.replace("<auth-method>KEYCLOAK</auth-method>", "<auth-method>BASIC</auth-method>");
+
+ archive.add(new StringAsset((webXmlContent)), WEBXML_PATH);
+ } catch (IOException ex) {
+ throw new RuntimeException("Cannot load web.xml from archive.");
+ }
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentTargetModifier.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentTargetModifier.java
new file mode 100644
index 0000000..faaeb81
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/DeploymentTargetModifier.java
@@ -0,0 +1,40 @@
+package org.keycloak.testsuite.arquillian;
+
+import java.util.List;
+import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
+import org.jboss.arquillian.container.spi.client.deployment.TargetDescription;
+import org.jboss.arquillian.container.test.impl.client.deployment.AnnotationDeploymentScenarioGenerator;
+import org.jboss.arquillian.test.spi.TestClass;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.arquillian.ContainersTestEnricher.*;
+
+/**
+ * Changes target container for all Arquillian deployments based on value of
+ * @AppServerContainer.
+ *
+ * @author tkyjovsk
+ */
+public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenerator {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ @Override
+ public List<DeploymentDescription> generate(TestClass testClass) {
+ List<DeploymentDescription> deployments = super.generate(testClass);
+
+ String appServerQualifier = getAppServerQualifier(
+ testClass.getJavaClass());
+
+ if (appServerQualifier != null && !appServerQualifier.isEmpty()) {
+ for (DeploymentDescription deployment : deployments) {
+ if (deployment.getTarget() == null || !deployment.getTarget().getName().equals(appServerQualifier)) {
+ log.debug("Setting target container for " + deployment.getName() + ": " + appServerQualifier);
+ deployment.setTarget(new TargetDescription(appServerQualifier));
+ }
+ }
+ }
+
+ return deployments;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JBossJiraParser.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JBossJiraParser.java
new file mode 100644
index 0000000..ea606b8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JBossJiraParser.java
@@ -0,0 +1,43 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.arquillian.jira;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.MediaType;
+
+/**
+ *
+ * @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
+ */
+public class JBossJiraParser {
+
+ private static final String JBOSS_TRACKER_REST_URL = "https://issues.jboss.org/rest/api/latest/issue/";
+
+ public static boolean isIssueClosed(String issueId) {
+ Status issueStatus;
+ try {
+ issueStatus = getIssueStatus(issueId);
+ } catch(Exception e) {
+ issueStatus = Status.CLOSED; //let the test run in case there is no connection
+ }
+ return issueStatus == Status.CLOSED || issueStatus == Status.RESOLVED;
+ }
+
+ private static Status getIssueStatus(String issueId) throws Exception {
+ Client client = ClientBuilder.newClient();
+ WebTarget target = client.target(JBOSS_TRACKER_REST_URL);
+ String json = target.path(issueId).request().accept(MediaType.APPLICATION_JSON_TYPE).get(String.class);
+ JsonObject jsonObject = new Gson().fromJson(json, JsonElement.class).getAsJsonObject();
+ String status = jsonObject.getAsJsonObject("fields").getAsJsonObject("status").get("name").getAsString();
+ client.close();
+ return Status.getByStatus(status);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Jira.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Jira.java
new file mode 100644
index 0000000..961ae82
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Jira.java
@@ -0,0 +1,28 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.arquillian.jira;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * Value should contain name of the issue listed in JBoss JIRA (like
+ * KEYCLOAK-1234), it can also contain multiple names separated by coma.
+ *
+ * @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
+ *
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Documented
+public @interface Jira {
+
+ String value();
+ boolean enabled() default true;
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JiraTestExecutionDecider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JiraTestExecutionDecider.java
new file mode 100644
index 0000000..fa3c571
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/JiraTestExecutionDecider.java
@@ -0,0 +1,61 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.arquillian.jira;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+import org.jboss.arquillian.test.spi.execution.ExecutionDecision;
+import org.jboss.arquillian.test.spi.execution.TestExecutionDecider;
+
+import static org.keycloak.testsuite.arquillian.jira.JBossJiraParser.isIssueClosed;
+
+/**
+ *
+ * @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
+ */
+public class JiraTestExecutionDecider implements TestExecutionDecider {
+
+ private static Map<String, Boolean> cache = new HashMap<String, Boolean>();
+
+ @Override
+ public ExecutionDecision decide(Method method) {
+ Jira jiraAnnotation = method.getAnnotation(Jira.class);
+ if (jiraAnnotation != null && jiraAnnotation.enabled()) {
+ boolean executeTest = true;
+ String[] issueIds = getIssuesId(jiraAnnotation.value());
+ for (String issueId : issueIds) {
+ if (cache.containsKey(issueId)) {
+ executeTest = cache.get(issueId);
+ } else {
+ if (isIssueClosed(issueId)) {
+ cache.put(issueId, true);
+ } else {
+ executeTest = false;
+ cache.put(issueId, false);
+ }
+ }
+ }
+
+ if (executeTest) {
+ return ExecutionDecision.execute();
+ } else {
+ return ExecutionDecision.dontExecute("Issue is still opened, therefore skipping the test " + method.getName());
+ }
+ }
+ return ExecutionDecision.execute();
+ }
+
+ private String[] getIssuesId(String value) {
+ return value.replaceAll("\\s+", "").split(",");
+ }
+
+ @Override
+ public int precedence() {
+ return 0;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Status.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Status.java
new file mode 100644
index 0000000..47f707a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/jira/Status.java
@@ -0,0 +1,36 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package org.keycloak.testsuite.arquillian.jira;
+
+/**
+ *
+ * @author <a href="mailto:pmensik@redhat.com">Petr Mensik</a>
+ */
+public enum Status {
+
+ OPEN("Open"), CLOSED("Closed"), PULL_REQUEST_SENT("Pull Request Sent"), REOPENED("Reopened"),
+ RESOLVED("Resolved"), CODING_IN_PROGRESS("Coding In Progress ");
+
+ private String status;
+
+ private Status(String status) {
+ this.status = status;
+ }
+
+ public String getStatus() {
+ return status;
+ }
+
+ public static Status getByStatus(String status) {
+ for (Status s : Status.values()) {
+ if (s.getStatus().equals(status)) {
+ return s;
+ }
+ }
+ return null;
+ }
+
+}
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
new file mode 100644
index 0000000..cf25d05
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java
@@ -0,0 +1,49 @@
+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.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;
+import org.jboss.arquillian.container.test.spi.client.deployment.DeploymentScenarioGenerator;
+import org.jboss.arquillian.core.spi.LoadableExtension;
+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;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class KeycloakArquillianExtension implements LoadableExtension {
+
+ @Override
+ public void register(ExtensionBuilder builder) {
+
+ builder
+ .service(ResourceProvider.class, SuiteContextProvider.class)
+ .service(ResourceProvider.class, TestContextProvider.class)
+ .service(ResourceProvider.class, AdminClientProvider.class);
+
+ builder
+ .service(DeploymentScenarioGenerator.class, DeploymentTargetModifier.class)
+ .service(ApplicationArchiveProcessor.class, DeploymentArchiveProcessor.class)
+ .observer(ContainersTestEnricher.class);
+
+ builder
+ .service(DeployableContainer.class, CustomUndertowContainer.class);
+
+ builder
+ .service(TestExecutionDecider.class, JiraTestExecutionDecider.class);
+
+ builder
+ .override(ResourceProvider.class, URLResourceProvider.class, URLProvider.class)
+ .override(ResourceProvider.class, CustomizableURLResourceProvider.class, URLProvider.class);
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/AdminClientProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/AdminClientProvider.java
new file mode 100644
index 0000000..8e7c3ac
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/AdminClientProvider.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.arquillian.provider;
+
+import java.lang.annotation.Annotation;
+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.admin.client.Keycloak;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminClientProvider implements ResourceProvider {
+
+ @Inject
+ Instance<Keycloak> adminClient;
+
+ @Override
+ public boolean canProvide(Class<?> type) {
+ return Keycloak.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
+ return adminClient.get();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/SuiteContextProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/SuiteContextProvider.java
new file mode 100644
index 0000000..2755688
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/SuiteContextProvider.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.arquillian.provider;
+
+import org.keycloak.testsuite.arquillian.SuiteContext;
+import java.lang.annotation.Annotation;
+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;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class SuiteContextProvider implements ResourceProvider {
+
+ @Inject
+ Instance<SuiteContext> suiteContext;
+
+ @Override
+ public boolean canProvide(Class<?> type) {
+ return SuiteContext.class.isAssignableFrom(type);
+ }
+
+ @Override
+ public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
+ return suiteContext.get();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/TestContextProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/TestContextProvider.java
new file mode 100644
index 0000000..ebe7326
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/TestContextProvider.java
@@ -0,0 +1,30 @@
+package org.keycloak.testsuite.arquillian.provider;
+
+import org.keycloak.testsuite.arquillian.TestContext;
+import java.lang.annotation.Annotation;
+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;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class TestContextProvider implements ResourceProvider {
+
+ @Inject
+ Instance<TestContext> testContext;
+
+ @Override
+ public boolean canProvide(Class<?> type) {
+ return TestContext.class.isAssignableFrom(type);
+ }
+
+ @Override
+ @ClassInjection
+ public Object lookup(ArquillianResource resource, Annotation... qualifiers) {
+ return testContext.get();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
new file mode 100644
index 0000000..dd31483
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/provider/URLProvider.java
@@ -0,0 +1,79 @@
+package org.keycloak.testsuite.arquillian.provider;
+
+import org.keycloak.testsuite.arquillian.TestContext;
+import java.lang.annotation.Annotation;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.arquillian.container.test.impl.enricher.resource.URLResourceProvider;
+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.logging.Logger;
+import org.jboss.logging.Logger.Level;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContext;
+import org.keycloak.testsuite.arquillian.annotation.AuthServerContext;
+
+public class URLProvider extends URLResourceProvider {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ public static final String LOCALHOST_ADDRESS = "127.0.0.1";
+ public static final String LOCALHOST_HOSTNAME = "localhost";
+
+ @Inject
+ Instance<TestContext> testContext;
+
+ private static final Set<String> fixedUrls = new HashSet<>();
+
+ @Override
+ public Object doLookup(ArquillianResource resource, Annotation... qualifiers) {
+ URL url = (URL) super.doLookup(resource, qualifiers);
+
+ // fix injected URL
+ if (url != null) {
+ try {
+ url = fixLocalhost(url);
+ url = removeTrailingSlash(url);
+ } catch (MalformedURLException ex) {
+ log.log(Level.FATAL, null, ex);
+ }
+
+ if (!fixedUrls.contains(url.toString())) {
+ fixedUrls.add(url.toString());
+ log.debug("Fixed injected @ArquillianResource URL to: " + url);
+ }
+ }
+
+ // inject context roots if annotation present
+ for (Annotation a : qualifiers) {
+ if (AuthServerContext.class.isAssignableFrom(a.annotationType())) {
+ return testContext.get().getAuthServerContextRoot();
+ }
+ if (AppServerContext.class.isAssignableFrom(a.annotationType())) {
+ return testContext.get().getAppServerContextRoot();
+ }
+ }
+
+ return url;
+ }
+
+ public URL fixLocalhost(URL url) throws MalformedURLException {
+ URL fixedUrl = url;
+ if (url.getHost().contains(LOCALHOST_ADDRESS)) {
+ fixedUrl = new URL(fixedUrl.toExternalForm().replace(LOCALHOST_ADDRESS, LOCALHOST_HOSTNAME));
+ }
+ return fixedUrl;
+ }
+
+ public URL removeTrailingSlash(URL url) throws MalformedURLException {
+ URL urlWithoutSlash = url;
+ String urlS = url.toExternalForm();
+ if (urlS.endsWith("/")) {
+ urlWithoutSlash = new URL(urlS.substring(0, urlS.length() - 1));
+ }
+ return urlWithoutSlash;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java
new file mode 100644
index 0000000..589c316
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/SuiteContext.java
@@ -0,0 +1,34 @@
+package org.keycloak.testsuite.arquillian;
+
+import java.util.HashMap;
+import java.util.Map;
+import static org.keycloak.testsuite.util.MailServerConfiguration.*;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public final class SuiteContext {
+
+ private boolean adminPasswordUpdated;
+ private final Map<String, String> smtpServer = new HashMap<>();
+
+ public SuiteContext() {
+ this.adminPasswordUpdated = false;
+ smtpServer.put("from", FROM);
+ smtpServer.put("host", HOST);
+ smtpServer.put("port", PORT);
+ }
+
+ public boolean isAdminPasswordUpdated() {
+ return adminPasswordUpdated;
+ }
+
+ public void setAdminPasswordUpdated(boolean adminPasswordUpdated) {
+ this.adminPasswordUpdated = adminPasswordUpdated;
+ }
+
+ public Map<String, String> getSmtpServer() {
+ return smtpServer;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java
new file mode 100644
index 0000000..850c787
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/TestContext.java
@@ -0,0 +1,50 @@
+package org.keycloak.testsuite.arquillian;
+
+import java.net.URL;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public final class TestContext {
+
+ private URL authServerContextRoot;
+ private URL appServerContextRoot;
+
+ private boolean adminLoggedIn;
+
+ public TestContext() {
+ this.adminLoggedIn = false;
+ }
+
+ public TestContext(URL authServerContextRoot, URL appServerContextRoot) {
+ this();
+ this.authServerContextRoot = authServerContextRoot;
+ this.appServerContextRoot = appServerContextRoot;
+ }
+
+ public URL getAuthServerContextRoot() {
+ return authServerContextRoot;
+ }
+
+ public URL getAppServerContextRoot() {
+ return appServerContextRoot;
+ }
+
+ public boolean isAdminLoggedIn() {
+ return adminLoggedIn;
+ }
+
+ public void setAdminLoggedIn(boolean adminLoggedIn) {
+ this.adminLoggedIn = adminLoggedIn;
+ }
+
+ public void setAuthServerContextRoot(URL authServerContextRoot) {
+ this.authServerContextRoot = authServerContextRoot;
+ }
+
+ public void setAppServerContextRoot(URL appServerContextRoot) {
+ this.appServerContextRoot = appServerContextRoot;
+ }
+
+}
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
new file mode 100644
index 0000000..e2dc4db
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainer.java
@@ -0,0 +1,144 @@
+package org.keycloak.testsuite.arquillian.undertow;
+
+import io.undertow.Undertow;
+import io.undertow.servlet.Servlets;
+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;
+import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext;
+import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData;
+import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet;
+import org.jboss.logging.Logger;
+import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
+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;
+
+public class CustomUndertowContainer implements DeployableContainer<CustomUndertowContainerConfiguration> {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ private UndertowJaxrsServer undertow;
+ private CustomUndertowContainerConfiguration configuration;
+
+ private DeploymentInfo createAuthServerDeploymentInfo() {
+ ResteasyDeployment deployment = new ResteasyDeployment();
+ deployment.setApplicationClass(KeycloakApplication.class.getName());
+
+ DeploymentInfo di = undertow.undertowDeployment(deployment);
+ di.setClassLoader(getClass().getClassLoader());
+ di.setContextPath("/auth");
+ di.setDeploymentName("Keycloak");
+
+ 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 connectionFilter = Servlets.filter("ClientConnectionFilter", ClientConnectionFilter.class);
+ di.addFilter(connectionFilter);
+ di.addFilterUrlMapping("ClientConnectionFilter", "/*", DispatcherType.REQUEST);
+
+ return di;
+ }
+
+ public DeploymentInfo getDeplotymentInfoFromArchive(Archive<?> archive) {
+ if (archive instanceof UndertowWebArchive) {
+ return ((UndertowWebArchive) archive).getDeploymentInfo();
+ } else {
+ throw new IllegalArgumentException("UndertowContainer only supports UndertowWebArchive or WebArchive.");
+ }
+ }
+
+ private HTTPContext createHttpContextForDeploymentInfo(DeploymentInfo deploymentInfo) {
+ HTTPContext httpContext = new HTTPContext(configuration.getBindAddress(), configuration.getBindHttpPort());
+ final Map<String, ServletInfo> servlets = deploymentInfo.getServlets();
+ final Collection<ServletInfo> servletsInfo = servlets.values();
+ for (ServletInfo servletInfo : servletsInfo) {
+ httpContext.add(new Servlet(servletInfo.getName(), deploymentInfo.getContextPath()));
+ }
+ return httpContext;
+ }
+
+ @Override
+ public ProtocolMetaData deploy(Archive<?> archive) throws DeploymentException {
+ DeploymentInfo di = getDeplotymentInfoFromArchive(archive);
+ undertow.deploy(di);
+ return new ProtocolMetaData().addContext(
+ createHttpContextForDeploymentInfo(di));
+ }
+
+ @Override
+ public void deploy(Descriptor descriptor) throws DeploymentException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public Class<CustomUndertowContainerConfiguration> getConfigurationClass() {
+ return CustomUndertowContainerConfiguration.class;
+ }
+
+ @Override
+ public ProtocolDescription getDefaultProtocol() {
+ return new ProtocolDescription("Servlet 3.1");
+ }
+
+ @Override
+ public void setup(
+ CustomUndertowContainerConfiguration undertowContainerConfiguration) {
+ this.configuration = undertowContainerConfiguration;
+ }
+
+ @Override
+ public void start() throws LifecycleException {
+ log.info("Starting auth server on embedded Undertow.");
+ long start = System.currentTimeMillis();
+
+ if (undertow == null) {
+ undertow = new UndertowJaxrsServer();
+ }
+ undertow.start(Undertow.builder()
+ .addHttpListener(configuration.getBindHttpPort(), configuration.getBindAddress())
+ .setWorkerThreads(configuration.getWorkerThreads())
+ .setIoThreads(configuration.getWorkerThreads() / 8)
+ );
+
+ undertow.deploy(createAuthServerDeploymentInfo());
+
+ log.info("Auth server started in " + (System.currentTimeMillis() - start) + " ms\n");
+ }
+
+ @Override
+ public void stop() throws LifecycleException {
+ log.info("Stopping auth server.");
+ undertow.stop();
+ }
+
+ @Override
+ public void undeploy(Archive<?> archive) throws DeploymentException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+ @Override
+ public void undeploy(Descriptor descriptor) throws DeploymentException {
+ throw new UnsupportedOperationException("Not implemented");
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainerConfiguration.java
new file mode 100644
index 0000000..5d87119
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/undertow/CustomUndertowContainerConfiguration.java
@@ -0,0 +1,35 @@
+package org.keycloak.testsuite.arquillian.undertow;
+
+import org.arquillian.undertow.UndertowContainerConfiguration;
+import org.jboss.arquillian.container.spi.ConfigurationException;
+
+public class CustomUndertowContainerConfiguration extends UndertowContainerConfiguration {
+
+ private int workerThreads = Math.max(Runtime.getRuntime().availableProcessors(), 2) * 8;
+ private String resourcesHome;
+
+ public int getWorkerThreads() {
+ return workerThreads;
+ }
+
+ public void setWorkerThreads(int workerThreads) {
+ this.workerThreads = workerThreads;
+ }
+
+ public String getResourcesHome() {
+ return resourcesHome;
+ }
+
+ public void setResourcesHome(String resourcesHome) {
+ this.resourcesHome = resourcesHome;
+ }
+
+ @Override
+ public void validate() throws ConfigurationException {
+ super.validate();
+
+ // TODO validate workerThreads
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Account.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Account.java
new file mode 100644
index 0000000..724928a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/Account.java
@@ -0,0 +1,81 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.account;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class Account extends AccountManagement {
+
+ @FindBy(id = "username")
+ private WebElement username;
+
+ @FindBy(id = "email")
+ private WebElement email;
+
+ @FindBy(id = "lastName")
+ private WebElement lastName;
+
+ @FindBy(id = "firstName")
+ private WebElement firstName;
+
+ public String getUsername() {
+ return username.getAttribute("value");
+ }
+
+ public String getEmail() {
+ return email.getAttribute("value");
+ }
+
+ public String getFirstName() {
+ return firstName.getAttribute("value");
+ }
+
+ public String getLastName() {
+ return lastName.getAttribute("value");
+ }
+
+ public Account setUsername(String value) {
+ username.clear();
+ username.sendKeys(value);
+ return this;
+ }
+
+ public Account setEmail(String value) {
+ email.clear();
+ email.sendKeys(value);
+ return this;
+ }
+
+ public Account setFirstName(String value) {
+ firstName.clear();
+ firstName.sendKeys(value);
+ return this;
+ }
+
+ public Account setLastName(String value) {
+ lastName.clear();
+ lastName.sendKeys(value);
+ return this;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java
new file mode 100644
index 0000000..f78d31a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/AccountFields.java
@@ -0,0 +1,59 @@
+package org.keycloak.testsuite.auth.page.account;
+
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElementNotPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AccountFields extends Form {
+
+ @FindBy(id = "username")
+ private WebElement usernameInput;
+ @FindBy(id = "email")
+ private WebElement emailInput;
+ @FindBy(id = "firstName")
+ private WebElement firstNameInput;
+ @FindBy(id = "lastName")
+ private WebElement lastNameInput;
+
+ public void setUsername(String username) {
+ Form.setInputValue(usernameInput, username);
+ }
+
+ public AccountFields setEmail(String email) {
+ Form.setInputValue(emailInput, email);
+ return this;
+ }
+
+ public AccountFields setFirstName(String firstName) {
+ Form.setInputValue(firstNameInput, firstName);
+ return this;
+ }
+
+ public AccountFields setLastName(String lastName) {
+ Form.setInputValue(lastNameInput, lastName);
+ return this;
+ }
+
+ public void setValues(UserRepresentation user) {
+ setUsername(user.getUsername());
+ setEmail(user.getEmail());
+ setFirstName(user.getFirstName());
+ setLastName(user.getLastName());
+ }
+
+ public void waitForUsernameInputPresent() {
+ waitAjaxForElement(usernameInput);
+ }
+
+ public void waitForUsernameInputNotPresent() {
+ waitAjaxForElementNotPresent(usernameInput);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java
new file mode 100644
index 0000000..6d1a96c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/account/ContactInfoFields.java
@@ -0,0 +1,24 @@
+package org.keycloak.testsuite.auth.page.account;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ContactInfoFields extends Form {
+
+ @FindBy(id = "user.attributes.street")
+ private WebElement streetInput;
+ @FindBy(id = "user.attributes.locality")
+ private WebElement localityInput;
+ @FindBy(id = "user.attributes.region")
+ private WebElement regionInput;
+ @FindBy(id = "user.attributes.postal_code")
+ private WebElement postalCodeInput;
+ @FindBy(id = "user.attributes.country")
+ private WebElement counryInput;
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java
new file mode 100644
index 0000000..e8a435c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthRealm.java
@@ -0,0 +1,58 @@
+package org.keycloak.testsuite.auth.page;
+
+import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
+import java.net.URI;
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+
+/**
+ * Keycloak realm.
+ *
+ * URL: http://localhost:${auth.server.http.port}/auth/realms/{authRealm}
+ *
+ * @author tkyjovsk
+ */
+public class AuthRealm extends AuthServer implements PageWithLoginUrl {
+
+ public static final String AUTH_REALM = "authRealm";
+
+ public static final String MASTER = "master";
+ public static final String TEST = "test";
+ public static final String DEMO = "demo";
+ public static final String EXAMPLE = "example";
+
+ public static final String ADMIN = "admin";
+
+ public AuthRealm() {
+ setUriParameter(AUTH_REALM, MASTER);
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("realms/{" + AUTH_REALM + "}");
+ }
+
+ public void setAuthRealm(String authRealm) {
+ setUriParameter(AUTH_REALM, authRealm);
+ }
+
+ public void setAuthRealm(AuthRealm authRealm) {
+ setUriParameter(AUTH_REALM, authRealm.getAuthRealm());
+ }
+
+ public String getAuthRealm() {
+ return (String) getUriParameter(AUTH_REALM);
+ }
+
+ /**
+ *
+ * @return OIDC Login URL for authRealm
+ */
+ @Override
+ public URI getOIDCLoginUrl() {
+ return OIDCLoginProtocolService.authUrl(UriBuilder.fromPath(getAuthRoot()))
+ .build(getAuthRealm());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServer.java
new file mode 100644
index 0000000..373c7bb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServer.java
@@ -0,0 +1,34 @@
+package org.keycloak.testsuite.auth.page;
+
+import java.net.URI;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.admin.client.Keycloak;
+
+/**
+ * Context path of Keycloak auth server.
+ *
+ * URL: http://localhost:${auth.server.http.port}/auth
+ *
+ * @author tkyjovsk
+ */
+public class AuthServer extends AuthServerContextRoot {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("auth");
+ }
+
+ public String getAuthRoot() {
+ URI uri = buildUri();
+ return uri.getScheme() + "://" + uri.getAuthority() + "/auth";
+ }
+
+ @ArquillianResource
+ protected Keycloak keycloak;
+
+ public Keycloak keycloak() {
+ return keycloak;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServerContextRoot.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServerContextRoot.java
new file mode 100644
index 0000000..6baf3a4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/AuthServerContextRoot.java
@@ -0,0 +1,26 @@
+package org.keycloak.testsuite.auth.page;
+
+import java.net.URL;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.keycloak.testsuite.arquillian.annotation.AuthServerContext;
+import org.keycloak.testsuite.page.AbstractPageWithInjectedUrl;
+
+/**
+ * Context root of the tested Keycloak server.
+ *
+ * URL: http://localhost:${auth.server.http.port}
+ *
+ * @author tkyjovsk
+ */
+public class AuthServerContextRoot extends AbstractPageWithInjectedUrl {
+
+ @ArquillianResource
+ @AuthServerContext
+ private URL authServerContextRoot;
+
+ @Override
+ public URL getInjectedUrl() {
+ return authServerContextRoot;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java
new file mode 100644
index 0000000..97dc1da
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Authenticate.java
@@ -0,0 +1,24 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class Authenticate extends LoginActions {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("authenticate");
+ }
+
+ @Page
+ private LoginForm login;
+
+ public LoginForm loginForm() {
+ return login;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java
new file mode 100644
index 0000000..e06c5b9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Login.java
@@ -0,0 +1,72 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementNotPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public abstract class Login extends AuthRealm {
+
+ public static final String PROTOCOL = "protocol";
+ public static final String OIDC = "openid-connect";
+ public static final String SAML = "saml";
+
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("protocol/{" + PROTOCOL + "}/auth");
+ }
+
+ public void setProtocol(String protocol) {
+ setUriParameter(PROTOCOL, protocol);
+ }
+
+ public String getProtocol() {
+ return getUriParameter(PROTOCOL).toString();
+ }
+
+ @Page
+ private LoginForm form;
+
+ public LoginForm form() {
+ return form;
+ }
+
+ @FindBy(css = "link[href*='login/keycloak/css/login.css']")
+ private WebElement keycloakTheme;
+
+ public void waitForKeycloakThemeNotPresent() {
+ waitGuiForElementNotPresent(keycloakTheme);
+ }
+
+ public void waitForKeycloakThemePresent() {
+ waitGuiForElement(keycloakTheme);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java
new file mode 100644
index 0000000..3c3da23
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginActions.java
@@ -0,0 +1,68 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class LoginActions extends AuthRealm {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("login-actions");
+ }
+
+ @FindBy(css = "input[type='submit']")
+ private WebElement submitButton;
+
+ @FindBy(css = "div[id='kc-form-options'] span a")
+ private WebElement backToLoginForm;
+
+ @FindBy(css = "span.kc-feedback-text")
+ private WebElement feedbackText;
+
+ @FindBy(xpath = "//div[@id='kc-error-message']/p")
+ private WebElement error;
+
+ public String getErrorMessage() {
+ waitGuiForElementPresent(error, "Error message should be visible");
+ return error.getText();
+ }
+
+ public String getFeedbackText() {
+ waitGuiForElementPresent(feedbackText, "Feedback message should be visible");
+ return feedbackText.getText();
+ }
+
+ public void backToLoginPage() {
+ backToLoginForm.click();
+ }
+
+ public void submit() {
+ submitButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java
new file mode 100644
index 0000000..8329abc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/LoginForm.java
@@ -0,0 +1,125 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.login;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import static org.keycloak.testsuite.util.WaitUtils.*;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class LoginForm extends Form {
+
+ @Page
+ private AccountFields accountFields;
+ @Page
+ private PasswordFields passwordFields;
+
+ @FindBy(name = "login")
+ private WebElement loginButton;
+// @FindBy(name = "cancel")
+// private WebElement cancelButton;
+
+ @FindBy(linkText = "Register")
+ private WebElement registerLink;
+ @FindBy(linkText = "Forgot Password?")
+ private WebElement forgottenPassword;
+ @FindBy(id = "rememberMe")
+ private WebElement rememberMe;
+
+ public void setUsername(String username) {
+ accountFields.setUsername(username);
+ }
+
+ public void setPassword(String password) {
+ passwordFields.setPassword(password);
+ }
+
+ public void login(UserRepresentation user) {
+ login(user.getUsername(), getPasswordOf(user));
+ }
+
+ public void login(String username, String password) {
+ setUsername(username);
+ setPassword(password);
+ login();
+ }
+
+ public void register() {
+ waitForUsernameInputPresent();
+ waitAjaxForElement(registerLink);
+ registerLink.click();
+ }
+
+ public void login() {
+ waitAjaxForElement(loginButton);
+ loginButton.click();
+ }
+
+ public void forgotPassword() {
+ waitAjaxForElement(forgottenPassword);
+ forgottenPassword.click();
+ }
+
+ public void rememberMe(boolean value) {
+ waitForRememberMePresent();
+ boolean selected = rememberMe.isSelected();
+ if ((value && !selected) || !value && selected) {
+ rememberMe.click();
+ }
+ }
+
+// @Override
+// public void cancel() {
+// waitAjaxForElement(cancelButton);
+// cancelButton.click();
+// }
+
+ public void waitForUsernameInputPresent() {
+ accountFields.waitForUsernameInputPresent();
+ }
+
+ public void waitForRegisterLinkNotPresent() {
+ waitAjaxForElementNotPresent(registerLink);
+ }
+
+ public void waitForResetPasswordLinkNotPresent() {
+ waitAjaxForElementNotPresent(forgottenPassword);
+ }
+
+ public void waitForRememberMePresent() {
+ waitAjaxForElement(rememberMe);
+ }
+
+ public void waitForRememberMeNotPresent() {
+ waitAjaxForElementNotPresent(rememberMe);
+ }
+
+ public void waitForLoginButtonPresent() {
+ waitGuiForElement(loginButton);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java
new file mode 100644
index 0000000..d002da8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/OIDCLogin.java
@@ -0,0 +1,13 @@
+package org.keycloak.testsuite.auth.page.login;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class OIDCLogin extends Login {
+
+ public OIDCLogin() {
+ setProtocol(OIDC);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java
new file mode 100644
index 0000000..53bef17
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/PageWithLoginUrl.java
@@ -0,0 +1,15 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import java.net.URI;
+import org.openqa.selenium.WebDriver;
+
+/**
+ * Used by util class LoginAssert. Implementing classes: AuthRealm, AdminConsole.
+ * @author tkyjovsk
+ */
+public interface PageWithLoginUrl {
+
+ WebDriver getDriver();
+ URI getOIDCLoginUrl();
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java
new file mode 100644
index 0000000..c9f901d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/Registration.java
@@ -0,0 +1,78 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.login;
+
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import org.keycloak.testsuite.auth.page.account.ContactInfoFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+
+/**
+ *
+ * @author Filip Kiss
+ */
+public class Registration extends LoginActions {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder()
+ .path("registration");
+ }
+
+ @Page
+ private AccountFields accountFields;
+
+ @Page
+ private PasswordFields passwordFields;
+
+ @Page
+ private ContactInfoFields contactInfoFields;
+
+ public void register(UserRepresentation user) {
+ setValues(user);
+ submit();
+ }
+
+ public void setValues(UserRepresentation user) {
+ setValues(user, getPasswordOf(user));
+ }
+
+ public void setValues(UserRepresentation user, String confirmPassword) {
+ accountFields.setValues(user);
+ passwordFields.setPassword(getPasswordOf(user));
+ passwordFields.setConfirmPassword(confirmPassword);
+ }
+
+ public void waitForUsernameInputPresent() {
+ accountFields.waitForUsernameInputPresent();
+ }
+
+ public void waitForUsernameInputNotPresent() {
+ accountFields.waitForUsernameInputNotPresent();
+ }
+
+ public void waitForConfirmPasswordInputPresent() {
+ passwordFields.waitForConfirmPasswordInputPresent();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java
new file mode 100644
index 0000000..3b4c4e8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/ResetCredentials.java
@@ -0,0 +1,63 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.auth.page.login;
+
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElementPresent;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class ResetCredentials extends LoginActions {
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("reset-credentials");
+ }
+
+ @Page
+ private AccountFields accountFields;
+ @Page
+ private PasswordFields passwordFields;
+
+ @FindBy(id = "kc-info")
+ private WebElement info;
+
+ public void resetCredentials(String value) {
+ accountFields.setUsername(value);
+ submit();
+ }
+
+ public void updatePassword(String password) {
+ passwordFields.setNewPassword(password);
+ passwordFields.setConfirmPassword(password);
+ submit();
+ }
+
+ public String getInfoMessage() {
+ waitGuiForElementPresent(info, "Info message should be visible");
+ return info.getText();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
new file mode 100644
index 0000000..b1e5c25
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdateAccount.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import org.keycloak.testsuite.auth.page.account.AccountFields;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.representations.idm.UserRepresentation;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UpdateAccount extends Authenticate {
+
+ @Page
+ private AccountFields accountFields;
+
+ public void updateAccount(UserRepresentation user) {
+ updateAccount(user.getEmail(), user.getFirstName(), user.getLastName());
+ }
+
+ public void updateAccount(String email, String firstName, String lastName) {
+ accountFields.setEmail(email);
+ accountFields.setFirstName(firstName);
+ accountFields.setLastName(lastName);
+ submit();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java
new file mode 100644
index 0000000..b0cc4c3
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/UpdatePassword.java
@@ -0,0 +1,21 @@
+package org.keycloak.testsuite.auth.page.login;
+
+import org.keycloak.testsuite.auth.page.account.PasswordFields;
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UpdatePassword extends Authenticate {
+
+ @Page
+ private PasswordFields passwordFields;
+
+ public void updatePasswords(String newPassword, String confirmPassword) {
+ passwordFields.setNewPassword(newPassword);
+ passwordFields.setConfirmPassword(confirmPassword);
+ submit();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
new file mode 100644
index 0000000..575ce7b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/auth/page/login/VerifyEmail.java
@@ -0,0 +1,9 @@
+package org.keycloak.testsuite.auth.page.login;
+
+/**
+ *
+ * @author vramik
+ */
+public class VerifyEmail extends Authenticate {
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
new file mode 100644
index 0000000..47bff5d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsole.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.page;
+
+import java.net.URI;
+import org.keycloak.testsuite.auth.page.AuthServer;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
+import org.keycloak.testsuite.console.page.fragment.Menu;
+import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class AdminConsole extends AuthServer implements PageWithLoginUrl {
+
+ public static final String ADMIN_REALM = "adminRealm";
+
+ public AdminConsole() {
+ setUriParameter(ADMIN_REALM, MASTER);
+ }
+
+ public AdminConsole setAdminRealm(String adminRealm) {
+ setUriParameter(ADMIN_REALM, adminRealm);
+ return this;
+ }
+
+ public String getAdminRealm() {
+ return getUriParameter(ADMIN_REALM).toString();
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("admin/{" + ADMIN_REALM + "}/console");
+ }
+
+ @Page
+ private Menu menu;
+
+ @FindBy(xpath = "//div[@class='modal-dialog']")
+ protected ModalDialog modalDialog;
+
+ /**
+ *
+ * @return OIDC Login URL for adminRealm parameter
+ */
+ @Override
+ public URI getOIDCLoginUrl() {
+ return OIDCLoginProtocolService.authUrl(UriBuilder.fromPath(getAuthRoot()))
+ .build(getAdminRealm());
+ }
+
+ @FindBy(css = ".btn-danger")
+ protected WebElement dangerButton;
+
+ //@FindByJQuery(".btn-primary:visible")
+ @FindBy(css = ".btn-primary")
+ protected WebElement primaryButton;
+
+ @FindBy(css = "navbar-brand")
+ protected WebElement brandLink;
+
+ public void logOut() {
+ menu.logOut();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java
new file mode 100644
index 0000000..35be14d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleCreate.java
@@ -0,0 +1,47 @@
+package org.keycloak.testsuite.console.page;
+
+import javax.ws.rs.core.UriBuilder;
+import static org.keycloak.testsuite.console.page.AdminConsoleRealm.CONSOLE_REALM;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleCreate extends AdminConsole {
+
+ public static final String ENTITY = "entity";
+
+ public AdminConsoleCreate() {
+ setUriParameter(CONSOLE_REALM, TEST);
+ }
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("/");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return "/create/{" + ENTITY + "}/{" + CONSOLE_REALM + "}";
+ }
+
+ public AdminConsoleCreate setEntity(String entity) {
+ setUriParameter(ENTITY, entity);
+ return this;
+ }
+
+ public String getEntity() {
+ return getUriParameter(ENTITY).toString();
+ }
+
+ public AdminConsoleCreate setConsoleRealm(String consoleRealm) {
+ setUriParameter(CONSOLE_REALM, consoleRealm);
+ return this;
+ }
+
+ public String getConsoleRealm() {
+ return getUriParameter(CONSOLE_REALM).toString();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java
new file mode 100644
index 0000000..4fd57ba
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealm.java
@@ -0,0 +1,121 @@
+package org.keycloak.testsuite.console.page;
+
+import org.keycloak.admin.client.resource.RealmResource;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+
+import org.keycloak.testsuite.util.WaitUtils;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleRealm extends AdminConsoleRealmsRoot {
+
+ public static final String CONSOLE_REALM = "consoleRealm";
+
+ public AdminConsoleRealm() {
+ setUriParameter(CONSOLE_REALM, TEST);
+ }
+
+ public AdminConsoleRealm setConsoleRealm(String realm) {
+ setUriParameter(CONSOLE_REALM, realm);
+ return this;
+ }
+
+ public String getConsoleRealm() {
+ return getUriParameter(CONSOLE_REALM).toString();
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + CONSOLE_REALM + "}";
+ }
+
+ @FindBy(xpath = "//div[./h2[text()='Configure']]")
+ private ConfigureMenu configureMenu;
+
+ public ConfigureMenu configure() {
+ waitGuiForElement(By.xpath("//div[./h2[text()='Configure']]"));
+ return configureMenu;
+ }
+
+ public RealmResource realmResource() {
+ return realmsResource().realm(getConsoleRealm());
+ }
+
+ public class ConfigureMenu {
+
+ @FindBy(partialLinkText = "Realm Settings")
+ private WebElement realmSettingsLink;
+ @FindBy(partialLinkText = "Clients")
+ private WebElement clientsLink;
+ @FindBy(partialLinkText = "Roles")
+ private WebElement rolesLink;
+ @FindBy(partialLinkText = "Identity Providers")
+ private WebElement identityProvidersLink;
+ @FindBy(partialLinkText = "User Feferation")
+ private WebElement userFederationLink;
+ @FindBy(partialLinkText = "Authentication")
+ private WebElement authenticationLink;
+
+ public void realmSettings() {
+ realmSettingsLink.click();
+ }
+
+ public void clients() {
+ clientsLink.click();
+ }
+
+ public void roles() {
+ rolesLink.click();
+ }
+
+ public void identityProviders() {
+ identityProvidersLink.click();
+ }
+
+ public void userFederation() {
+ userFederationLink.click();
+ }
+
+ public void authentication() {
+ authenticationLink.click();
+ }
+
+ }
+
+ @FindBy(xpath = "//div[./h2[text()='Manage']]")
+ protected ManageMenu manageMenu;
+
+ public ManageMenu manage() {
+ WaitUtils.waitGuiForElement(By.xpath("//div[./h2[text()='Manage']]"));
+ return manageMenu;
+ }
+
+ public class ManageMenu {
+
+ @FindBy(partialLinkText = "Users")
+ private WebElement usersLink;
+ @FindBy(partialLinkText = "Sessions")
+ private WebElement sessionsLink;
+ @FindBy(partialLinkText = "Events")
+ private WebElement eventsLink;
+
+ public void users() {
+ usersLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ public void events() {
+ eventsLink.click();
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java
new file mode 100644
index 0000000..bb001ea
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/AdminConsoleRealmsRoot.java
@@ -0,0 +1,49 @@
+package org.keycloak.testsuite.console.page;
+
+import java.util.List;
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.admin.client.resource.RealmsResource;
+import org.keycloak.testsuite.console.page.fragment.RealmSelector;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AdminConsoleRealmsRoot extends AdminConsole {
+
+ @FindBy(xpath = "//tr[@data-ng-repeat='r in realms']//a[contains(@class,'ng-binding')]")
+ private List<WebElement> realmLinks;
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ return super.createUriBuilder().path("/");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return "/realms";
+ }
+
+ public void clickRealm(String realm) {
+ boolean linkFound = false;
+ for (WebElement realmLink : realmLinks) {
+ if (realmLink.getText().equals(realm)) {
+ linkFound = true;
+ realmLink.click();
+ }
+ }
+ if (!linkFound) {
+ throw new IllegalStateException("A link for realm '" + realm + "' not found on the Realms page.");
+ }
+ }
+
+ @FindBy(css = "realm-selector")
+ protected RealmSelector realmSelector;
+
+ public RealmsResource realmsResource() {
+ return keycloak.realms();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java
new file mode 100644
index 0000000..5cd74bf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Authentication.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Authentication extends AdminConsoleRealm {
+
+ @FindBy(xpath = "//h1[text()='Authentication']/..")
+ private AuthenticationTabs authenticationTabs;
+
+ public AuthenticationTabs tabs() {
+ return authenticationTabs;
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/authentication";
+ }
+
+ public class AuthenticationTabs {
+ @FindBy(linkText = "Flows")
+ private WebElement flowsTab;
+ @FindBy(linkText = "Required Actions")
+ private WebElement requiredActionsTab;
+ @FindBy(linkText = "Password Policy")
+ private WebElement passwordPolicyTab;
+ @FindBy(linkText = "Bindings")
+ private WebElement binding;
+ @FindBy(linkText = "OTP Policy")
+ private WebElement otpPolicy;
+
+ public void flows() {
+ flowsTab.click();
+ }
+
+ public void requiredActions() {
+ requiredActionsTab.click();
+ }
+
+ public void passwordPolicy() {
+ passwordPolicyTab.click();
+ }
+
+ public void binding() {
+ binding.click();
+ }
+
+ public void otpPolicy() {
+ otpPolicy.click();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java
new file mode 100644
index 0000000..6e8d4a4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Bindings.java
@@ -0,0 +1,147 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * Created by mhajas on 8/21/15.
+ */
+public class Bindings extends Authentication{
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/flow-binding";
+ }
+
+ @FindBy(id = "browser")
+ private Select BrowserFlowSelect;
+
+ public void changeBrowserFlowSelect(BrowserFlowSelectValues value) {
+ BrowserFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "registration")
+ private Select RegistrationFlowSelect;
+
+ public void changeRegistrationFlowSelect(RegistrationFlowSelectValues value) {
+ RegistrationFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "grant")
+ private Select DirectGrantFlowSelect;
+
+ public void changeDirectGrantFlowSelect(DirectGrantFlowSelectValues value) {
+ DirectGrantFlowSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "resetCredentials")
+ private Select ResetCredentialsSelect;
+
+ public void changeResetCredentialsSelect(ResetCredentialsSelectValues value) {
+ ResetCredentialsSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "clientAuthentication")
+ private Select ClientAuthenticationSelect;
+
+ public void changeClientAuthenticationSelect(ClientAuthenticationSelectValues value) {
+ ClientAuthenticationSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(xpath = "//button[text()='Save']")
+ private WebElement saveButton;
+
+ public void clickSave() {
+ saveButton.click();
+ }
+
+ @FindBy(xpath = "//button[text()='Cancel']")
+ private WebElement cancelButton;
+
+ public void clickCancel() {
+ cancelButton.click();
+ }
+
+ public enum BrowserFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private BrowserFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum RegistrationFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private RegistrationFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum DirectGrantFlowSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials");
+
+ private String name;
+
+ private DirectGrantFlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum ResetCredentialsSelectValues {
+
+ DIRECT_GRANT("direct grant"), REGISTRATION("registration"), BROWSER("browser"),
+ RESET_CREDENTIALS("reset credentials"), NOTHING("");
+
+ private String name;
+
+ private ResetCredentialsSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum ClientAuthenticationSelectValues {
+
+ CLIENTS("clients");
+
+ private String name;
+
+ private ClientAuthenticationSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java
new file mode 100644
index 0000000..1994064
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/Flows.java
@@ -0,0 +1,212 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Flows extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/flows";
+ }
+
+ @FindBy(tagName = "select")
+ private Select flowSelect;
+
+ public void changeFlowSelect(FlowSelectValues value) {
+ flowSelect.selectByVisibleText(value.getName());
+ }
+
+ public enum FlowSelectValues {
+
+ DIRECT_GRANT("Direct grant"), REGISTRATION("Registration"), BROWSER("Browser"),
+ RESET_CREDENTIALS("Reset credentials"), CLIENTS("Clients");
+
+ private String name;
+
+ private FlowSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ @FindBy(linkText = "New")
+ private WebElement newButton;
+
+ @FindBy(linkText = "Copy")
+ private WebElement copyButton;
+
+ public void clickNew() {
+ newButton.click();
+ }
+
+ public void clickCopy() {
+ copyButton.click();
+ }
+
+ // Direct grant
+ public void setPasswordRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setOTPOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'O T P')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Registration
+ public void setRegistrationFormRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration form')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationFormDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration form')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationUserCreationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration User Creation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRegistrationUserCreationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Registration User Creation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setProfileValidationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Profile Validation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setProfileValidationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Profile Validation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordValidationRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password Validation')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setPasswordValidationDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Password Validation')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setRecaptchaRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Recaptcha')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setRecaptchaDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Recaptcha')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Browser
+ public void setCookieAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Cookie')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setCookieDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Cookie')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setKerberosDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Kerberos')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ public void setFormsAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setFormsRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setFormsDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Forms')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setOTPFormDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' O T P Form')]]/../td[5]//input[@type='radio']")).click();
+ }
+
+ // Reset credentials
+ public void setResetPasswordRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setResetPasswordOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setResetPasswordDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset Password')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPOptional() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setResetOTPDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Reset O T P')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ // Clients
+ public void setClientIdAndSecretRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setClientIdAndSecretAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setClientIdAndSecretDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,'Client Id and Secret')]]/../td[4]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtRequired() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[2]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtAlternative() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[3]//input[@type='radio']")).click();
+ }
+
+ public void setSignedJwtDisabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()[contains(.,' Signed Jwt')]]/../td[4]//input[@type='radio']")).click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java
new file mode 100644
index 0000000..34e8a09
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/OTPPolicy.java
@@ -0,0 +1,85 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * Created by mhajas on 8/21/15.
+ */
+public class OTPPolicy extends Authentication {
+
+ @FindBy(linkText = "Save")
+ private WebElement saveButton;
+
+ public void clickSave() {
+ saveButton.click();
+ }
+
+ @FindBy(linkText = "Cancel")
+ private WebElement cancelButton;
+
+ public void clickCancel() {
+ cancelButton.click();
+ }
+
+ @FindBy(id = "lookAhead")
+ private WebElement lookAheadInput;
+
+ public void setLookAheadInputValue(String value) {
+ Form.setInputValue(lookAheadInput, value);
+ }
+
+ @FindBy(id = "counter")
+ private WebElement initialCounterInput;
+
+ public void setInitialcounterInputValue(String value) {
+ Form.setInputValue(initialCounterInput, value);
+ }
+
+ public enum OTPTypeSelectValues {
+
+ TIME_BASED("time Based"), COUNTER_BASED("Counter Based");
+
+ private String name;
+
+ private OTPTypeSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum OTPHashAlgorithmSelectValues {
+
+ SHA1("SHA1"), SHA256("SHA256"), SHA512("SHA512");
+
+ private String name;
+
+ private OTPHashAlgorithmSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public enum NumberOfDigitsSelectValues {
+
+ NUMBER6("6"), NUMBER8("8");
+
+ private String name;
+
+ private NumberOfDigitsSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
new file mode 100644
index 0000000..b2b08b5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/PasswordPolicy.java
@@ -0,0 +1,100 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import java.util.List;
+
+import org.jboss.arquillian.graphene.findby.ByJQuery;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author Petr Mensik
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class PasswordPolicy extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/password-policy";
+ }
+
+ @FindBy(tagName = "select")
+ private Select addPolicySelect;
+
+ @FindBy(tagName = "select")
+ private WebElement addPolicySelectElement;
+
+ @FindBy(css = "tr.ng-scope")
+ private List<WebElement> allRows;
+
+ public void addPolicy(PasswordPolicy.Type policy, String value) {
+ waitGuiForElement(addPolicySelectElement);
+ addPolicySelect.selectByVisibleText(policy.getName());
+ setPolicyValue(policy, value);
+ primaryButton.click();
+ }
+
+
+ public void addPolicy(PasswordPolicy.Type policy, int value) {
+ addPolicy(policy, String.valueOf(value));
+ }
+
+ public void addPolicy(PasswordPolicy.Type policy) {
+ addPolicySelect.selectByVisibleText(policy.getName());
+ primaryButton.click();
+ }
+
+ public void removePolicy(PasswordPolicy.Type policy) {
+ int policyInputLocation = findPolicy(policy);
+ allRows.get(policyInputLocation).findElements(By.tagName("button")).get(0).click();
+ primaryButton.click();
+ }
+
+ public void editPolicy(PasswordPolicy.Type policy, int value) {
+ editPolicy(policy, String.valueOf(value));
+ }
+
+ public void editPolicy(PasswordPolicy.Type policy, String value) {
+ setPolicyValue(policy, value);
+ primaryButton.click();
+ }
+
+ private void setPolicyValue(PasswordPolicy.Type policy, String value) {
+ int policyInputLocation = findPolicy(policy);
+ WebElement input = allRows.get(policyInputLocation).findElement(By.tagName("input"));
+ input.clear();
+ input.sendKeys(value);
+ }
+
+ private int findPolicy(PasswordPolicy.Type policy) {
+ for (int i = 0; i < allRows.size(); i++) {
+ String policyName = allRows.get(i).findElement(ByJQuery.selector("td:eq(0)")).getText();
+ if (policyName.equals(policy.getName())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ public enum Type {
+
+ HASH_ITERATIONS("Hash Iterations"), LENGTH("Length"), DIGITS("Digits"), LOWER_CASE("Lower Case"),
+ UPPER_CASE("Upper Case"), SPECIAL_CHARS("Special Chars"), NOT_USERNAME("Not Username"),
+ REGEX_PATTERN("Regex Pattern"), PASSWORD_HISTORY("Password History"),
+ FORCE_EXPIRED_PASSWORD_CHANGE("Force Expired Password Change");
+
+ private String name;
+
+ private Type(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java
new file mode 100644
index 0000000..5da1cda
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/authentication/RequiredActions.java
@@ -0,0 +1,55 @@
+package org.keycloak.testsuite.console.page.authentication;
+
+import org.openqa.selenium.By;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class RequiredActions extends Authentication {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/required-actions";
+ }
+
+ public void clickTermsAndConditionEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Terms and Conditions']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickTermsAndConditionDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Terms and Conditions']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickVerifyEmailEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Verify Email']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickVerifyEmailDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Verify Email']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickUpdatePasswordEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Password']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickUpdatePasswordDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Password']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickConfigureTotpEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Configure Totp']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickConfigureTotpDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Configure Totp']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+
+ public void clickUpdateProfileEnabled() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Profile']/..//input[@type='checkbox' and @ng-model='requiredAction.enabled']")).click();
+ }
+
+ public void clickUpdateProfileDefaultAction() {
+ driver.findElement(By.xpath("//td[@class='ng-binding' and text()='Update Profile']/..//input[@type='checkbox' and @ng-model='requiredAction.defaultAction']")).click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
new file mode 100644
index 0000000..783cd0b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Client.java
@@ -0,0 +1,107 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.testsuite.console.page.fragment.Breadcrumb;
+import static org.keycloak.testsuite.console.page.fragment.Breadcrumb.BREADCRUMB_XPATH;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Client extends Clients {
+
+ public static final String ID = "id"; // TODO client.id vs client.clientId
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ID + "}";
+ }
+
+ public final void setId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getId() {
+ return getUriParameter(ID).toString();
+ }
+
+ @FindBy(xpath = BREADCRUMB_XPATH)
+ private Breadcrumb breadcrumb;
+
+ public Breadcrumb breadcrumb() {
+ return breadcrumb;
+ }
+
+ public void backToClientsViaBreadcrumb() {
+ breadcrumb.clickItemOneLevelUp();
+ }
+
+ @FindBy(id = "removeClient")
+ private WebElement deleteIcon;
+
+ public void delete() {
+ deleteIcon.click();
+ modalDialog.confirmDeletion();
+ }
+
+ @FindBy(xpath = "//div[@data-ng-controller='ClientTabCtrl']/ul")
+ protected ClientTabs clientTabs;
+
+ public ClientTabs tabs() {
+ return clientTabs;
+ }
+
+ public class ClientTabs {
+
+ @FindBy(linkText = "Settings")
+ private WebElement settingsLink;
+ @FindBy(linkText = "Roles")
+ private WebElement rolesLink;
+ @FindBy(linkText = "Mappers")
+ private WebElement mappersLink;
+ @FindBy(linkText = "Scope")
+ private WebElement scopeLink;
+ @FindBy(linkText = "Revocation")
+ private WebElement revocationLink;
+ @FindBy(linkText = "Sessions")
+ private WebElement sessionsLink;
+ @FindBy(linkText = "Installation")
+ private WebElement installationLink;
+
+ public void settings() {
+ settingsLink.click();
+ }
+
+ public void roles() {
+ rolesLink.click();
+ }
+
+ public void mappers() {
+ mappersLink.click();
+ }
+
+ public void scope() {
+ scopeLink.click();
+ }
+
+ public void revocation() {
+ revocationLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ public void installation() {
+ installationLink.click();
+ }
+
+ }
+
+ public ClientResource clientResource() {
+ return clientsResource().get(getId());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java
new file mode 100644
index 0000000..cdc8d6b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientClustering.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientClustering extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clustering";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java
new file mode 100644
index 0000000..08a3523
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientCredentials.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientCredentials extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/credentials";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java
new file mode 100644
index 0000000..65c1c6c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientInstallation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientInstallation extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/installation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java
new file mode 100644
index 0000000..d56dca5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientMappers.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientMappers extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/mappers";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java
new file mode 100644
index 0000000..fb161a8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRevocation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRevocation extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/revocation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java
new file mode 100644
index 0000000..d0a0b0d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRole.java
@@ -0,0 +1,36 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.testsuite.console.page.roles.*;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRole extends ClientRoles {
+
+ public static final String ROLE_ID = "roleId";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ROLE_ID + "}";
+ }
+
+ public void setRoleId(String id) {
+ setUriParameter(ROLE_ID, id);
+ }
+
+ public String getRoleId() {
+ return getUriParameter(ROLE_ID).toString();
+ }
+
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+ public void backToClientRolesViaBreadcrumb() {
+ breadcrumb().clickItemOneLevelUp();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java
new file mode 100644
index 0000000..a8ee969
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientRoles.java
@@ -0,0 +1,29 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.testsuite.console.page.roles.RolesTable;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientRoles extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/roles";
+ }
+
+ @FindBy(css = "table[class*='table']")
+ private RolesTable table;
+
+ public RolesTable roles() {
+ return table;
+ }
+
+ public RolesResource rolesResource() {
+ return clientResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
new file mode 100644
index 0000000..6f9cccd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/Clients.java
@@ -0,0 +1,138 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.page.clients;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.admin.client.resource.ClientsResource;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+
+import static org.openqa.selenium.By.linkText;
+import static org.openqa.selenium.By.tagName;
+
+/**
+ *
+ * @author Filip Kisss
+ */
+public class Clients extends AdminConsoleRealm {
+
+ public static final String CREATE = "Create";
+ public static final String IMPORT = "Import";
+
+ public static final String EDIT = "Edit";
+ public static final String DELETE = "Delete";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clients";
+ }
+
+ @FindBy(tagName = "table")
+ private ClientsTable clientsTable;
+
+ public ClientsTable table() {
+ return clientsTable;
+ }
+
+ public class ClientsTable extends DataTable {
+
+ public List<ClientRepresentation> searchClients(String searchPattern) {
+ search(searchPattern);
+ return getClientsFromRows();
+ }
+
+ public void createClient() {
+ waitAjaxForBody();
+ clickHeaderLink(CREATE);
+ }
+
+ public void importClient() {
+ waitAjaxForBody();
+ clickHeaderLink(IMPORT);
+ }
+
+ public void clickClient(ClientRepresentation client) {
+ waitAjaxForBody();
+ clickClient(client.getClientId());
+ }
+
+ public void clickClient(String clientId) {
+ waitAjaxForBody();
+ body().findElement(linkText(clientId)).click();
+ }
+
+ public void editClient(String clientId) {
+ waitAjaxForBody();
+ clickRowActionButton(getRowByLinkText(clientId), EDIT);
+ }
+
+ public void deleteClient(String clientId) {
+ waitAjaxForBody();
+ clickRowActionButton(getRowByLinkText(clientId), DELETE);
+ }
+
+ public ClientRepresentation findClient(String clientId) {
+ List<ClientRepresentation> clients = searchClients(clientId);
+ if (clients.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == clients.size();
+ return clients.get(0);
+ }
+ }
+
+ public List<ClientRepresentation> getClientsFromRows() {
+ List<ClientRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ ClientRepresentation client = getClientFromRow(row);
+ if (client != null) {
+ rows.add(client);
+ }
+ }
+ return rows;
+ }
+
+ public ClientRepresentation getClientFromRow(WebElement row) {
+ ClientRepresentation client = null;
+ if (row.isDisplayed()) {
+ client = new ClientRepresentation();
+ List<WebElement> tds = row.findElements(tagName("td"));
+ client.setClientId(tds.get(0).getText());
+ List<String> redirectUris = new ArrayList<>();
+ redirectUris.add(tds.get(2).getText()); // FIXME there can be more than 1 redirect uri
+ client.setRedirectUris(redirectUris);
+ }
+ return client;
+ }
+ }
+
+ public void deleteClient(String clientId) {
+ clientsTable.searchClients(clientId);
+ clientsTable.deleteClient(clientId);
+ }
+
+ public ClientsResource clientsResource() {
+ return realmResource().clients();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java
new file mode 100644
index 0000000..32107fd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientScopeMappings.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientScopeMappings extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/scope-mappings";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java
new file mode 100644
index 0000000..eae5013
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSessions.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.clients;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSessions extends Client {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java
new file mode 100644
index 0000000..8fe1c2c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettings.java
@@ -0,0 +1,18 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSettings extends Client {
+
+ @Page
+ private ClientSettingsForm form;
+
+ public ClientSettingsForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
new file mode 100644
index 0000000..41cdf71
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/ClientSettingsForm.java
@@ -0,0 +1,91 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.auth.page.login.Login.OIDC;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ClientSettingsForm extends CreateClientForm {
+
+ @FindBy(id = "baseUrl")
+ private WebElement baseUrlInput;
+ @FindBy(id = "adminUrl")
+ private WebElement adminUrlInput;
+
+ @FindBy(id = "newWebOrigin")
+ private WebElement newWebOriginInput;
+ @FindBy(xpath = ".//input[ng-model='client.webOrigins[i]']")
+ private List<WebElement> webOriginInputs;
+ @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteWebOrigin')]")
+ private List<WebElement> deleteWebOriginIcons;
+
+ public void setBaseUrl(String baseUrl) {
+ setInputValue(baseUrlInput, baseUrl);
+ }
+
+ public String getBaseUrl() {
+ return getInputValue(baseUrlInput);
+ }
+
+ public void setAdminUrl(String adminUrl) {
+ setInputValue(adminUrlInput, adminUrl);
+ }
+
+ public String getAdminUrl() {
+ return getInputValue(adminUrlInput);
+ }
+
+ public void addWebOrigin(String redirectUri) {
+ newWebOriginInput.sendKeys(redirectUri);
+ }
+
+ public List<String> getWebOrigins() {
+ List<String> values = new ArrayList<>();
+ for (WebElement input : webOriginInputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+ public void setWebOrigins(List<String> webOrigins) {
+ while (!deleteWebOriginIcons.isEmpty()) {
+ deleteWebOriginIcons.get(0).click();
+ pause(100);
+ }
+ if (webOrigins != null) {
+ for (String redirectUri : webOrigins) {
+ addWebOrigin(redirectUri);
+ pause(100);
+ }
+ }
+ }
+
+ @Override
+ public void setValues(ClientRepresentation client) {
+ super.setValues(client);
+ setBaseUrl(client.getBaseUrl());
+ if (OIDC.equals(client.getProtocol())) {
+ setAdminUrl(client.getAdminUrl());
+ setWebOrigins(client.getWebOrigins());
+ }
+ }
+
+ @Override
+ public ClientRepresentation getValues() {
+ ClientRepresentation values = super.getValues();
+ values.setBaseUrl(getBaseUrl());
+ if (OIDC.equals(values.getProtocol())) {
+ values.setAdminUrl(getAdminUrl());
+ values.setWebOrigins(getWebOrigins());
+ }
+ return values;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java
new file mode 100644
index 0000000..87568c9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClient.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClient extends AdminConsoleCreate {
+
+ public CreateClient() {
+ setEntity("client");
+ }
+
+ @Page
+ private CreateClientForm form;
+
+ public CreateClientForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
new file mode 100644
index 0000000..87eadb5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientForm.java
@@ -0,0 +1,230 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.auth.page.login.OIDCLogin.OIDC;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.page.Form.getInputValue;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.keycloak.testsuite.util.Timer;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClientForm extends Form {
+
+ @FindBy(id = "clientId")
+ private WebElement clientIdInput;
+
+ @FindBy(id = "name")
+ private WebElement nameInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='enabled']]")
+ private OnOffSwitch enabledSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='consentRequired']]")
+ private OnOffSwitch consentRequiredSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='directGrantsOnly']]")
+ private OnOffSwitch directGrantsOnlySwitch;
+
+ @FindBy(id = "protocol")
+ private Select protocolSelect;
+ @FindBy(id = "protocol")
+ private WebElement protocolSelectElement;
+
+ @FindBy
+ private SAMLClientSettingsForm samlForm;
+
+ public SAMLClientSettingsForm samlForm() {
+ return samlForm;
+ }
+
+ @FindBy(id = "accessType")
+ private Select accessTypeSelect;
+ @FindBy(id = "accessType")
+ private WebElement accessTypeSelectElement;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='serviceAccountsEnabled']]")
+ private OnOffSwitch serviceAccountsEnabledSwitch;
+
+ @FindBy(id = "newRedirectUri")
+ private WebElement newRedirectUriInput;
+ @FindBy(xpath = ".//input[@ng-model='client.redirectUris[i]']")
+ private List<WebElement> redirectUriInputs;
+ @FindBy(xpath = ".//i[contains(@data-ng-click, 'deleteRedirectUri')]")
+ private List<WebElement> deleteRedirectUriIcons;
+
+ public void setValues(ClientRepresentation client) {
+ waitAjaxForElement(clientIdInput);
+
+ setClientId(client.getClientId());
+ setName(client.getName());
+ setEnabled(client.isEnabled());
+ setConsentRequired(client.isConsentRequired());
+ setDirectGrantsOnly(client.isDirectGrantsOnly());
+ setProtocol(client.getProtocol());
+ if (OIDC.equals(client.getProtocol())) {
+ setAccessType(client);
+ if (!client.isBearerOnly()) {
+ if (!client.isPublicClient()) {
+ setServiceAccountsEnabled(client.isServiceAccountsEnabled());
+ }
+ setRedirectUris(client.getRedirectUris());
+ }
+ }
+ }
+
+ public ClientRepresentation getValues() {
+ ClientRepresentation values = new ClientRepresentation();
+ values.setClientId(getClientId());
+ values.setName(getName());
+ values.setEnabled(isEnabled());
+ values.setConsentRequired(isConsentRequired());
+ values.setDirectGrantsOnly(isDirectGrantsOnly());
+ values.setProtocol(getProtocol());
+ if (OIDC.equals(values.getProtocol())) {
+ values.setBearerOnly(isBearerOnly());
+ if (!values.isBearerOnly()) {
+ values.setPublicClient(isPublicClient());
+ if (!values.isPublicClient()) {
+ values.setServiceAccountsEnabled(isServiceAccountsEnabled());
+ }
+ values.setRedirectUris(getRedirectUris());
+ }
+ }
+ return values;
+ }
+
+ public String getClientId() {
+ return getInputValue(clientIdInput);
+ }
+
+ public void setClientId(String clientId) {
+ setInputValue(clientIdInput, clientId);
+ }
+
+ public String getName() {
+ return getInputValue(nameInput);
+ }
+
+ public void setName(String name) {
+ setInputValue(nameInput, name);
+ }
+
+ public boolean isEnabled() {
+ return enabledSwitch.isOn();
+ }
+
+ public void setEnabled(boolean enabled) {
+ enabledSwitch.setOn(enabled);
+ }
+
+ public static final String BEARER_ONLY = "bearer-only";
+ public static final String PUBLIC = "public";
+ public static final String CONFIDENTIAL = "confidential";
+
+ public boolean isBearerOnly() {
+ return BEARER_ONLY.equals(
+ accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
+ }
+
+ public boolean isPublicClient() {
+ return PUBLIC.equals(
+ accessTypeSelect.getFirstSelectedOption().getAttribute(VALUE));
+ }
+
+ public void setBearerOnly(boolean bearerOnly) {
+ accessTypeSelectElement.sendKeys(BEARER_ONLY);
+// accessTypeSelect.selectByVisibleText(BEARER_ONLY);
+ }
+
+ public void setPublicClient(boolean publicClient) {
+ accessTypeSelectElement.sendKeys(PUBLIC);
+// accessTypeSelect.selectByVisibleText(PUBLIC);
+ }
+
+ public void setAccessType(ClientRepresentation client) { // TODO verify
+ setBearerOnly(client.isBearerOnly());
+ setPublicClient(client.isPublicClient());
+ if (!client.isBearerOnly() && !client.isPublicClient()) {
+ accessTypeSelect.selectByVisibleText(CONFIDENTIAL);
+ }
+ }
+
+ public void addRedirectUri(String redirectUri) {
+ newRedirectUriInput.sendKeys(redirectUri);
+ }
+
+ public List<String> getRedirectUris() {
+ List<String> values = new ArrayList<>();
+ for (WebElement input : redirectUriInputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+ public void setRedirectUris(List<String> redirectUris) {
+ Timer.time();
+ while (!deleteRedirectUriIcons.isEmpty()) {
+ deleteRedirectUriIcons.get(0).click();
+ pause(100);
+ }
+ Timer.time("deleteRedirectUris");
+ if (redirectUris != null) {
+ for (String redirectUri : redirectUris) {
+ addRedirectUri(redirectUri);
+ pause(100);
+ }
+ }
+ Timer.time("addRedirectUris");
+ }
+
+ public boolean isConsentRequired() {
+ return consentRequiredSwitch.isOn();
+ }
+
+ public void setConsentRequired(boolean consentRequired) {
+ consentRequiredSwitch.setOn(consentRequired);
+ }
+
+ public boolean isDirectGrantsOnly() {
+ return directGrantsOnlySwitch.isOn();
+ }
+
+ public void setDirectGrantsOnly(boolean directGrantsOnly) {
+ directGrantsOnlySwitch.setOn(directGrantsOnly);
+ }
+
+ public String getProtocol() {
+ waitAjaxForElement(protocolSelect.getFirstSelectedOption());
+ return protocolSelect.getFirstSelectedOption().getText();
+ }
+
+ public void setProtocol(String protocol) {
+ Timer.time();
+ protocolSelectElement.sendKeys(protocol);
+ Timer.time("clientSettings.setProtocol()");
+ }
+
+ public boolean isServiceAccountsEnabled() {
+ return serviceAccountsEnabledSwitch.isOn();
+ }
+
+ public void setServiceAccountsEnabled(boolean serviceAccountsEnabled) {
+ serviceAccountsEnabledSwitch.setOn(serviceAccountsEnabled);
+ }
+
+ public class SAMLClientSettingsForm extends Form {
+
+ // TODO add SAML client attributes
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java
new file mode 100644
index 0000000..8a3317a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/clients/CreateClientRole.java
@@ -0,0 +1,25 @@
+package org.keycloak.testsuite.console.page.clients;
+
+import static org.keycloak.testsuite.console.page.clients.Client.ID;
+import org.keycloak.testsuite.console.page.roles.CreateRole;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateClientRole extends CreateRole {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/clients/{" + ID + "}";
+ }
+
+ public void setClientId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getClientId() {
+ return getUriParameter(ID).toString();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java
new file mode 100644
index 0000000..08e0e00
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/AdminEvents.java
@@ -0,0 +1,104 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class AdminEvents extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/admin-events";
+ }
+
+ @FindBy(tagName = "table")
+ private AdminEventsTable table;
+
+ public AdminEventsTable table() {
+ return table;
+ }
+
+ public class AdminEventsTable extends DataTable {
+
+ public void update() {
+ waitAjaxForBody();
+ clickHeaderButton("Update");
+ }
+
+ public void reset() {
+ waitAjaxForBody();
+ clickHeaderButton("Reset");
+ }
+
+ @FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
+ private WebElement filterButton;
+
+ public void filter() {
+ waitAjaxForBody();
+ filterButton.click();
+ }
+
+ @FindBy(tagName = "form")
+ private AdminEventsTableFilterForm filterForm;
+
+ public AdminEventsTableFilterForm filterForm() {
+ return filterForm;
+ }
+
+ public class AdminEventsTableFilterForm extends Form {
+
+ public void addOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_adminEnabledEventOperations']/ul")).click();
+ driver.findElement(By.xpath("//div[@id='select2-drop']//div[text()[contains(.,'" + type + "')]]/..")).click();
+ }
+
+ public void removeOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_adminEnabledEventOperations']//div[text()='" + type + "']/../a")).click();
+ }
+
+ @FindBy(id = "resource")
+ private WebElement resourcePathInput;
+
+ public void setResourcePathInput(String value) {
+ setInputValue(resourcePathInput, value);
+ }
+
+ @FindBy(id = "realm")
+ private WebElement realmInput;
+
+ public void setRealmInput(String value) {
+ setInputValue(realmInput, value);
+ }
+
+ @FindBy(id = "client")
+ private WebElement clientInput;
+
+ public void setClientInput(String value) {
+ setInputValue(clientInput, value);
+ }
+
+ @FindBy(id = "user")
+ private WebElement userInput;
+
+ public void setUserInput(String value) {
+ setInputValue(userInput, value);
+ }
+
+ @FindBy(id = "ipAddress")
+ private WebElement ipAddressInput;
+
+ public void setIpAddressInput(String value) {
+ setInputValue(ipAddressInput, value);
+ }
+ }
+
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java
new file mode 100644
index 0000000..686e902
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Config.java
@@ -0,0 +1,99 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class Config extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/events-settings";
+ }
+
+ @FindBy(xpath = "//form")
+ private ConfigForm form;
+
+ public ConfigForm form() {
+ return form;
+ }
+
+ public class ConfigForm extends Form {
+ @FindBy(id = "s2id_autogen1")
+ private WebElement eventListenersInput;
+
+ @FindBy(xpath = "//div[@id='s2id_autogen1']/..//select")
+ private Select eventListenersSelect;
+
+ public void addEventListener(String listener) {
+ eventListenersInput.click();
+ eventListenersSelect.selectByVisibleText(listener);
+ }
+
+ public void removeEventListener(String listener) {
+ eventListenersInput.findElement(By.xpath("//div[text()='" + listener + "']/../a")).click();
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='enabled']]")
+ private OnOffSwitch SaveEvents;
+
+ public void setSaveEvents(boolean value) {
+ SaveEvents.setOn(value);
+ }
+
+ @FindBy(xpath = "//div[@id='s2id_enabledEventTypes']//input")
+ private WebElement savedTypesInput;
+
+ @FindBy(xpath = "//div[@id='select2-drop']/ul")
+ private WebElement savedTypesOptions;
+
+ public void addSaveType(String type) {
+ savedTypesInput.click();
+ savedTypesOptions.findElement(By.xpath("//div[text()='" + type + "']")).click();
+ }
+
+ public void removeSaveType(String type) {
+ savedTypesInput.findElement(By.xpath("//div[text()='" + type + "']/../a")).click();
+ }
+
+ public void clearLoginEvents() {
+ driver.findElement(By.xpath("//button[@data-ng-click='clearEvents()']")).click();
+ }
+
+ @FindBy(id = "expiration")
+ private WebElement expirationInput;
+
+ @FindBy(name = "expirationUnit")
+ private Select expirationUnitSelect;
+
+ public void setExpiration(String value, String unit) {
+ expirationUnitSelect.selectByVisibleText(unit);
+ Form.setInputValue(expirationInput, value);
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsEnabled']]")
+ private OnOffSwitch saveAdminEvents;
+
+ public void setSaveAdminEvents(boolean value) {
+ saveAdminEvents.setOn(value);
+ }
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='adminEventsDetailsEnabled']]")
+ private OnOffSwitch includeRepresentation;
+
+ public void setIncludeRepresentation(boolean value) {
+ includeRepresentation.setOn(value);
+ }
+
+ public void clearAdminEvents() {
+ driver.findElement(By.xpath("//button[@data-ng-click='clearAdminEvents()']")).click();
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java
new file mode 100644
index 0000000..53c8345
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/Events.java
@@ -0,0 +1,35 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Events extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment();
+ }
+
+ @FindBy(linkText = "Login Events")
+ private WebElement loginEventsTab;
+ @FindBy(linkText = "Admin Events")
+ private WebElement adminEventsTab;
+ @FindBy(linkText = "Config")
+ private WebElement configTab;
+
+ public void loginEvents() {
+ loginEventsTab.click();
+ }
+ public void adminEvents() {
+ adminEventsTab.click();
+ }
+ public void config() {
+ configTab.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java
new file mode 100644
index 0000000..6752951
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/events/LoginEvents.java
@@ -0,0 +1,80 @@
+package org.keycloak.testsuite.console.page.events;
+
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class LoginEvents extends Events {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/events";
+ }
+
+ @FindBy(tagName = "table")
+ private LoginEventsTable table;
+
+ public LoginEventsTable table() {
+ return table;
+ }
+
+ public class LoginEventsTable extends DataTable {
+
+ public void update() {
+ waitAjaxForBody();
+ clickHeaderButton("Update");
+ }
+
+ public void reset() {
+ waitAjaxForBody();
+ clickHeaderButton("Reset");
+ }
+
+ @FindBy(xpath = "//button[text()[contains(.,'Filter')]]")
+ private WebElement filterButton;
+
+ public void filter() {
+ waitAjaxForBody();
+ filterButton.click();
+ }
+
+ @FindBy(tagName = "form")
+ private LoginEventsTableFilterForm filterForm;
+
+ public LoginEventsTableFilterForm filterForm() {
+ return filterForm;
+ }
+
+ public class LoginEventsTableFilterForm extends Form {
+
+ public void addEventType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_eventTypes']/ul")).click();
+ driver.findElement(By.xpath("//div[@id='select2-drop']//div[text()='" + type + "']/..")).click();
+ }
+
+ public void removeOperationType(String type) {
+ driver.findElement(By.xpath("//div[@id='s2id_eventTypes']//div[text()='" + type + "']/../a")).click();
+ }
+
+ @FindBy(id = "client")
+ private WebElement clientInput;
+
+ public void setClientInput(String value) {
+ setInputValue(clientInput, value);
+ }
+
+ @FindBy(id = "user")
+ private WebElement userInput;
+
+ public void setUserInput(String value) {
+ setInputValue(userInput, value);
+ }
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java
new file mode 100644
index 0000000..4dc47f9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/CreateLdapUserProvider.java
@@ -0,0 +1,20 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateLdapUserProvider extends AdminConsoleCreate {
+
+ public CreateLdapUserProvider() {
+ setEntity("user-federation");
+ }
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/providers/ldap";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
new file mode 100644
index 0000000..a9b8882
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/LdapUserProviderForm.java
@@ -0,0 +1,130 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+
+/**
+ * Created by fkiss.
+ */
+public class LdapUserProviderForm extends Form {
+
+ @FindBy(id = "consoleDisplayName")
+ private WebElement consoleDisplayNameInput;
+
+ @FindBy(id = "priority")
+ private WebElement priorityInput;
+
+ @FindBy(id = "usernameLDAPAttribute")
+ private WebElement usernameLDAPAttributeInput;
+
+ @FindBy(id = "userObjectClasses")
+ private WebElement userObjectClassesInput;
+
+ @FindBy(id = "ldapConnectionUrl")
+ private WebElement ldapConnectionUrlInput;
+
+ @FindBy(id = "ldapBaseDn")
+ private WebElement ldapBaseDnInput;
+
+ @FindBy(id = "ldapUsersDn")
+ private WebElement ldapUserDnInput;
+
+ @FindBy(id = "ldapBindDn")
+ private WebElement ldapBindDnInput;
+
+ @FindBy(id = "ldapBindCredential")
+ private WebElement ldapBindCredentialInput;
+
+ @FindBy(id = "kerberosRealm")
+ private WebElement kerberosRealmInput;
+
+ @FindBy(id = "serverPrincipal")
+ private WebElement serverPrincipalInput;
+
+ @FindBy(id = "keyTab")
+ private WebElement keyTabInput;
+
+ @FindBy(id = "batchSizeForSync")
+ private WebElement batchSizeForSyncInput;
+
+ @FindBy(id = "fullSyncPeriod")
+ private WebElement fullSyncPeriodInput;
+
+ @FindBy(id = "changedSyncPeriod")
+ private WebElement changedSyncPeriodInput;
+
+ @FindBy(id = "editMode")
+ private Select editModeSelect;
+
+ @FindBy(id = "vendor")
+ private Select vendorSelect;
+
+ @FindByJQuery("a:contains('Test connection')")
+ private WebElement testConnectionButton;
+
+ @FindByJQuery("a:contains('Test authentication')")
+ private WebElement testAuthenticationButton;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch syncRegistrations;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch connectionPooling;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(2)")
+ private OnOffSwitch pagination;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(3)")
+ private OnOffSwitch allowKerberosAuth;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(4)")
+ private OnOffSwitch debug;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(5)")
+ private OnOffSwitch useKerberosForPwdAuth;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(6)")
+ private OnOffSwitch periodicFullSync;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(7)")
+ private OnOffSwitch periodicChangedUsersSync;
+
+ @FindByJQuery("button:contains('Save')")
+ private WebElement saveButton;
+
+ public void selectEditMode(String mode){
+ waitGuiForElement(By.id("editMode"));
+ editModeSelect.selectByVisibleText(mode);
+ }
+
+ public void selectVendor(String vendor){
+ waitGuiForElement(By.id("editMode"));
+ vendorSelect.selectByVisibleText(vendor);
+ }
+
+ public void configureLdap(String displayName, String editMode, String vendor, String connectionUrl, String userDN, String ldapBindDn, String ldapBindCredential){
+ consoleDisplayNameInput.sendKeys(displayName);
+ editModeSelect.selectByVisibleText(editMode);
+ selectVendor(vendor);
+ ldapConnectionUrlInput.sendKeys(connectionUrl);
+ ldapUserDnInput.sendKeys(userDN);
+ ldapBindDnInput.sendKeys(ldapBindDn);
+ ldapBindCredentialInput.sendKeys(ldapBindCredential);
+ saveButton.click();
+ }
+
+ public void testConnection(){
+ testConnectionButton.click();
+ }
+
+ public void testAuthentication(){
+ testAuthenticationButton.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
new file mode 100644
index 0000000..b209b81
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/federation/UserFederation.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.federation;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.By;
+import org.openqa.selenium.support.ui.Select;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+
+/**
+ * Created by fkiss.
+ */
+public class UserFederation extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/user-federation";
+ }
+
+ @FindByJQuery("select[ng-model*='selectedProvider']")
+ private Select addProviderSelect;
+
+ public void addProvider(String provider) {
+ waitGuiForElement(By.cssSelector("select[ng-model*='selectedProvider']"));
+ addProviderSelect.selectByVisibleText(provider);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java
new file mode 100644
index 0000000..d6d517b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/Breadcrumb.java
@@ -0,0 +1,34 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.List;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Breadcrumb {
+
+ public static final String BREADCRUMB_XPATH = "//ol[@class='breadcrumb']";
+
+ @FindBy(xpath = "./li[not(contains(@class,'ng-hide'))]/a")
+ private List<WebElement> items;
+
+ public int size() {
+ return items.size();
+ }
+
+ public WebElement getItem(int index) {
+ return items.get(index);
+ }
+
+ public WebElement getItemFromEnd(int index) {
+ return items.get(size() - index - 1);
+ }
+
+ public void clickItemOneLevelUp() {
+ getItemFromEnd(0).click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java
new file mode 100644
index 0000000..d049d2e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/DataTable.java
@@ -0,0 +1,76 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.List;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.By;
+import static org.openqa.selenium.By.xpath;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class DataTable {
+
+ @FindBy(css = "input[class*='search']")
+ private WebElement searchInput;
+ @FindBy(css = "div[class='input-group-addon'] i")
+ private WebElement searchButton;
+
+ @FindBy(tagName = "thead")
+ private WebElement header;
+ @FindBy(css = "tbody")
+ private WebElement body;
+ @FindBy(css = "tbody tr.ng-scope")
+ private List<WebElement> rows;
+
+ @FindBy
+ private WebElement infoRow;
+
+ public void search(String pattern) {
+ waitAjaxForBody();
+ searchInput.sendKeys(pattern);
+ searchButton.click();
+ }
+
+ public void clickHeaderButton(String buttonText) {
+ waitAjaxForBody();
+ header.findElement(By.xpath(".//button[text()='" + buttonText + "']")).click();
+ }
+
+ public void clickHeaderLink(String linkText) {
+ waitAjaxForBody();
+ header.findElement(By.linkText(linkText)).click();
+ }
+
+ public WebElement body() {
+ return body;
+ }
+
+ public void waitAjaxForBody() {
+ waitAjaxForElement(body);
+ }
+
+ public List<WebElement> rows() {
+ waitAjaxForBody();
+ pause(250);
+ return rows;
+ }
+
+ public WebElement getRowByLinkText(String text) {
+ WebElement row = body.findElement(By.xpath(".//tr[./td/a[text()='" + text + "']]"));
+ waitAjaxForElement(row);
+ return row;
+ }
+
+ public void clickRowByLinkText(String text) {
+ body.findElement(By.xpath(".//tr/td/a[text()='" + text + "']")).click();
+ }
+
+ public void clickRowActionButton(WebElement row, String buttonText) {
+ row.findElement(xpath(".//button[text()='" + buttonText + "']")).click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java
new file mode 100644
index 0000000..902287e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/InputList.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import java.util.ArrayList;
+import java.util.List;
+import static org.keycloak.testsuite.page.Form.getInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class InputList {
+
+ @FindBy(xpath=".//input[@ng-model='client.redirectUris[i]']")
+ private List<WebElement> inputs;
+
+ public List<String> getValues() {
+ List<String> values = new ArrayList<>();
+ for (WebElement input: inputs) {
+ values.add(getInputValue(input));
+ }
+ return values;
+ }
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
new file mode 100644
index 0000000..9bb95dd
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/ModalDialog.java
@@ -0,0 +1,36 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ModalDialog {
+
+ @FindBy(xpath = ".//button[text()='Cancel']")
+ private WebElement cancelButton;
+ @FindBy(xpath = ".//button[text()='Delete']")
+ private WebElement deleteButton;
+
+ @FindBy(xpath = ".//button[@ng-click='ok()']")
+ private WebElement okButton;
+
+ public void ok() {
+ waitAjaxForElement(okButton);
+ okButton.click();
+ }
+
+ public void confirmDeletion() {
+ waitAjaxForElement(deleteButton);
+ deleteButton.click();
+ }
+
+ public void cancel() {
+ waitAjaxForElement(cancelButton);
+ cancelButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java
new file mode 100644
index 0000000..5e2ea52
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/fragment/RealmSelector.java
@@ -0,0 +1,11 @@
+package org.keycloak.testsuite.console.page.fragment;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RealmSelector {
+
+ // TODO
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java
new file mode 100644
index 0000000..e903f01
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/CacheSettings.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+
+/**
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class CacheSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/cache-settings";
+ }
+
+ @Page
+ private CacheSettingsForm form;
+
+ public CacheSettingsForm form() {
+ return form;
+ }
+
+ public class CacheSettingsForm extends Form {
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch realmCacheEnabled;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch userCacheEnabled;
+
+ public void setRealmCacheEnabled(boolean value) {
+ realmCacheEnabled.setOn(value);
+ }
+
+ public void setUserCacheEnabled(boolean value) {
+ userCacheEnabled.setOn(value);
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java
new file mode 100644
index 0000000..f75117b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/EmailSettings.java
@@ -0,0 +1,70 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ * Created by mhajas on 8/25/15.
+ */
+public class EmailSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/smtp-settings";
+ }
+
+ @Page
+ private EmailSettingsForm form;
+
+ public EmailSettingsForm form() {
+ return form;
+ }
+
+ public class EmailSettingsForm extends Form {
+ @FindBy(id = "smtpHost")
+ private WebElement hostInput;
+
+ @FindBy(id = "smtpPort")
+ private WebElement portInput;
+
+ @FindBy(id = "smtpFrom")
+ private WebElement fromInput;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch enableSSL;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(1)")
+ private OnOffSwitch enableStartTLS;
+
+ @FindByJQuery("div[class='onoffswitch']:eq(2)")
+ private OnOffSwitch enableAuthentication;
+
+ public void setEnableSSL(boolean sslEnabled) {
+ enableSSL.setOn(sslEnabled);
+ }
+
+ public void setEnableStartTLS(boolean startTLS) {
+ enableSSL.setOn(startTLS);
+ }
+
+ public void setEnableAuthentication(boolean authentication) {
+ enableSSL.setOn(authentication);
+ }
+
+ public void setHostInput(String value) {
+ setInputValue(hostInput, value);
+ }
+
+ public void setPortInput(String value) {
+ setInputValue(portInput, value);
+ }
+
+ public void setFromInput(String value) {
+ setInputValue(fromInput, value);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java
new file mode 100644
index 0000000..902acf0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/KeysSettings.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.realm;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class KeysSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/cache-settings";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java
new file mode 100644
index 0000000..2cfcace
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/LoginSettings.java
@@ -0,0 +1,121 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Petr Mensik
+ */
+public class LoginSettings extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/login-settings";
+ }
+
+ @Page
+ private LoginSettingsForm form;
+
+ public LoginSettingsForm form() {
+ return form;
+ }
+
+ public enum RequireSSLOption {
+ all, external, none;
+ }
+
+ public class LoginSettingsForm extends Form {
+
+ @FindByJQuery("div[class='onoffswitch']:eq(0)")
+ private OnOffSwitch registrationAllowed;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='registrationEmailAsUsername']]")
+ private OnOffSwitch emailAsUsernameOnOffSwitch;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='editUsernameAllowed']]")
+ private OnOffSwitch editUsernameAllowed;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='resetPasswordAllowed']]")
+ private OnOffSwitch resetPasswordAllowed;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='rememberMe']]")
+ private OnOffSwitch rememberMeEnabled;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='verifyEmail']]")
+ private OnOffSwitch verifyEmailEnabled;
+
+ @FindBy(id = "sslRequired")
+ private Select requireSsl;
+
+ public boolean isRegistrationAllowed() {
+ return registrationAllowed.isOn();
+ }
+
+ public void setRegistrationAllowed(boolean allowed) {
+ registrationAllowed.setOn(allowed);
+ }
+
+ public void setEmailAsUsername(boolean emailAsUsername) {
+ emailAsUsernameOnOffSwitch.setOn(emailAsUsername);
+ }
+
+ public boolean isEditUsernameAllowed() {
+ return editUsernameAllowed.isOn();
+ }
+
+ public void setEditUsernameAllowed(boolean allowed) {
+ editUsernameAllowed.setOn(allowed);
+ }
+
+ public boolean isResetPasswordAllowed() {
+ return resetPasswordAllowed.isOn();
+ }
+
+ public void setResetPasswordAllowed(boolean allowed) {
+ resetPasswordAllowed.setOn(allowed);
+ }
+
+ public boolean isRememberMeAllowed() {
+ return rememberMeEnabled.isOn();
+ }
+
+ public void setRememberMeAllowed(boolean allowed) {
+ rememberMeEnabled.setOn(allowed);
+ }
+
+ public void setVerifyEmailAllowed(boolean allowed) {
+ verifyEmailEnabled.setOn(allowed);
+ }
+
+ public boolean isVerifyEmailAllowed() {
+ return verifyEmailEnabled.isOn();
+ }
+
+ public void selectRequireSSL(RequireSSLOption option) {
+ requireSsl.selectByValue(option.name());
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java
new file mode 100644
index 0000000..83b1a67
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/RealmSettings.java
@@ -0,0 +1,72 @@
+package org.keycloak.testsuite.console.page.realm;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RealmSettings extends AdminConsoleRealm {
+
+ @FindBy(xpath = "//div[@data-ng-controller='RealmTabCtrl']/ul")
+ private RealmTabs realmTabs;
+
+ public RealmTabs tabs() {
+ return realmTabs;
+ }
+
+ public class RealmTabs {
+
+ @FindBy(linkText = "General")
+ private WebElement generalSettingsTab;
+ @FindBy(linkText = "Login")
+ private WebElement loginSettingsTab;
+ @FindBy(linkText = "Keys")
+ private WebElement keysSettingsTab;
+ @FindBy(linkText = "Email")
+ private WebElement emailSettingsTab;
+ @FindBy(linkText = "Themes")
+ private WebElement themeSettingsTab;
+ @FindBy(linkText = "Cache")
+ private WebElement cacheSettingsTab;
+ @FindBy(linkText = "Tokens")
+ private WebElement tokenSettingsTab;
+ @FindBy(linkText = "Security Defenses")
+ private WebElement defenseTab;
+
+ public void general() {
+ generalSettingsTab.click();
+ }
+
+ public void login() {
+ loginSettingsTab.click();
+ }
+
+ public void keys() {
+ keysSettingsTab.click();
+ }
+
+ public void email() {
+ emailSettingsTab.click();
+ }
+
+ public void themes() {
+ themeSettingsTab.click();
+ }
+
+ public void cache() {
+ cacheSettingsTab.click();
+ }
+
+ public void tokens() {
+ tokenSettingsTab.click();
+ }
+
+ public void securityDefenses() {
+ defenseTab.click();
+ }
+
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java
new file mode 100644
index 0000000..00bd0b8
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/realm/SecurityDefenses.java
@@ -0,0 +1,197 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.page.realm;
+
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.page.Form.setInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ * @author Filip Kiss
+ * @author mhajas
+ */
+public class SecurityDefenses extends RealmSettings {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/defense"; // NOTE: page doesn't exist, only subpages
+ }
+
+ public class Headers extends SecurityDefenses {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/headers";
+ }
+
+ @Page
+ private HeadersForm form;
+
+ public HeadersForm form() {
+ return form;
+ }
+
+ public class HeadersForm extends Form {
+
+ @FindBy(id = "xFrameOptions")
+ private WebElement xFrameOptions;
+
+ public void setXFrameOptions(String value) {
+ setInputValue(xFrameOptions, value);
+ }
+
+ @FindBy(id = "contentSecurityPolicy")
+ private WebElement contentSecurityPolicy;
+
+ public void setContentSecurityPolicy(String value) {
+ setInputValue(contentSecurityPolicy, value);
+ }
+ }
+ }
+
+ public enum TimeSelectValues {
+
+ SECONDS("Seconds"), MINUTES("Minutes"), HOURS("Hours"), DAYS("Days");
+
+ private String name;
+
+ private TimeSelectValues(String name) {
+ this.name = name;
+ }
+
+ public String getName() {
+ return name;
+ }
+ }
+
+ public class BruteForceDetection extends SecurityDefenses {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/brute-force";
+ }
+
+ @Page
+ private BruteForceDetectionForm form;
+
+ public BruteForceDetectionForm form() {
+ return form;
+ }
+
+ public class BruteForceDetectionForm extends Form {
+
+ @FindByJQuery("div[class='onoffswitch']")
+ private OnOffSwitch protectionEnabled;
+
+ public void setProtectionEnabled(boolean protectionEnabled) {
+ this.protectionEnabled.setOn(protectionEnabled);
+ }
+
+ @FindBy(id = "failureFactor")
+ private WebElement maxLoginFailures;
+
+ public void setMaxLoginFailures(String value) {
+ setInputValue(maxLoginFailures, value);
+ }
+
+ @FindBy(id = "waitIncrement")
+ private WebElement waitIncrementInput;
+
+ @FindBy(name = "waitIncrementUnit")
+ private Select waitIncrementSelect;
+
+ public void setWaitIncrementInput(String value) {
+ setInputValue(waitIncrementInput, value);
+ }
+
+ public void setWaitIncrementSelect(TimeSelectValues value) {
+ waitIncrementSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "quickLoginCheckMilliSeconds")
+ private WebElement quickLoginCheckInput;
+
+ public void setQuickLoginCheckInput(String value) {
+ setInputValue(quickLoginCheckInput, value);
+ }
+
+ @FindBy(id = "minimumQuickLoginWait")
+ private WebElement minQuickLoginWaitInput;
+
+ @FindBy(name = "minimumQuickLoginWaitUnit")
+ private Select minQuickLoginWaitSelect;
+
+ public void setMinQuickLoginWaitInput(String value) {
+ setInputValue(minQuickLoginWaitInput, value);
+ }
+
+ public void setMinQuickLoginWaitSelect(TimeSelectValues value) {
+ minQuickLoginWaitSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "maxFailureWait")
+ private WebElement maxWaitInput;
+
+ @FindBy(name = "maxFailureWaitUnit")
+ private Select maxWaitSelect;
+
+ public void setMaxWaitInput(String value) {
+ setInputValue(maxWaitInput, value);
+ }
+
+ public void setMaxWaitSelect(TimeSelectValues value) {
+ maxWaitSelect.selectByVisibleText(value.getName());
+ }
+
+ @FindBy(id = "maxDeltaTime")
+ private WebElement failureResetTimeInput;
+
+ @FindBy(name = "maxDeltaTimeUnit")
+ private Select failureResetTimeSelect;
+
+ public void setFailureResetTimeInput(String value) {
+ setInputValue(failureResetTimeInput, value);
+ }
+
+ public void setFailureResetTimeSelect(TimeSelectValues value) {
+ failureResetTimeSelect.selectByVisibleText(value.getName());
+ }
+
+ }
+
+ }
+
+ @FindByJQuery("a:contains('Brute Force Detection')")
+ private WebElement bruteForceDetectionTab;
+
+ public void goToBruteForceDetection() {
+ bruteForceDetectionTab.click();
+ }
+
+ @FindByJQuery("a:contains('Headers')")
+ private WebElement headersTab;
+
+ public void goToHeaders() {
+ headersTab.click();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java
new file mode 100644
index 0000000..d1f218b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/CreateRole.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateRole extends AdminConsoleCreate {
+
+ public CreateRole() {
+ setEntity("role");
+ }
+
+ @Page
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java
new file mode 100644
index 0000000..2bde155
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Role.java
@@ -0,0 +1,33 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Role extends RealmRoles {
+
+ public static final String ROLE_ID = "roleId";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ROLE_ID + "}";
+ }
+
+ public void setRoleId(String id) {
+ setUriParameter(ROLE_ID, id);
+ }
+
+ public String getRoleId() {
+ return getUriParameter(ROLE_ID).toString();
+ }
+
+ @Page
+ private RoleForm form;
+
+ public RoleForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
new file mode 100644
index 0000000..876b45b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleCompositeRoles.java
@@ -0,0 +1,182 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import org.keycloak.representations.idm.RoleRepresentation.Composites;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.util.WaitUtils.waitGuiForElement;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author fkiss
+ * @author tkyjovsk
+ */
+public class RoleCompositeRoles extends Form {
+
+ @FindBy(id = "available")
+ protected Select availableRealmRolesSelect;
+ @FindBy(id = "assigned")
+ protected Select assignedRealmRolesSelect;
+
+ @FindBy(id = "clients")
+ protected Select clientSelect;
+ @FindBy(id = "available-client")
+ protected Select availableClientRolesSelect;
+ @FindBy(id = "assigned-client")
+ protected Select assignedClientRolesSelect;
+
+ @FindBy(css = "button[ng-click*='addRealm']")
+ protected WebElement addSelectedRealmRolesButton;
+ @FindBy(css = "button[ng-click*='addClient']")
+ protected WebElement addSelectedClientRolesButton;
+ @FindBy(css = "button[ng-click*='deleteRealm']")
+ protected WebElement removeSelectedRealmRolesButton;
+ @FindBy(css = "button[ng-click*='deleteClient']")
+ protected WebElement removeSelectedClientRolesButton;
+
+ public Composites getComposites() {
+ Composites composites = new Composites();
+ // realm roles
+ composites.setRealm(getSelectValues(assignedRealmRolesSelect));
+ // client roles
+ Map<String, List<String>> clientRoles = new HashMap<>();
+ for (String client : getSelectValues(clientSelect)) {
+ clientSelect.selectByVisibleText(client);
+ clientRoles.put(client, new ArrayList(getSelectValues(assignedClientRolesSelect)));
+ }
+ composites.setClient(clientRoles);
+ return composites;
+ }
+
+ public void setComposites(Composites composites) {
+ if (composites != null) {
+ setRealmRoles(composites.getRealm());
+ for (String client : composites.getClient().keySet()) {
+ clientSelect.selectByVisibleText(client);
+ setClientRoles(composites.getClient().get(client));
+ }
+ }
+ }
+
+ private void setRealmRoles(Collection<String> roles) {
+ removeRedundantRoles(assignedRealmRolesSelect, removeSelectedRealmRolesButton, roles);
+ addMissingRoles(availableRealmRolesSelect, addSelectedRealmRolesButton, roles);
+ }
+
+ private void setClientRoles(Collection<String> roles) {
+ removeRedundantRoles(assignedClientRolesSelect, removeSelectedClientRolesButton, roles);
+ addMissingRoles(availableClientRolesSelect, addSelectedClientRolesButton, roles);
+ }
+
+ private void removeRedundantRoles(Select select, WebElement button, Collection<String> roles) {
+ select.deselectAll();
+ for (String role : getSelectValues(select)) {
+ if (roles == null // if roles not provided, remove all
+ || !roles.contains(role)) { // if roles provided, remove only the redundant
+ select.selectByVisibleText(role);
+ }
+ }
+ button.click();
+ }
+
+ protected void addMissingRoles(Select select, WebElement button, Collection<String> roles) {
+ select.deselectAll();
+ if (roles != null) { // if roles not provided, don't add any
+ for (String role : getSelectValues(select)) {
+ if (roles.contains(role)) { // if roles provided, add only the missing
+ select.selectByVisibleText(role);
+ }
+ }
+ button.click();
+ }
+ }
+
+ public static Set<String> getSelectValues(Select select) {
+ Set<String> roles = new HashSet<>();
+ for (WebElement option : select.getOptions()) {
+ roles.add(option.getText());
+ }
+ return roles;
+ }
+
+ // ***
+ public Set<String> getAvailableRealmRoles() {
+ return getSelectValues(availableRealmRolesSelect);
+ }
+
+ public Set<String> getAvailableClientRoles(String client) {
+ return getSelectValues(availableClientRolesSelect);
+ }
+
+ public Set<String> getAssignedRealmRoles() {
+ return getSelectValues(assignedRealmRolesSelect);
+ }
+
+ public Set<String> getAssignedClientRoles() {
+ return getSelectValues(assignedClientRolesSelect);
+ }
+
+ // *** original methods ***
+ public void addAvailableRole(String... roles) {
+ waitGuiForElement(By.id("available"));
+ for (String role : roles) {
+ availableRealmRolesSelect.selectByVisibleText(role);
+ addSelectedRealmRolesButton.click();
+ }
+ }
+
+ public void removeAssignedRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ assignedRealmRolesSelect.selectByVisibleText(role);
+ removeSelectedRealmRolesButton.click();
+ }
+
+ public boolean isAssignedRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ try {
+ assignedRealmRolesSelect.selectByVisibleText(role);
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+
+ public boolean isAssignedClientRole(String role) {
+ waitGuiForElement(By.id("assigned"));
+ try {
+ assignedClientRolesSelect.selectByVisibleText(role);
+ } catch (Exception ex) {
+ return false;
+ }
+ return true;
+ }
+
+ public void selectClientRole(String client) {
+ waitGuiForElement(By.id("clients"));
+ clientSelect.selectByVisibleText(client);
+ }
+
+ public void addAvailableClientRole(String... roles) {
+ waitGuiForElement(By.id("available-client"));
+ for (String role : roles) {
+ availableClientRolesSelect.selectByVisibleText(role);
+ addSelectedClientRolesButton.click();
+ }
+ }
+
+ public void removeAssignedClientRole(String client) {
+ waitGuiForElement(By.id("assigned-client"));
+ assignedClientRolesSelect.selectByVisibleText(client);
+ removeSelectedClientRolesButton.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java
new file mode 100644
index 0000000..b4f974a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RoleForm.java
@@ -0,0 +1,114 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RoleForm extends Form {
+
+ @FindBy(id = "name")
+ private WebElement nameInput;
+
+ @FindBy(id = "description")
+ private WebElement descriptionInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='scopeParamRequired']]")
+ private OnOffSwitch scopeParamRequired;
+
+ @FindBy(xpath = ".//div[contains(@class,'onoffswitch') and ./input[@id='compositeSwitch']]")
+ private OnOffSwitch compositeSwitch;
+
+ @FindBy(xpath = ".//fieldset[./legend[contains(text(),'Composite Roles')]]")
+ private RoleCompositeRoles compositeRoles;
+
+ @FindBy(id = "removeRole")
+ private WebElement removeIcon;
+
+ public RoleRepresentation getRole() {
+ RoleRepresentation role = new RoleRepresentation(getName(), getDescription(), isScopeParamRequired());
+ role.setComposite(isComposite());
+ if (role.isComposite()) {
+ role.setComposites(compositeRoles.getComposites());
+ }
+ return role;
+ }
+
+ public void setRole(RoleRepresentation role) {
+ setBasicAttributes(role);
+ }
+
+ public RoleRepresentation getBasicAttributes() {
+ RoleRepresentation role = new RoleRepresentation();
+ role.setName(getName());
+ role.setDescription(getDescription());
+ role.setScopeParamRequired(isScopeParamRequired());
+ role.setComposite(isComposite());
+ log.info(role.getName() + ": " + role.getDescription() + ", comp: " + role.isComposite());
+ return role;
+ }
+
+ public void setBasicAttributes(RoleRepresentation role) {
+ setName(role.getName());
+ setDescription(role.getDescription());
+ setScopeParamRequired(role.isScopeParamRequired());
+ if (role.isComposite()) {
+ setCompositeRoles(role);
+ }
+ }
+
+ // TODO KEYCLOAK-1364 enabling/disabling composite role seems unintuitive
+ // it should be possible to remove all composite roles by switching to OFF
+ public void setCompositeRoles(RoleRepresentation role) {
+ if (role.isComposite() && role.getComposites() != null) {
+ setComposite(true);
+ }
+ compositeRoles.setComposites(role.getComposites());
+ }
+
+ public void setName(String name) {
+ setInputValue(nameInput, name);
+ }
+
+ public String getName() {
+ return getInputValue(nameInput);
+ }
+
+ public void setDescription(String description) {
+ setInputValue(descriptionInput, description);
+ }
+
+ public String getDescription() {
+ return getInputValue(descriptionInput);
+ }
+
+ public void setScopeParamRequired(boolean scopeParamRequired) {
+ this.scopeParamRequired.setOn(scopeParamRequired);
+ }
+
+ public boolean isScopeParamRequired() {
+ return scopeParamRequired.isOn();
+ }
+
+ public void setComposite(boolean composite) {
+ compositeSwitch.setOn(composite);
+ }
+
+ public boolean isComposite() {
+ return compositeSwitch.isOn();
+ }
+
+ public RoleCompositeRoles compositeRoles() {
+ return compositeRoles;
+ }
+
+ public void delete() {
+ removeIcon.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java
new file mode 100644
index 0000000..910e470
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/Roles.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import org.keycloak.admin.client.resource.RolesResource;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Roles extends AdminConsoleRealm {
+
+ @FindBy(css = "ul.nav-tabs")
+ private RoleTabs tabs;
+
+ public RoleTabs tabs() {
+ return tabs;
+ }
+
+ public class RoleTabs {
+
+ @FindBy(linkText = "Realm Roles")
+ private WebElement realmRolesTab;
+ @FindBy(linkText = "Default Roles")
+ private WebElement defaultRolesTab;
+
+ public void realmRoles() {
+ realmRolesTab.click();
+ }
+
+ public void defaultRoles() {
+ defaultRolesTab.click();
+ }
+
+ }
+
+ public RolesResource rolesResource() {
+ return realmResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java
new file mode 100644
index 0000000..b215844
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/roles/RolesTable.java
@@ -0,0 +1,85 @@
+package org.keycloak.testsuite.console.page.roles;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import static org.openqa.selenium.By.tagName;
+import org.openqa.selenium.WebElement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class RolesTable extends DataTable {
+
+ public static final String ADD_ROLE = "Add Role";
+
+ public static final String EDIT = "Edit";
+ public static final String DELETE = "Delete";
+
+ public List<RoleRepresentation> searchRoles(String searchPattern) {
+ search(searchPattern);
+ return getRolesFromTableRows();
+ }
+
+ public void addRole() {
+ clickHeaderLink(ADD_ROLE);
+ }
+
+ public void clickRole(String name) {
+ waitAjaxForBody();
+ clickRowByLinkText(name);
+ }
+
+ public void editRole(String name) {
+ clickRowActionButton(getRowByLinkText(name), EDIT);
+ }
+
+ public void deleteRole(String name) {
+ clickRowActionButton(getRowByLinkText(name), DELETE);
+ }
+
+ public RoleRepresentation findRole(String name) {
+ List<RoleRepresentation> roles = searchRoles(name);
+ if (roles.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == roles.size();
+ return roles.get(0);
+ }
+ }
+
+ public boolean containsRole(String roleName) {
+ for (RoleRepresentation r : getRolesFromTableRows()) {
+ if (roleName.equals(r.getName())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public List<RoleRepresentation> getRolesFromTableRows() {
+ List<RoleRepresentation> rows = new ArrayList<>();
+ for (WebElement row : rows()) {
+ RoleRepresentation role = getRoleFromRow(row);
+ if (role != null) {
+ rows.add(role);
+ }
+ }
+ return rows;
+ }
+
+ public RoleRepresentation getRoleFromRow(WebElement row) {
+ RoleRepresentation role = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ role = new RoleRepresentation();
+ role.setName(tds.get(0).getText());
+ role.setComposite(Boolean.valueOf(tds.get(1).getText()));
+ role.setDescription(tds.get(2).getText());
+ }
+ return role;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java
new file mode 100644
index 0000000..0175848
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Revocation.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.sessions;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Revocation extends Sessions {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/revocation";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java
new file mode 100644
index 0000000..a0a8dba
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/sessions/Sessions.java
@@ -0,0 +1,32 @@
+package org.keycloak.testsuite.console.page.sessions;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Sessions extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+ @FindBy(linkText = "Realm Sessions")
+ private WebElement realmSessionsTab;
+
+ @FindBy(linkText = "Revocation")
+ private WebElement revocationTab;
+
+ public void realmSessions() {
+ realmSessionsTab.click();
+ }
+
+ public void revocation() {
+ revocationTab.click();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java
new file mode 100644
index 0000000..4cd0df6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/CreateUser.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.testsuite.console.page.AdminConsoleCreate;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class CreateUser extends AdminConsoleCreate {
+
+ public CreateUser() {
+ setEntity("user");
+ }
+
+ @Page
+ private UserAttributesForm form;
+
+ public UserAttributesForm form() {
+ return form;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java
new file mode 100644
index 0000000..fba2adb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/User.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.keycloak.admin.client.resource.UserResource;
+import org.keycloak.testsuite.console.page.fragment.Breadcrumb;
+import static org.keycloak.testsuite.console.page.fragment.Breadcrumb.BREADCRUMB_XPATH;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class User extends Users {
+
+ public static final String ID = "id";
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/{" + ID + "}";
+ }
+
+ public void setId(String id) {
+ setUriParameter(ID, id);
+ }
+
+ public String getId() {
+ return (String) getUriParameter(ID);
+ }
+
+ @FindBy(xpath = BREADCRUMB_XPATH)
+ private Breadcrumb breadcrumb;
+
+ public Breadcrumb breadcrumb() {
+ return breadcrumb;
+ }
+
+ @FindBy(xpath = "//div[@data-ng-controller='UserTabCtrl']/ul")
+ protected UserTabs userTabs;
+
+ public UserTabs tabs() {
+ return userTabs;
+ }
+
+ public class UserTabs {
+
+ @FindBy(linkText = "Attributes")
+ private WebElement attributesLink;
+ @FindBy(linkText = "Credentials")
+ private WebElement credentialsLink;
+ @FindBy(linkText = "Role Mappings")
+ private WebElement roleMappingsLink;
+ @FindBy(linkText = "Consents")
+ private WebElement consentsLink;
+ @FindBy(linkText = "Sessions")
+ private WebElement sessionsLink;
+
+ public void attributes() {
+ attributesLink.click();
+ }
+
+ public void credentials() {
+ credentialsLink.click();
+ }
+
+ public void roleMappings() {
+ roleMappingsLink.click();
+ }
+
+ public void consents() {
+ consentsLink.click();
+ }
+
+ public void sessions() {
+ sessionsLink.click();
+ }
+
+ }
+
+ public UserResource userResource() {
+ return usersResource().get(getId());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java
new file mode 100644
index 0000000..ebe5218
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributes.java
@@ -0,0 +1,22 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserAttributes extends User {
+
+ @FindBy(name = "userForm")
+ private UserAttributesForm form;
+
+ public UserAttributesForm form() {
+ return form;
+ }
+
+ public void backToUsersViaBreadcrumb() {
+ breadcrumb().clickItemOneLevelUp();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java
new file mode 100644
index 0000000..85b32e6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserAttributesForm.java
@@ -0,0 +1,139 @@
+package org.keycloak.testsuite.console.page.users;
+
+import java.util.List;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import org.keycloak.testsuite.page.Form;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class UserAttributesForm extends Form {
+
+ @FindBy(id = "id")
+ private WebElement idInput;
+
+ @FindBy(id = "username")
+ private WebElement usernameInput;
+
+ @FindBy(id = "email")
+ private WebElement emailInput;
+
+ @FindBy(id = "firstName")
+ private WebElement firstNameInput;
+
+ @FindBy(id = "lastName")
+ private WebElement lastNameInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='userEnabled']]")
+ private OnOffSwitch userEnabledSwitch;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='emailVerified']]")
+ private OnOffSwitch emailVerifiedSwitch;
+
+ @FindBy(xpath = ".//div[./label[contains(text(), 'Required User Actions')]]//input")
+ private WebElement requiredUserActionsInput;
+
+ @FindBy(id = "reqActions")
+ private Select requiredUserActionsSelect;
+
+ @FindBy(className = "select2-result-label")
+ private WebElement requiredUserActionsConfirm;
+
+ @FindBy(className = "select2-search-choice-close")
+ private List<WebElement> removeRequiredActionsList;
+
+ @FindBy(xpath = "//button[@data-ng-click='unlockUser()']")
+ private WebElement unlockUserButton;
+
+ public String getId() {
+ return getInputValue(idInput);
+ }
+
+ public String getUsername() {
+ return getInputValue(usernameInput);
+ }
+
+ public void setUsername(String username) {
+ setInputValue(usernameInput, username);
+ }
+
+ public String getEmail() {
+ return getInputValue(emailInput);
+ }
+
+ public void setEmail(String email) {
+ setInputValue(emailInput, email);
+ }
+
+ public String getFirstName() {
+ return getInputValue(firstNameInput);
+ }
+
+ public void setFirstName(String firstName) {
+ setInputValue(firstNameInput, firstName);
+ }
+
+ public String getLastName() {
+ return getInputValue(lastNameInput);
+ }
+
+ public void setLastName(String lastname) {
+ setInputValue(lastNameInput, lastname);
+ }
+
+ public boolean isEnabled() {
+ return userEnabledSwitch.isOn();
+ }
+
+ public void setEnabled(boolean enabled) {
+ userEnabledSwitch.setOn(enabled);
+ }
+
+ public void unlockUser() {
+ unlockUserButton.click();
+ }
+
+ public boolean isEmailVerified() {
+ return emailVerifiedSwitch.isOn();
+ }
+
+ public void setEmailVerified(boolean emailVerified) {
+ emailVerifiedSwitch.setOn(emailVerified);
+ }
+
+ public void addRequiredAction(String requiredAction) {
+ requiredUserActionsInput.click();
+ requiredUserActionsSelect.selectByVisibleText(requiredAction);
+ }
+
+ public void setRequiredActions(List<String> requiredActions) {
+ for (WebElement e : removeRequiredActionsList) {
+ e.click();
+ }
+ if (requiredActions != null && !requiredActions.isEmpty()) {
+ for (String action : requiredActions) {
+ addRequiredAction(action);
+ }
+ }
+ }
+
+ public void setValues(UserRepresentation user) {
+ waitAjaxForElement(usernameInput);
+ setUsername(user.getUsername());
+ setEmail(user.getEmail());
+ setFirstName(user.getFirstName());
+ setLastName(user.getLastName());
+ setEnabled(user.isEnabled());
+ setEmailVerified(user.isEmailVerified());
+ setRequiredActions(user.getRequiredActions());
+ }
+
+ // TODO Contact Information section
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java
new file mode 100644
index 0000000..6525763
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserConsents.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.users;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserConsents extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/consents";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java
new file mode 100644
index 0000000..830d789
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserCredentials.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.keycloak.testsuite.console.page.fragment.OnOffSwitch;
+import static org.keycloak.testsuite.page.Form.setInputValue;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserCredentials extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/user-credentials";
+ }
+
+ @FindBy(id = "password")
+ private WebElement newPasswordInput;
+
+ @FindBy(id = "confirmPassword")
+ private WebElement confirmPasswordInput;
+
+ @FindBy(xpath = ".//div[@class='onoffswitch' and ./input[@id='temporaryPassword']]")
+ private OnOffSwitch temporaryOnOffSwitch;
+
+ @FindBy(xpath = ".//button[contains(@data-ng-click, 'resetPassword')]")
+ private WebElement resetPasswordButton;
+
+ public void setNewPassword(String newPassword) {
+ setInputValue(newPasswordInput, newPassword);
+ }
+
+ public void setConfirmPassword(String confirmPassword) {
+ setInputValue(confirmPasswordInput, confirmPassword);
+ }
+
+ public void setTemporary(boolean temporary) {
+ temporaryOnOffSwitch.setOn(temporary);
+ }
+
+ public void clickResetPasswordAndConfirm() {
+ resetPasswordButton.click();
+ modalDialog.ok();
+ }
+
+ public void resetPassword(String newPassword) {
+ resetPassword(newPassword, newPassword);
+ }
+ public void resetPassword(String newPassword, String confirmPassword) {
+ setNewPassword(newPassword);
+ setConfirmPassword(confirmPassword);
+ clickResetPasswordAndConfirm();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java
new file mode 100644
index 0000000..3a05b68
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappings.java
@@ -0,0 +1,28 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.keycloak.admin.client.resource.RoleMappingResource;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserRoleMappings extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "role-mappings";
+ }
+
+ @Page
+ private UserRoleMappingsForm form;
+
+ public UserRoleMappingsForm form() {
+ return form;
+ }
+
+ public RoleMappingResource roleMappingResource() {
+ return userResource().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java
new file mode 100644
index 0000000..2629017
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserRoleMappingsForm.java
@@ -0,0 +1,46 @@
+package org.keycloak.testsuite.console.page.users;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+import org.openqa.selenium.support.ui.Select;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.roles.RoleCompositeRoles;
+
+/**
+ * Created by fkiss.
+ */
+public class UserRoleMappingsForm extends RoleCompositeRoles {
+
+ @FindBy(id = "realm-composite")
+ private Select effectiveRolesSelect;
+
+ @FindBy(id = "client-composite")
+ private Select effectiveClientRolesSelect;
+
+ public boolean isEffectiveRealmRolesComplete(RoleRepresentation... roles) {
+ return isEffectiveRolesComplete(effectiveRolesSelect, roles);
+ }
+
+ public boolean isEffectiveClientRolesComplete(RoleRepresentation... roles) {
+ return isEffectiveRolesComplete(effectiveClientRolesSelect, roles);
+ }
+
+ private boolean isEffectiveRolesComplete(Select select, RoleRepresentation... roles) {
+ List<String> roleNames = new ArrayList<>();
+ for (RoleRepresentation role : roles) {
+ roleNames.add(role.getName());
+ }
+ for (WebElement role : select.getOptions()) {
+ roleNames.contains(role.getText());
+ roleNames.remove(role.getText());
+ }
+ log.info(Arrays.toString(roles));
+ log.info(roleNames);
+ return roleNames.isEmpty();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java
new file mode 100644
index 0000000..3b1d730
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/Users.java
@@ -0,0 +1,142 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.page.users;
+
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+import java.util.ArrayList;
+import java.util.List;
+import org.keycloak.admin.client.resource.UsersResource;
+import org.keycloak.representations.idm.UserRepresentation;
+
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.fragment.DataTable;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import static org.openqa.selenium.By.*;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class Users extends AdminConsoleRealm {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/users";
+ }
+
+ public static final String VIEW_ALL_USERS = "View all users";
+ public static final String UNLOCK_USERS = "Unlock Users";
+ public static final String ADD_USER = "Add User";
+
+ public static final String EDIT = "Edit";
+ public static final String IMPERSONATE = "Impersonate";
+ public static final String DELETE = "Delete";
+
+ @FindBy(xpath = "//div[./h1[text()='Users']]/table")
+ private UsersTable table;
+
+ public UsersTable table() {
+ return table;
+ }
+
+ public class UsersTable extends DataTable {
+
+ public List<UserRepresentation> searchUsers(String searchPattern) {
+ search(searchPattern);
+ return getUsersFromTableRows();
+ }
+
+ public void viewAllUsers() {
+ clickHeaderButton(VIEW_ALL_USERS);
+ }
+
+ public void unlockUsers() {
+ clickHeaderButton(UNLOCK_USERS);
+ }
+
+ public void clickUser(String username) {
+ waitAjaxForElement(body());
+ body().findElement(linkText(username)).click();
+ }
+
+ public void editUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), EDIT);
+ }
+
+ public void impersonateUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), IMPERSONATE);
+ }
+
+ public void deleteUser(String username) {
+ clickRowActionButton(getRowByLinkText(username), DELETE);
+ modalDialog.confirmDeletion();
+ }
+
+ public void addUser() {
+ clickHeaderLink(ADD_USER);
+ }
+
+ public UserRepresentation findUser(String searchPattern) {
+ List<UserRepresentation> users = searchUsers(searchPattern);
+ if (users.isEmpty()) {
+ return null;
+ } else {
+ assert 1 == users.size();
+ return users.get(0);
+ }
+ }
+
+ public UserRepresentation getUserFromTableRow(WebElement row) {
+ UserRepresentation user = null;
+ List<WebElement> tds = row.findElements(tagName("td"));
+ if (!(tds.isEmpty() || tds.get(0).getText().isEmpty())) {
+ user = new UserRepresentation();
+ user.setUsername(tds.get(0).getText());
+ user.setLastName(tds.get(1).getText());
+ user.setFirstName(tds.get(2).getText());
+ user.setEmail(tds.get(3).getText());
+ }
+ return user;
+ }
+
+ public List<UserRepresentation> getUsersFromTableRows() {
+ List<UserRepresentation> users = new ArrayList<>();
+ List<WebElement> rows = rows();
+// if (rows.size() > 1) {
+ for (WebElement rowElement : rows) {
+ if (rowElement.isDisplayed()) {
+ UserRepresentation user = getUserFromTableRow(rowElement);
+ if (user != null) {
+ users.add(user);
+ }
+ }
+ }
+// }
+ return users;
+ }
+
+ }
+
+ public UsersResource usersResource() {
+ return realmResource().users();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java
new file mode 100644
index 0000000..bfe9ac5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/console/page/users/UserSessions.java
@@ -0,0 +1,14 @@
+package org.keycloak.testsuite.console.page.users;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UserSessions extends User {
+
+ @Override
+ public String getUriFragment() {
+ return super.getUriFragment() + "/sessions";
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java
new file mode 100644
index 0000000..a06666c
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPage.java
@@ -0,0 +1,83 @@
+package org.keycloak.testsuite.page;
+
+import java.net.URI;
+import java.util.HashMap;
+import java.util.Map;
+import javax.ws.rs.core.UriBuilder;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.openqa.selenium.WebDriver;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractPage {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ private final Map<String, Object> uriParameters = new HashMap<>();
+
+ @Drone
+ protected WebDriver driver;
+
+ private UriBuilder builder;
+
+ public WebDriver getDriver() {
+ return driver;
+ }
+
+ public abstract UriBuilder createUriBuilder();
+
+ public String getUriFragment() {
+ return "";
+ }
+
+ /**
+ *
+ * @return Instance of UriBuilder that can build URIs for a concrete page.
+ */
+ public UriBuilder getUriBuilder() {
+ if (builder == null) {
+ builder = createUriBuilder();
+ String fragment = getUriFragment();
+ if (fragment != null && !fragment.isEmpty()) {
+ builder.fragment(fragment);
+ }
+ }
+ return builder;
+ }
+
+ public AbstractPage setUriParameter(String name, Object value) {
+ uriParameters.put(name, value);
+ return this;
+ }
+
+ public Object getUriParameter(String name) {
+ return uriParameters.get(name);
+ }
+
+ public URI buildUri() {
+ return getUriBuilder().buildFromMap(uriParameters);
+ }
+
+ @Override
+ public String toString() {
+ return buildUri().toASCIIString();
+ }
+
+ public void navigateTo() {
+ String uri = buildUri().toASCIIString();
+ log.debug("current URL: " + driver.getCurrentUrl());
+ log.info("navigating to " + uri);
+ driver.navigate().to(uri);
+ pause(300); // this is needed for FF for some reason
+ log.info("current URL: " + driver.getCurrentUrl());
+ }
+
+ public boolean isCurrent() {
+ return driver.getCurrentUrl().equals(toString());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
new file mode 100644
index 0000000..b85c75a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/AbstractPageWithInjectedUrl.java
@@ -0,0 +1,23 @@
+package org.keycloak.testsuite.page;
+
+import java.net.URISyntaxException;
+import java.net.URL;
+import javax.ws.rs.core.UriBuilder;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractPageWithInjectedUrl extends AbstractPage {
+
+ public abstract URL getInjectedUrl();
+
+ @Override
+ public UriBuilder createUriBuilder() {
+ try {
+ return UriBuilder.fromUri(getInjectedUrl().toURI());
+ } catch (URISyntaxException ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java
new file mode 100644
index 0000000..eee8a21
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/page/Form.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.page;
+
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import static org.jboss.arquillian.graphene.Graphene.guardAjax;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.util.WaitUtils.waitAjaxForElement;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Form {
+
+ protected final Logger log = Logger.getLogger(this.getClass());
+
+ @Drone
+ protected WebDriver driver;
+
+ public static final String ACTIVE_DIV_XPATH = ".//div[not(contains(@class,'ng-hide'))]";
+
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Save']")
+ private WebElement save;
+ @FindBy(xpath = ACTIVE_DIV_XPATH + "/button[text()='Cancel']")
+ private WebElement cancel;
+
+ public void save() {
+// guardAjax(save).click();
+ save.click();
+ }
+
+ public void cancel() {
+ guardAjax(cancel).click();
+ }
+
+ public static String getInputValue(WebElement input) {
+ waitAjaxForElement(input);
+ return input.getAttribute(VALUE);
+ }
+
+ public static final String VALUE = "value";
+
+ public static void setInputValue(WebElement input, String value) {
+ waitAjaxForElement(input);
+ if (input.isEnabled()) {
+ input.clear();
+ if (value != null) {
+ input.sendKeys(value);
+ }
+ } else {
+ // TODO log warning
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java
new file mode 100644
index 0000000..2898773
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/servlet/ApplicationServlet.java
@@ -0,0 +1,61 @@
+/*
+ * 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.servlet;
+
+import org.keycloak.services.resources.RealmsResource;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.ws.rs.core.UriBuilder;
+import java.io.IOException;
+import java.io.PrintWriter;
+
+/**
+ * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
+ */
+public class ApplicationServlet extends HttpServlet {
+
+ private static final String LINK = "<a href=\"%s\" id=\"%s\">%s</a>";
+
+ @Override
+ protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
+ String title;
+ if (req.getRequestURI().endsWith("auth")) {
+ title = "AUTH_RESPONSE";
+ } else if (req.getRequestURI().endsWith("logout")) {
+ title = "LOGOUT_REQUEST";
+ } else {
+ title = "APP_REQUEST";
+ }
+
+ PrintWriter pw = resp.getWriter();
+ pw.printf("<html><head><title>%s</title></head><body>", title);
+ UriBuilder base = UriBuilder.fromUri("http://localhost:8081/auth");
+ pw.printf(LINK, RealmsResource.accountUrl(base).build("test"), "account", "account");
+
+ pw.print("</body></html>");
+ pw.flush();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
new file mode 100644
index 0000000..9ff2bcf
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/IOUtil.java
@@ -0,0 +1,43 @@
+package org.keycloak.testsuite.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.util.JsonSerialization;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class IOUtil {
+
+ public static <T> T loadJson(InputStream is, Class<T> type) {
+ try {
+ return JsonSerialization.readValue(is, type);
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to load json.", e);
+ }
+ }
+
+ public static RealmRepresentation loadRealm(String realmConfig) {
+ return loadRealm(IOUtil.class.getResourceAsStream(realmConfig));
+ }
+
+ public static RealmRepresentation loadRealm(File realmFile) {
+ try {
+ return loadRealm(new FileInputStream(realmFile));
+ } catch (FileNotFoundException ex) {
+ throw new IllegalStateException("Test realm file not found: " + realmFile);
+ }
+ }
+
+ public static RealmRepresentation loadRealm(InputStream is) {
+ RealmRepresentation realm = loadJson(is, RealmRepresentation.class);
+ System.out.println("Loaded realm " + realm.getRealm());
+ return realm;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java
new file mode 100644
index 0000000..9d1d19e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/LDAPTestConfiguration.java
@@ -0,0 +1,128 @@
+package org.keycloak.testsuite.util;
+
+import org.jboss.logging.Logger;
+import org.keycloak.constants.KerberosConstants;
+import org.keycloak.models.LDAPConstants;
+import org.keycloak.models.UserFederationProvider;
+
+import java.io.File;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class LDAPTestConfiguration {
+
+ private static final Logger log = Logger.getLogger(LDAPTestConfiguration.class);
+
+ private String connectionPropertiesLocation;
+ private boolean startEmbeddedLdapLerver = true;
+ private Map<String, String> config;
+
+ protected static final Map<String, String> PROP_MAPPINGS = new HashMap<String, String>();
+ protected static final Map<String, String> DEFAULT_VALUES = new HashMap<String, String>();
+
+ static {
+ PROP_MAPPINGS.put(LDAPConstants.CONNECTION_URL, "idm.test.ldap.connection.url");
+ PROP_MAPPINGS.put(LDAPConstants.BASE_DN, "idm.test.ldap.base.dn");
+ PROP_MAPPINGS.put(LDAPConstants.USERS_DN, "idm.test.ldap.user.dn.suffix");
+ PROP_MAPPINGS.put(LDAPConstants.BIND_DN, "idm.test.ldap.bind.dn");
+ PROP_MAPPINGS.put(LDAPConstants.BIND_CREDENTIAL, "idm.test.ldap.bind.credential");
+ PROP_MAPPINGS.put(LDAPConstants.VENDOR, "idm.test.ldap.vendor");
+ PROP_MAPPINGS.put(LDAPConstants.CONNECTION_POOLING, "idm.test.ldap.connection.pooling");
+ PROP_MAPPINGS.put(LDAPConstants.PAGINATION, "idm.test.ldap.pagination");
+ PROP_MAPPINGS.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, "idm.test.ldap.batch.size.for.sync");
+ PROP_MAPPINGS.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, "idm.test.ldap.username.ldap.attribute");
+ PROP_MAPPINGS.put(LDAPConstants.RDN_LDAP_ATTRIBUTE, "idm.test.ldap.rdn.ldap.attribute");
+ PROP_MAPPINGS.put(LDAPConstants.USER_OBJECT_CLASSES, "idm.test.ldap.user.object.classes");
+ PROP_MAPPINGS.put(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE, "idm.test.ldap.user.account.controls.after.password.update");
+ PROP_MAPPINGS.put(LDAPConstants.EDIT_MODE, "idm.test.ldap.edit.mode");
+
+ PROP_MAPPINGS.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "idm.test.kerberos.allow.kerberos.authentication");
+ PROP_MAPPINGS.put(KerberosConstants.KERBEROS_REALM, "idm.test.kerberos.realm");
+ PROP_MAPPINGS.put(KerberosConstants.SERVER_PRINCIPAL, "idm.test.kerberos.server.principal");
+ PROP_MAPPINGS.put(KerberosConstants.KEYTAB, "idm.test.kerberos.keytab");
+ PROP_MAPPINGS.put(KerberosConstants.DEBUG, "idm.test.kerberos.debug");
+ PROP_MAPPINGS.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "idm.test.kerberos.allow.password.authentication");
+ PROP_MAPPINGS.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "idm.test.kerberos.update.profile.first.login");
+ PROP_MAPPINGS.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "idm.test.kerberos.use.kerberos.for.password.authentication");
+
+ DEFAULT_VALUES.put(LDAPConstants.CONNECTION_URL, "ldap://localhost:10389");
+ DEFAULT_VALUES.put(LDAPConstants.BASE_DN, "dc=keycloak,dc=org");
+ DEFAULT_VALUES.put(LDAPConstants.USERS_DN, "ou=People,dc=keycloak,dc=org");
+ DEFAULT_VALUES.put(LDAPConstants.BIND_DN, "uid=admin,ou=system");
+ DEFAULT_VALUES.put(LDAPConstants.BIND_CREDENTIAL, "secret");
+ DEFAULT_VALUES.put(LDAPConstants.VENDOR, LDAPConstants.VENDOR_OTHER);
+ DEFAULT_VALUES.put(LDAPConstants.CONNECTION_POOLING, "true");
+ DEFAULT_VALUES.put(LDAPConstants.PAGINATION, "true");
+ DEFAULT_VALUES.put(LDAPConstants.BATCH_SIZE_FOR_SYNC, String.valueOf(LDAPConstants.DEFAULT_BATCH_SIZE_FOR_SYNC));
+ DEFAULT_VALUES.put(LDAPConstants.USERNAME_LDAP_ATTRIBUTE, null);
+ DEFAULT_VALUES.put(LDAPConstants.USER_OBJECT_CLASSES, null);
+ DEFAULT_VALUES.put(LDAPConstants.USER_ACCOUNT_CONTROLS_AFTER_PASSWORD_UPDATE, "false");
+ DEFAULT_VALUES.put(LDAPConstants.EDIT_MODE, UserFederationProvider.EditMode.READ_ONLY.toString());
+
+ DEFAULT_VALUES.put(KerberosConstants.ALLOW_KERBEROS_AUTHENTICATION, "false");
+ DEFAULT_VALUES.put(KerberosConstants.KERBEROS_REALM, "KEYCLOAK.ORG");
+ DEFAULT_VALUES.put(KerberosConstants.SERVER_PRINCIPAL, "HTTP/localhost@KEYCLOAK.ORG");
+ URL keytabUrl = LDAPTestConfiguration.class.getResource("/kerberos/http.keytab");
+ String keyTabPath = new File(keytabUrl.getFile()).getAbsolutePath();
+ DEFAULT_VALUES.put(KerberosConstants.KEYTAB, keyTabPath);
+ DEFAULT_VALUES.put(KerberosConstants.DEBUG, "true");
+ DEFAULT_VALUES.put(KerberosConstants.ALLOW_PASSWORD_AUTHENTICATION, "true");
+ DEFAULT_VALUES.put(KerberosConstants.UPDATE_PROFILE_FIRST_LOGIN, "true");
+ DEFAULT_VALUES.put(KerberosConstants.USE_KERBEROS_FOR_PASSWORD_AUTHENTICATION, "false");
+ }
+
+ public static LDAPTestConfiguration readConfiguration(String connectionPropertiesLocation) {
+ LDAPTestConfiguration ldapTestConfiguration = new LDAPTestConfiguration();
+ ldapTestConfiguration.setConnectionPropertiesLocation(connectionPropertiesLocation);
+ ldapTestConfiguration.loadConnectionProperties();
+ return ldapTestConfiguration;
+ }
+
+ protected void loadConnectionProperties() {
+ Properties p = new Properties();
+ try {
+ log.info("Reading LDAP configuration from: " + connectionPropertiesLocation);
+ InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream(connectionPropertiesLocation);
+ p.load(is);
+ }
+ catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+
+ config = new HashMap<String, String>();
+ for (Map.Entry<String, String> property : PROP_MAPPINGS.entrySet()) {
+ String propertyName = property.getKey();
+ String configName = property.getValue();
+
+ String value = (String) p.get(configName);
+ if (value == null) {
+ value = DEFAULT_VALUES.get(propertyName);
+ }
+
+ config.put(propertyName, value);
+ }
+
+ startEmbeddedLdapLerver = Boolean.parseBoolean(p.getProperty("idm.test.ldap.start.embedded.ldap.server", "true"));
+ log.info("Start embedded server: " + startEmbeddedLdapLerver);
+ log.info("Read config: " + config);
+ }
+
+ public Map<String,String> getLDAPConfig() {
+ return config;
+ }
+
+ public void setConnectionPropertiesLocation(String connectionPropertiesLocation) {
+ this.connectionPropertiesLocation = connectionPropertiesLocation;
+ }
+
+ public boolean isStartEmbeddedLdapLerver() {
+ return startEmbeddedLdapLerver;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
new file mode 100644
index 0000000..ba5790d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/MailServerConfiguration.java
@@ -0,0 +1,11 @@
+package org.keycloak.testsuite.util;
+
+/**
+ *
+ * @author vramik
+ */
+public class MailServerConfiguration {
+ public static final String FROM = "server@mail.test";
+ public static final String HOST = "localhost";
+ public static final String PORT = "3025";
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
new file mode 100644
index 0000000..0ce7eda
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/util/Timer.java
@@ -0,0 +1,57 @@
+package org.keycloak.testsuite.util;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class Timer {
+
+ private static Long time;
+
+ private static final Map<String, List<Long>> stats = new HashMap<>();
+
+ public static void time() {
+ time = new Date().getTime();
+ }
+
+ public static void time(String operation) {
+ if (time == null) {
+ System.out.println(MessageFormat.format("Starting timer for operation {0}", operation));
+ time();
+ } else {
+ long timeOrig = time;
+ time();
+ logOperation(operation, time - timeOrig);
+ System.out.println(MessageFormat.format("Operation {0} took {1} ms", operation, time - timeOrig));
+ }
+ }
+
+ private static void logOperation(String operation, long delta) {
+ if (!stats.containsKey(operation)) {
+ stats.put(operation, new ArrayList<Long>());
+ }
+ stats.get(operation).add(delta);
+ }
+
+ public static void printStats() {
+ if (!stats.isEmpty()) {
+ System.out.println("OPERATION STATS:");
+ }
+ for (String op : stats.keySet()) {
+ long sum = 0;
+ for (Long t : stats.get(op)) {
+ sum += t;
+ }
+ System.out.println(MessageFormat.format("Operation {0} average time: {1,number,#} ms", op, sum / stats.get(op).size()));
+ }
+ stats.clear();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension b/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
new file mode 100644
index 0000000..88c25fa
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/main/resources/META-INF/services/org.jboss.arquillian.core.spi.LoadableExtension
@@ -0,0 +1,3 @@
+org.keycloak.testsuite.arquillian.KeycloakArquillianExtension
+!org.jboss.arquillian.container.impl.ContainerExtension
+org.keycloak.testsuite.arquillian.containers.MultipleContainersExtension
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
new file mode 100644
index 0000000..98aa62e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractAuthTest.java
@@ -0,0 +1,104 @@
+package org.keycloak.testsuite;
+
+import java.text.MessageFormat;
+import java.util.List;
+import org.jboss.arquillian.graphene.findby.FindByJQuery;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.keycloak.admin.client.resource.RealmResource;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.console.page.fragment.FlashMessage;
+import org.openqa.selenium.Cookie;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractAuthTest extends AbstractKeycloakTest {
+
+ @Page
+ protected AuthRealm testRealmPage;
+ @Page
+ protected OIDCLogin testRealmLoginPage;
+
+ protected UserRepresentation testUser;
+
+ @FindByJQuery(".alert")
+ protected FlashMessage flashMessage;
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation testRealmRep = new RealmRepresentation();
+ testRealmRep.setRealm(TEST);
+ testRealmRep.setEnabled(true);
+ testRealms.add(testRealmRep);
+ }
+
+ @Before
+ public void beforeAuthTest() {
+ testRealmLoginPage.setAuthRealm(testRealmPage);
+
+ testUser = createUserRepresentation("test", "test@email.test", "test", "user", true);
+ setPasswordFor(testUser, PASSWORD);
+
+ deleteAllCookiesForTestRealm();
+ }
+
+ public void createTestUserWithAdminClient() {
+ log.debug("creating test user");
+ String id = createUserAndResetPasswordWithAdminClient(testRealmResource(), testUser, PASSWORD);
+ testUser.setId(id);
+ }
+
+ public static UserRepresentation createUserRepresentation(String username, String email, String firstName, String lastName, boolean enabled) {
+ UserRepresentation user = new UserRepresentation();
+ user.setUsername(username);
+ user.setEmail(email);
+ user.setFirstName(firstName);
+ user.setLastName(lastName);
+ user.setEnabled(enabled);
+ return user;
+ }
+
+ public void deleteAllCookiesForTestRealm() {
+ testRealmPage.navigateTo();
+ log.debug("deleting cookies in test realm");
+ driver.manage().deleteAllCookies();
+ }
+
+ public void listCookies() {
+ log.info("LIST OF COOKIES: ");
+ for (Cookie c : driver.manage().getCookies()) {
+ log.info(MessageFormat.format(" {1} {2} {0}",
+ c.getName(), c.getDomain(), c.getPath(), c.getValue()));
+ }
+ }
+
+ public void assertFlashMessageSuccess() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+ }
+
+ public void assertFlashMessageDanger() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isDanger());
+ }
+
+ public void assertFlashMessageError() {
+ flashMessage.waitUntilPresent();
+ assertTrue(flashMessage.getText(), flashMessage.isError());
+ }
+
+ public RealmResource testRealmResource() {
+ return adminClient.realm(testRealmPage.getAuthRealm());
+ }
+
+}
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
new file mode 100644
index 0000000..3399115
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/AbstractKeycloakTest.java
@@ -0,0 +1,176 @@
+package org.keycloak.testsuite;
+
+import org.keycloak.testsuite.arquillian.TestContext;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.NotFoundException;
+import org.jboss.arquillian.container.test.api.RunAsClient;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.logging.Logger;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.runner.RunWith;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.RealmResource;
+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.openqa.selenium.WebDriver;
+import org.keycloak.testsuite.auth.page.AuthServer;
+import org.keycloak.testsuite.auth.page.AuthServerContextRoot;
+import static org.keycloak.testsuite.util.URLAssert.*;
+import org.keycloak.testsuite.auth.page.AuthRealm;
+import static org.keycloak.testsuite.auth.page.AuthRealm.ADMIN;
+import static org.keycloak.testsuite.auth.page.AuthRealm.MASTER;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.auth.page.login.OIDCLogin;
+import org.keycloak.testsuite.auth.page.login.UpdatePassword;
+import org.keycloak.testsuite.util.Timer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@RunWith(Arquillian.class)
+@RunAsClient
+public abstract class AbstractKeycloakTest {
+
+ protected Logger log = Logger.getLogger(this.getClass());
+
+ @ArquillianResource
+ protected SuiteContext suiteContext;
+
+ @ArquillianResource
+ protected TestContext testContext;
+
+ @ArquillianResource
+ protected Keycloak adminClient;
+
+ protected List<RealmRepresentation> testRealmReps;
+
+ @Drone
+ protected WebDriver driver;
+
+ @Page
+ protected AuthServerContextRoot authServerContextRootPage;
+ @Page
+ protected AuthServer authServerPage;
+
+ @Page
+ protected AuthRealm masterRealmPage;
+
+ @Page
+ protected Account accountPage;
+ @Page
+ protected OIDCLogin loginPage;
+ @Page
+ protected UpdatePassword updatePasswordPage;
+
+ protected UserRepresentation adminUser;
+
+ @Before
+ public void beforeAbstractKeycloakTest() {
+ adminUser = createAdminUserRepresentation();
+
+ setDefaultPageUriParameters();
+
+ driverSettings();
+
+ if (!suiteContext.isAdminPasswordUpdated()) {
+ updateMasterAdminPassword();
+ suiteContext.setAdminPasswordUpdated(true);
+ }
+
+ importTestRealms();
+ }
+
+ @After
+ public void afterAbstractKeycloakTest() {
+// removeTestRealms(); // keeping test realms after test to be able to inspect failures, instead deleting existing realms before import
+// keycloak.close(); // keeping admin connection open
+ Timer.printStats();
+ }
+
+ private void updateMasterAdminPassword() {
+ accountPage.navigateTo();
+ loginPage.form().login(ADMIN, ADMIN);
+ updatePasswordPage.updatePasswords(ADMIN, ADMIN);
+ assertCurrentUrlStartsWith(accountPage);
+ deleteAllCookiesForMasterRealm();
+ }
+
+ public void deleteAllCookiesForMasterRealm() {
+ masterRealmPage.navigateTo();
+ log.debug("deleting cookies in master realm");
+ driver.manage().deleteAllCookies();
+ }
+
+ protected void driverSettings() {
+ driver.manage().timeouts().pageLoadTimeout(5, TimeUnit.SECONDS);
+ driver.manage().timeouts().implicitlyWait(3, TimeUnit.SECONDS);
+ driver.manage().timeouts().setScriptTimeout(3, TimeUnit.SECONDS);
+ driver.manage().window().maximize();
+ }
+
+ public void setDefaultPageUriParameters() {
+ masterRealmPage.setAuthRealm(MASTER);
+ loginPage.setAuthRealm(MASTER);
+ }
+
+ public abstract void addTestRealms(List<RealmRepresentation> testRealms);
+
+ private void addTestRealms() {
+ log.debug("loading test realms");
+ if (testRealmReps == null) {
+ testRealmReps = new ArrayList<>();
+ }
+ if (testRealmReps.isEmpty()) {
+ addTestRealms(testRealmReps);
+ }
+ }
+
+ public void importTestRealms() {
+ addTestRealms();
+ log.info("importing test realms");
+ for (RealmRepresentation testRealm : testRealmReps) {
+ importRealm(testRealm);
+ }
+ }
+
+ public void removeTestRealms() {
+ log.info("removing test realms");
+ for (RealmRepresentation testRealm : testRealmReps) {
+ removeRealm(testRealm);
+ }
+ }
+
+ private UserRepresentation createAdminUserRepresentation() {
+ UserRepresentation adminUserRep = new UserRepresentation();
+ adminUserRep.setUsername(ADMIN);
+ setPasswordFor(adminUserRep, ADMIN);
+ return adminUserRep;
+ }
+
+ public void importRealm(RealmRepresentation realm) {
+ log.debug("importing realm: " + realm.getRealm());
+ try { // TODO - figure out a way how to do this without try-catch
+ RealmResource realmResource = adminClient.realms().realm(realm.getRealm());
+ RealmRepresentation rRep = realmResource.toRepresentation();
+ log.debug("realm already exists on server, re-importing");
+ realmResource.remove();
+ } catch (NotFoundException nfe) {
+ // expected when realm does not exist
+ }
+ adminClient.realms().create(realm);
+ }
+
+ public void removeRealm(RealmRepresentation realm) {
+ adminClient.realms().realm(realm.getRealm()).remove();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java
new file mode 100644
index 0000000..6a7a4c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AbstractAccountManagementTest.java
@@ -0,0 +1,31 @@
+package org.keycloak.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.testsuite.AbstractAuthTest;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.account.AccountManagement;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractAccountManagementTest extends AbstractAuthTest {
+
+ @Page
+ protected AccountManagement testRealmAccountManagementPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(TEST);
+ testRealmAccountManagementPage.setAuthRealm(TEST);
+ }
+
+ @Before
+ public void beforeAbstractAccountTest() {
+ // make user test user exists in test realm
+ createTestUserWithAdminClient();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
new file mode 100644
index 0000000..e509276
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/AccountTest.java
@@ -0,0 +1,80 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.After;
+import org.junit.Test;
+
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.keycloak.testsuite.auth.page.account.Account;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class AccountTest extends AbstractAccountManagementTest {
+
+ private static final String UPDATED_EMAIL = "new-name@email.test";
+ private static final String NEW_FIRST_NAME = "John";
+ private static final String NEW_LAST_NAME = "Smith";
+
+ @Page
+ private Account testRealmAccountPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmAccountPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeAccountTest() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ }
+
+ @After
+ public void afterAccountTest() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmAccountManagementPage.signOut();
+ }
+
+ @Test
+ public void editAccount() {
+ testRealmAccountManagementPage.account();
+ assertEquals(testRealmAccountPage.getUsername(), testUser.getUsername());
+
+ testRealmAccountPage.setEmail(UPDATED_EMAIL);
+ testRealmAccountPage.setFirstName(NEW_FIRST_NAME);
+ testRealmAccountPage.setLastName(NEW_LAST_NAME);
+ testRealmAccountPage.save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser);
+
+ testRealmAccountManagementPage.account();
+ assertEquals(testRealmAccountPage.getEmail(), UPDATED_EMAIL);
+ assertEquals(testRealmAccountPage.getFirstName(), NEW_FIRST_NAME);
+ assertEquals(testRealmAccountPage.getLastName(), NEW_LAST_NAME);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java
new file mode 100644
index 0000000..5787903
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ChangePasswordTest.java
@@ -0,0 +1,67 @@
+package org.keycloak.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.account.ChangePassword;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class ChangePasswordTest extends AbstractAccountManagementTest {
+
+ private static final String NEW_PASSWORD = "newpassword";
+ private static final String WRONG_PASSWORD = "wrongpassword";
+
+ @Page
+ private ChangePassword testRealmChangePasswordPage;
+
+ private String correctPassword;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmChangePasswordPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeChangePasswordTest() {
+ correctPassword = getPasswordOf(testUser);
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ testRealmAccountManagementPage.password();
+ }
+
+ @Test
+ public void invalidChangeAttempts() {
+ testRealmChangePasswordPage.save();
+ assertFlashMessageError();
+
+ testRealmChangePasswordPage.changePasswords(WRONG_PASSWORD, NEW_PASSWORD, NEW_PASSWORD);
+ assertFlashMessageError();
+
+ testRealmChangePasswordPage.changePasswords(correctPassword, NEW_PASSWORD, NEW_PASSWORD + "-mismatch");
+ assertFlashMessageError();
+ }
+
+ @Test
+ public void successfulChangeAttempts() {
+ // change password successfully
+ testRealmChangePasswordPage.changePasswords(correctPassword, NEW_PASSWORD, NEW_PASSWORD);
+ assertFlashMessageSuccess();
+
+ // login using new password
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser.getUsername(), NEW_PASSWORD);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+
+ // change password back
+ testRealmAccountManagementPage.password();
+ testRealmChangePasswordPage.changePasswords(NEW_PASSWORD, correctPassword, correctPassword);
+ assertFlashMessageSuccess();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java
new file mode 100644
index 0000000..5297038
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/RegistrationTest.java
@@ -0,0 +1,132 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.login.Registration;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.findUserByUsername;
+import static org.keycloak.testsuite.admin.Users.getPasswordOf;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class RegistrationTest extends AbstractAccountManagementTest {
+
+ @Page
+ private Registration testRealmRegistrationPage;
+
+ private UserRepresentation newUser;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmRegistrationPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeUserRegistration() {
+ // enable user registration in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setRegistrationAllowed(true);
+ testRealmResource().update(testRealmRep);
+
+ newUser = createUserRepresentation("new_user", "new_user@email.test", "new", "user", true);
+ setPasswordFor(newUser, PASSWORD);
+
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().register();
+ }
+
+ public void assertUserExistsWithAdminClient(UserRepresentation user) {
+ assertNotNull(findUserByUsername(testRealmResource(), user.getUsername()));
+ }
+
+ public void assertUserDoesntExistWithAdminClient(UserRepresentation user) {
+ assertNull(findUserByUsername(testRealmResource(), user.getUsername()));
+ }
+
+ public void assertMessageAttributeMissing(String attributeName) {
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .contains("Please specify " + attributeName + "."));
+ }
+
+ @Test
+ public void successfulRegistration() {
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUser);
+ }
+
+ @Test
+ public void invalidEmail() {
+ newUser.setEmail("invalid.email.value");
+ testRealmRegistrationPage.register(newUser);
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .equals("Invalid email address."));
+ assertUserDoesntExistWithAdminClient(newUser);
+ }
+
+ @Test
+ public void emptyAttributes() {
+ UserRepresentation newUserEmpty = new UserRepresentation(); // empty user attributes
+
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("username");
+
+ newUserEmpty.setUsername(newUser.getUsername());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("first name");
+
+ newUserEmpty.setFirstName(newUser.getFirstName());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("last name");
+
+ newUserEmpty.setLastName(newUser.getLastName());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("email");
+
+ newUserEmpty.setEmail(newUser.getEmail());
+ testRealmRegistrationPage.register(newUserEmpty);
+ assertMessageAttributeMissing("password");
+
+ setPasswordFor(newUserEmpty, getPasswordOf(newUser));
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUserEmpty);
+ }
+
+ @Test
+ public void notMatchingPasswords() {
+ testRealmRegistrationPage.setValues(newUser, "not-matching-password");
+ testRealmRegistrationPage.submit();
+ assertTrue(testRealmRegistrationPage.getFeedbackText()
+ .equals("Password confirmation doesn't match."));
+
+ testRealmRegistrationPage.register(newUser);
+ assertUserExistsWithAdminClient(newUser);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java
new file mode 100644
index 0000000..95bff41
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/ResetCredentialsTest.java
@@ -0,0 +1,101 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.login.ResetCredentials;
+import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
+import org.keycloak.testsuite.util.MailServer;
+import org.keycloak.testsuite.util.MailServerConfiguration;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class ResetCredentialsTest extends AbstractAccountManagementTest {
+
+ @Page
+ private ResetCredentials testRealmResetCredentialsPage;
+
+ private static boolean init = false;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmResetCredentialsPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeResetCredentials() {
+ // enable reset credentials and configure smpt server in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setSmtpServer(suiteContext.getSmtpServer());
+ testRealmRep.setResetPasswordAllowed(true);
+ testRealmResource().update(testRealmRep);
+
+ if (!init) {
+ init = true;
+ MailServer.start();
+ MailServer.createEmailAccount(testUser.getEmail(), "password");
+ }
+
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().forgotPassword();
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ MailServer.stop();
+ }
+
+ @Test
+ public void resetCredentialsWithEmail() {
+ testRealmResetCredentialsPage.resetCredentials(testUser.getEmail());
+ resetCredentialsAndLoginWithNewPassword();
+ }
+
+ @Test
+ public void resetCredentialsWithUsername() {
+ testRealmResetCredentialsPage.resetCredentials(testUser.getUsername());
+ resetCredentialsAndLoginWithNewPassword();
+ }
+
+ private void resetCredentialsAndLoginWithNewPassword() {
+ assertEquals("You should receive an email shortly with further instructions.",
+ testRealmResetCredentialsPage.getFeedbackText());
+
+ String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, testUser.getEmail(),
+ "Someone just requested to change your Test account's credentials.");
+
+ log.info("navigating to " + url);
+ driver.navigate().to(url);
+ assertCurrentUrlStartsWith(testRealmResetCredentialsPage);
+ testRealmResetCredentialsPage.updatePassword("newPassword");
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login("test", "newPassword");
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java
new file mode 100644
index 0000000..59e08cc
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/account/VerifyEmailTest.java
@@ -0,0 +1,88 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.account;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.AfterClass;
+import static org.junit.Assert.assertEquals;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.login.VerifyEmail;
+import static org.keycloak.testsuite.util.MailAssert.assertEmailAndGetUrl;
+import org.keycloak.testsuite.util.MailServer;
+import org.keycloak.testsuite.util.MailServerConfiguration;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+
+/**
+ *
+ * @author vramik
+ */
+public class VerifyEmailTest extends AbstractAccountManagementTest {
+
+ @Page
+ private VerifyEmail testRealmVerifyEmailPage;
+
+ private static boolean init = false;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmVerifyEmailPage.setAuthRealm(testRealmPage);
+ }
+
+ @Before
+ public void beforeVerifyEmail() {
+ // enable verify email and configure smpt server in test realm
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ testRealmRep.setSmtpServer(suiteContext.getSmtpServer());
+ testRealmRep.setVerifyEmail(true);
+ testRealmResource().update(testRealmRep);
+
+ if (!init) {
+ init = true;
+ MailServer.start();
+ MailServer.createEmailAccount(testUser.getEmail(), "password");
+ }
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ MailServer.stop();
+ }
+
+ @Test
+ public void verifyEmail() {
+ testRealmAccountManagementPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+
+ assertEquals("You need to verify your email address to activate your account.",
+ testRealmVerifyEmailPage.getFeedbackText());
+
+ String url = assertEmailAndGetUrl(MailServerConfiguration.FROM, testUser.getEmail(),
+ "Someone has created a Test account with this email address.");
+
+ log.info("navigating to " + url);
+ driver.navigate().to(url);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ testRealmAccountManagementPage.signOut();
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountManagementPage);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
new file mode 100644
index 0000000..73ea7a6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractAdapterTest.java
@@ -0,0 +1,139 @@
+package org.keycloak.testsuite.adapter;
+
+import java.io.IOException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.List;
+import org.apache.commons.io.IOUtils;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.Archive;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.arquillian.ContainersTestEnricher;
+import org.keycloak.testsuite.arquillian.annotation.AppServerContainer;
+import org.keycloak.testsuite.adapter.page.AppServerContextRoot;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+@AppServerContainer
+public abstract class AbstractAdapterTest extends AbstractAuthTest {
+
+ @Page
+ protected AppServerContextRoot appServerContextRootPage;
+
+ public static final String JBOSS_DEPLOYMENT_STRUCTURE_XML = "jboss-deployment-structure.xml";
+ public static final URL jbossDeploymentStructure = AbstractServletsAdapterTest.class
+ .getResource("/adapter-test/" + JBOSS_DEPLOYMENT_STRUCTURE_XML);
+ public static final String TOMCAT_CONTEXT_XML = "context.xml";
+ public static final URL tomcatContext = AbstractServletsAdapterTest.class
+ .getResource("/adapter-test/" + TOMCAT_CONTEXT_XML);
+
+ @Override
+ public void addTestRealms(List<RealmRepresentation> testRealms) {
+ addAdapterTestRealms(testRealms);
+ for (RealmRepresentation tr : testRealms) {
+ log.info("Setting redirect-uris in test realm '" + tr.getRealm() + "' as " + (isRelative() ? "" : "non-") + "relative");
+
+ modifyClientRedirectUris(tr, "http://localhost:8080", "");
+ modifyClientUrls(tr, "http://localhost:8080", "");
+
+ if (isRelative()) {
+ modifyClientRedirectUris(tr, appServerContextRootPage.toString(), "");
+ modifyClientUrls(tr, appServerContextRootPage.toString(), "");
+ modifyClientWebOrigins(tr, "8080", System.getProperty("auth.server.http.port", null));
+ } else {
+ modifyClientRedirectUris(tr, "^(/.*/\\*)", appServerContextRootPage.toString() + "$1");
+ modifyClientUrls(tr, "^(/.*)", appServerContextRootPage.toString() + "$1");
+ }
+ }
+ }
+
+ public abstract void addAdapterTestRealms(List<RealmRepresentation> testRealms);
+
+ public boolean isRelative() {
+ return ContainersTestEnricher.isRelative(this.getClass());
+ }
+
+ protected void modifyClientRedirectUris(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ List<String> redirectUris = client.getRedirectUris();
+ if (redirectUris != null) {
+ List<String> newRedirectUris = new ArrayList<>();
+ for (String uri : redirectUris) {
+ newRedirectUris.add(uri.replaceAll(regex, replacement));
+ }
+ client.setRedirectUris(newRedirectUris);
+ }
+ }
+ }
+
+ protected void modifyClientUrls(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ String baseUrl = client.getBaseUrl();
+ if (baseUrl != null) {
+ client.setBaseUrl(baseUrl.replaceAll(regex, replacement));
+ }
+ String adminUrl = client.getAdminUrl();
+ if (adminUrl != null) {
+ client.setAdminUrl(adminUrl.replaceAll(regex, replacement));
+ }
+ }
+ }
+
+ protected void modifyClientWebOrigins(RealmRepresentation realm, String regex, String replacement) {
+ for (ClientRepresentation client : realm.getClients()) {
+ List<String> webOrigins = client.getWebOrigins();
+ if (webOrigins != null) {
+ List<String> newWebOrigins = new ArrayList<>();
+ for (String uri : webOrigins) {
+ newWebOrigins.add(uri.replaceAll(regex, replacement));
+ }
+ client.setWebOrigins(newWebOrigins);
+ }
+ }
+ }
+
+ /**
+ * Modifies baseUrl, adminUrl and redirectUris for client based on real
+ * deployment url of the app.
+ *
+ * @param realm
+ * @param clientId
+ * @param deploymentUrl
+ */
+ protected void fixClientUrisUsingDeploymentUrl(RealmRepresentation realm, String clientId, String deploymentUrl) {
+ for (ClientRepresentation client : realm.getClients()) {
+ if (clientId.equals(client.getClientId())) {
+ if (client.getBaseUrl() != null) {
+ client.setBaseUrl(deploymentUrl);
+ }
+ if (client.getAdminUrl() != null) {
+ client.setAdminUrl(deploymentUrl);
+ }
+ List<String> redirectUris = client.getRedirectUris();
+ if (redirectUris != null) {
+ List<String> newRedirectUris = new ArrayList<>();
+ for (String uri : redirectUris) {
+ newRedirectUris.add(deploymentUrl + "/*");
+ }
+ client.setRedirectUris(newRedirectUris);
+ }
+ }
+ }
+ }
+
+ public static void addContextXml(Archive archive, String contextPath) {
+ try {
+ String contextXmlContent = IOUtils.toString(tomcatContext.openStream())
+ .replace("%CONTEXT_PATH%", contextPath);
+ archive.add(new StringAsset(contextXmlContent), "/META-INF/context.xml");
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
new file mode 100644
index 0000000..defb866
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractExampleAdapterTest.java
@@ -0,0 +1,62 @@
+package org.keycloak.testsuite.adapter;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.file.Paths;
+
+import org.apache.commons.io.IOUtils;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.asset.StringAsset;
+import org.jboss.shrinkwrap.api.spec.JavaArchive;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractExampleAdapterTest extends AbstractAdapterTest {
+
+ public static final String EXAMPLES_HOME;
+ public static final String EXAMPLES_VERSION_SUFFIX;
+ public static final String EXAMPLES_HOME_DIR;
+ public static final String EXAMPLES_WEB_XML;
+
+ static {
+ EXAMPLES_HOME = System.getProperty("examples.home", null);
+ Assert.assertNotNull("Property ${examples.home} must bet set.", EXAMPLES_HOME);
+ System.out.println(EXAMPLES_HOME);
+
+ EXAMPLES_VERSION_SUFFIX = System.getProperty("examples.version.suffix", null);
+ Assert.assertNotNull("Property ${examples.version.suffix} must bet set.", EXAMPLES_VERSION_SUFFIX);
+ System.out.println(EXAMPLES_VERSION_SUFFIX);
+
+ EXAMPLES_HOME_DIR = EXAMPLES_HOME + "/keycloak-examples-" + EXAMPLES_VERSION_SUFFIX;
+
+ EXAMPLES_WEB_XML = EXAMPLES_HOME + "/web.xml";
+ }
+
+ protected static WebArchive exampleDeployment(String name) throws IOException {
+ return ShrinkWrap.createFromZipFile(WebArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".war"))
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
+ }
+
+ protected static WebArchive exampleDeployment(String name, String contextPath) throws IOException {
+ URL webXML = Paths.get(EXAMPLES_WEB_XML).toUri().toURL();
+ String webXmlContent = IOUtils.toString(webXML.openStream())
+ .replace("%CONTEXT_PATH%", contextPath);
+ WebArchive webArchive = ShrinkWrap.createFromZipFile(WebArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".war"))
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML)
+ .add(new StringAsset(webXmlContent), "/WEB-INF/web.xml");
+ return webArchive;
+ }
+
+ protected static JavaArchive exampleJarDeployment(String name) {
+ return ShrinkWrap.createFromZipFile(JavaArchive.class,
+ new File(EXAMPLES_HOME + "/" + name + "-" + EXAMPLES_VERSION_SUFFIX + ".jar"));
+ }
+
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
new file mode 100644
index 0000000..7dc58c0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/AbstractServletsAdapterTest.java
@@ -0,0 +1,45 @@
+package org.keycloak.testsuite.adapter;
+
+import java.net.URL;
+import java.util.List;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.*;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+
+public abstract class AbstractServletsAdapterTest extends AbstractAdapterTest {
+
+ protected static WebArchive servletDeployment(String name, Class... servletClasses) {
+ return servletDeployment(name, "keycloak.json", servletClasses);
+ }
+
+ protected static WebArchive servletDeployment(String name, String adapterConfig, Class... servletClasses) {
+ String webInfPath = "/adapter-test/" + name + "/WEB-INF/";
+
+ URL keycloakJSON = AbstractServletsAdapterTest.class.getResource(webInfPath + adapterConfig);
+ URL webXML = AbstractServletsAdapterTest.class.getResource(webInfPath + "web.xml");
+
+ WebArchive deployment = ShrinkWrap.create(WebArchive.class, name + ".war")
+ .addClasses(servletClasses)
+ .addAsWebInfResource(webXML, "web.xml")
+ .addAsWebInfResource(keycloakJSON, "keycloak.json")
+ .addAsWebInfResource(jbossDeploymentStructure, JBOSS_DEPLOYMENT_STRUCTURE_XML);
+
+ addContextXml(deployment, name);
+
+ return deployment;
+ }
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(loadRealm("/adapter-test/demorealm.json"));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java
new file mode 100644
index 0000000..8a592d9
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractBasicAuthExampleAdapterTest.java
@@ -0,0 +1,68 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.core.Response;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import org.keycloak.testsuite.adapter.page.BasicAuthExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
+
+public abstract class AbstractBasicAuthExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private BasicAuthExample basicAuthExample;
+
+ @Deployment(name = BasicAuthExample.DEPLOYMENT_NAME)
+ private static WebArchive basicAuthExample() throws IOException {
+ return exampleDeployment("examples-basicauth");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(loadRealm(new File(EXAMPLES_HOME_DIR + "/basic-auth/basicauthrealm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(EXAMPLE);
+ }
+
+ @Test
+ public void testBasicAuthExample() {
+ String value = "hello";
+ Client client = ClientBuilder.newClient();
+
+ Response response = client.target(basicAuthExample
+ .setTemplateValues("admin", "password", value).buildUri()).request().get();
+ assertEquals(200, response.getStatus());
+ assertEquals(value, response.readEntity(String.class));
+ response.close();
+
+ response = client.target(basicAuthExample
+ .setTemplateValues("invalid-user", "password", value).buildUri()).request().get();
+ assertEquals(401, response.getStatus());
+ assertTrue(response.readEntity(String.class).contains("Unauthorized"));
+ response.close();
+
+ response = client.target(basicAuthExample
+ .setTemplateValues("admin", "invalid-password", value).buildUri()).request().get();
+ assertEquals(401, response.getStatus());
+ assertTrue(response.readEntity(String.class).contains("Unauthorized"));
+ response.close();
+
+ client.close();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java
new file mode 100644
index 0000000..72db4bb
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractCorsExampleAdapterTest.java
@@ -0,0 +1,105 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import org.keycloak.testsuite.adapter.page.AngularCorsProductExample;
+import org.keycloak.testsuite.adapter.page.CorsDatabaseServiceExample;
+import org.keycloak.testsuite.arquillian.jira.Jira;
+import org.keycloak.testsuite.auth.page.account.Account;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ * Created by fkiss.
+ */
+public abstract class AbstractCorsExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ public static final String CORS = "cors";
+
+ @Page
+ private AngularCorsProductExample angularCorsProductExample;
+
+ @Page
+ private Account testRealmAccount;
+
+ @Deployment(name = AngularCorsProductExample.DEPLOYMENT_NAME)
+ private static WebArchive angularCorsProductExample() throws IOException {
+ return exampleDeployment(AngularCorsProductExample.DEPLOYMENT_NAME, "angular-cors-product");
+ }
+
+ @Deployment(name = CorsDatabaseServiceExample.DEPLOYMENT_NAME)
+ private static WebArchive corsDatabaseServiceExample() throws IOException {
+ return exampleDeployment("database-service");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(
+ loadRealm(new File(EXAMPLES_HOME_DIR + "/cors/cors-realm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(CORS);
+ testRealmLoginPage.setAuthRealm(CORS);
+ testRealmAccount.setAuthRealm(CORS);
+ }
+
+ @Before
+ public void beforeDemoExampleTest() {
+ angularCorsProductExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ }
+
+ @Jira("KEYCLOAK-1546")
+ @Test
+ public void angularCorsProductTest() {
+ angularCorsProductExample.navigateTo();
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(angularCorsProductExample);
+ angularCorsProductExample.reloadData();
+ Assert.assertTrue(driver.getPageSource().contains("Product Listing"));
+ Assert.assertTrue(driver.getPageSource().contains("iphone"));
+ Assert.assertTrue(driver.getPageSource().contains("ipad"));
+ Assert.assertTrue(driver.getPageSource().contains("ipod"));
+
+ angularCorsProductExample.loadRoles();
+ Assert.assertTrue(driver.getPageSource().contains("Role Listing"));
+ Assert.assertTrue(driver.getPageSource().contains("user"));
+
+ angularCorsProductExample.addRole();
+ Assert.assertTrue(driver.getPageSource().contains("stuff"));
+
+ angularCorsProductExample.deleteRole();
+ Assert.assertFalse(driver.getPageSource().contains("stuff"));
+
+ angularCorsProductExample.loadAvailableSocialProviders();
+ Assert.assertTrue(driver.getPageSource().contains("Available social providers"));
+ Assert.assertTrue(driver.getPageSource().contains("twitter"));
+ Assert.assertTrue(driver.getPageSource().contains("google"));
+ Assert.assertTrue(driver.getPageSource().contains("linkedin"));
+ Assert.assertTrue(driver.getPageSource().contains("facebook"));
+ Assert.assertTrue(driver.getPageSource().contains("stackoverflow"));
+ Assert.assertTrue(driver.getPageSource().contains("github"));
+
+ angularCorsProductExample.loadPublicRealmInfo();
+ Assert.assertTrue(driver.getPageSource().contains("Realm name: cors"));
+
+ angularCorsProductExample.loadVersion();
+ Assert.assertTrue(driver.getPageSource().contains("Keycloak version: "));
+
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
new file mode 100644
index 0000000..d31d390
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractDemoExampleAdapterTest.java
@@ -0,0 +1,157 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.*;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account.Account;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.page.CustomerPortalExample;
+import org.keycloak.testsuite.adapter.page.DatabaseServiceExample;
+import org.keycloak.testsuite.adapter.page.ProductPortalExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import org.openqa.selenium.By;
+
+public abstract class AbstractDemoExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private CustomerPortalExample customerPortalExample;
+ @Page
+ private ProductPortalExample productPortalExample;
+ @Page
+ private DatabaseServiceExample databaseServiceExample;
+
+ @Page
+ private Account testRealmAccount;
+
+ @Deployment(name = CustomerPortalExample.DEPLOYMENT_NAME)
+ private static WebArchive customerPortalExample() throws IOException {
+ return exampleDeployment(CustomerPortalExample.DEPLOYMENT_NAME);
+ }
+
+ @Deployment(name = ProductPortalExample.DEPLOYMENT_NAME)
+ private static WebArchive productPortalExample() throws IOException {
+ return exampleDeployment(ProductPortalExample.DEPLOYMENT_NAME);
+ }
+
+ @Deployment(name = DatabaseServiceExample.DEPLOYMENT_NAME)
+ private static WebArchive databaseServiceExample() throws IOException {
+ return exampleDeployment("database-service");
+ }
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ testRealms.add(
+ loadRealm(new File(EXAMPLES_HOME_DIR + "/preconfigured-demo/testrealm.json")));
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ testRealmLoginPage.setAuthRealm(DEMO);
+ testRealmAccount.setAuthRealm(DEMO);
+ }
+
+ @Before
+ public void beforeDemoExampleTest() {
+ customerPortalExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ productPortalExample.navigateTo();
+ driver.manage().deleteAllCookies();
+ }
+
+ @Test
+ public void customerPortalListingTest() {
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+ customerPortalExample.waitForCustomerListingHeader();
+
+ Assert.assertTrue(driver.getPageSource().contains("Username: bburke@redhat.com"));
+ Assert.assertTrue(driver.getPageSource().contains("Bill Burke"));
+ Assert.assertTrue(driver.getPageSource().contains("Stian Thorgersen"));
+ }
+
+ @Test
+ public void customerPortalSessionTest() {
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerSession();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+
+ customerPortalExample.waitForCustomerSessionHeader();
+ Assert.assertTrue(driver.getPageSource().contains("You visited this page"));
+ }
+
+ @Test
+ public void productPortalListingTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+
+ Assert.assertTrue(driver.getPageSource().contains("iphone"));
+ Assert.assertTrue(driver.getPageSource().contains("ipad"));
+ Assert.assertTrue(driver.getPageSource().contains("ipod"));
+
+ productPortalExample.goToCustomers();
+ }
+
+ @Test
+ public void goToProductPortalWithOneLoginTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+ productPortalExample.goToCustomers();
+
+ assertCurrentUrlStartsWith(customerPortalExample);
+ customerPortalExample.customerListing();
+ customerPortalExample.goToProducts();
+ assertCurrentUrlStartsWith(productPortalExample);
+ }
+
+ @Test
+ public void logoutFromAllAppsTest() {
+
+ productPortalExample.navigateTo();
+ productPortalExample.productListing();
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.waitForProductListingHeader();
+
+ productPortalExample.logOut();
+ assertCurrentUrlStartsWith(productPortalExample);
+ productPortalExample.productListing();
+
+ customerPortalExample.navigateTo();
+ customerPortalExample.customerListing();
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+
+ customerPortalExample.logOut();
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java
new file mode 100644
index 0000000..647b15b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractFuseExampleAdapterTest.java
@@ -0,0 +1,131 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.util.List;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.auth.page.account.Account;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.adapter.AbstractExampleAdapterTest.EXAMPLES_HOME_DIR;
+import org.keycloak.testsuite.adapter.page.fuse.AdminInterface;
+import org.keycloak.testsuite.adapter.page.fuse.CustomerListing;
+import org.keycloak.testsuite.adapter.page.fuse.CustomerPortalFuseExample;
+import org.keycloak.testsuite.adapter.page.fuse.ProductPortalFuseExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractFuseExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ protected CustomerPortalFuseExample customerPortal;
+ @Page
+ protected CustomerListing customerListing;
+ @Page
+ protected AdminInterface adminInterface;
+
+ @Page
+ protected ProductPortalFuseExample productPortal;
+
+ @Page
+ protected Account testRealmAccount;
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation fureRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/fuse/testrealm.json"));
+ testRealms.add(fureRealm);
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(DEMO);
+ testRealmLoginPage.setAuthRealm(DEMO);
+ testRealmAccount.setAuthRealm(DEMO);
+ }
+
+ // no Arquillian deployments - examples already installed by maven
+
+ @Test
+ public void testCustomerListingAndAccountManagement() {
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickCustomerListingLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(customerListing);
+
+ String src = driver.getPageSource();
+ assertTrue(src.contains("Username: bburke@redhat.com")
+ && src.contains("Bill Burke")
+ && src.contains("Stian Thorgersen")
+ );
+
+ // account mgmt
+ customerListing.clickAccountManagement();
+
+ assertCurrentUrlStartsWith(testRealmAccount);
+ assertEquals(testRealmAccount.getUsername(), "bburke@redhat.com");
+
+ driver.navigate().back();
+ customerListing.clickLogOut();
+
+ // assert user not logged in
+ customerPortal.clickCustomerListingLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ }
+
+ @Test
+ public void testAdminInterface() {
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickAdminInterfaceLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("admin", "password");
+ assertCurrentUrlStartsWith(adminInterface);
+ assertTrue(driver.getPageSource().contains("Hello admin!"));
+
+ customerListing.navigateTo();
+ customerListing.clickLogOut();
+ pause(500);
+ assertCurrentUrlStartsWith(customerPortal);
+
+ customerPortal.clickAdminInterfaceLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(adminInterface);
+ assertTrue(driver.getPageSource().contains("Status code is 403"));
+ }
+
+ @Test
+ public void testProductPortal() {
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlStartsWith(productPortal);
+
+ assertTrue(productPortal.getProduct1UnsecuredText().contains("401: Unauthorized"));
+ assertTrue(productPortal.getProduct1SecuredText().contains("Product received: id=1"));
+ assertTrue(productPortal.getProduct2SecuredText().contains("Product received: id=2"));
+
+ productPortal.clickLogOutLink();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
new file mode 100644
index 0000000..61d8efa
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/example/AbstractJSConsoleExampleAdapterTest.java
@@ -0,0 +1,133 @@
+package org.keycloak.testsuite.adapter.example;
+
+import org.keycloak.testsuite.adapter.AbstractExampleAdapterTest;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.representations.idm.RealmRepresentation;
+import static org.keycloak.testsuite.util.IOUtil.loadRealm;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.keycloak.testsuite.adapter.page.JSConsoleExample;
+import static org.keycloak.testsuite.auth.page.AuthRealm.EXAMPLE;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlDoesntStartWith;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+
+public abstract class AbstractJSConsoleExampleAdapterTest extends AbstractExampleAdapterTest {
+
+ @Page
+ private JSConsoleExample jsConsoleExample;
+
+ public static int TOKEN_LIFESPAN_LEEWAY = 3; // seconds
+
+ @Deployment(name = JSConsoleExample.DEPLOYMENT_NAME)
+ private static WebArchive jsConsoleExample() throws IOException {
+ return exampleDeployment(JSConsoleExample.CLIENT_ID);
+ }
+
+ @Override
+ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
+ RealmRepresentation jsConsoleRealm = loadRealm(new File(EXAMPLES_HOME_DIR + "/js-console/example-realm.json"));
+
+ fixClientUrisUsingDeploymentUrl(jsConsoleRealm,
+ JSConsoleExample.CLIENT_ID, jsConsoleExample.buildUri().toASCIIString());
+
+ jsConsoleRealm.setAccessTokenLifespan(30 + TOKEN_LIFESPAN_LEEWAY); // seconds
+
+ testRealms.add(jsConsoleRealm);
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(EXAMPLE);
+ }
+
+ @Test
+ public void testJSConsoleAuth() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ pause(1000);
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "invalid-password");
+ assertCurrentUrlDoesntStartWith(jsConsoleExample);
+
+ testRealmLoginPage.form().login("invalid-user", "password");
+ assertCurrentUrlDoesntStartWith(jsConsoleExample);
+
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Init Success (Authenticated)"));
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ pause(1000);
+
+ jsConsoleExample.logOut();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Init Success (Not Authenticated)"));
+ }
+
+ @Test
+ public void testRefreshToken() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ }
+
+ @Test
+ public void testRefreshTokenIfUnder30s() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.refreshToken();
+ assertTrue(driver.getPageSource().contains("Failed to refresh token"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.refreshTokenIfUnder30s();
+ assertTrue(driver.getPageSource().contains("Token not refreshed, valid for"));
+
+ pause((TOKEN_LIFESPAN_LEEWAY + 2) * 1000);
+
+ jsConsoleExample.refreshTokenIfUnder30s();
+ assertTrue(driver.getPageSource().contains("Auth Refresh Success"));
+ }
+
+ @Test
+ public void testGetProfile() {
+ jsConsoleExample.navigateTo();
+ assertCurrentUrlStartsWith(jsConsoleExample);
+
+ jsConsoleExample.getProfile();
+ assertTrue(driver.getPageSource().contains("Failed to load profile"));
+
+ jsConsoleExample.logIn();
+ testRealmLoginPage.form().login("user", "password");
+ assertCurrentUrlStartsWith(jsConsoleExample);
+ assertTrue(driver.getPageSource().contains("Auth Success"));
+
+ jsConsoleExample.getProfile();
+ assertTrue(driver.getPageSource().contains("Failed to load profile"));
+ assertTrue(driver.getPageSource().contains("\"username\": \"user\""));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
new file mode 100644
index 0000000..38e47b7
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractDemoServletsAdapterTest.java
@@ -0,0 +1,386 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
+import java.net.URI;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.TimeUnit;
+import javax.ws.rs.client.Client;
+import javax.ws.rs.client.ClientBuilder;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.client.WebTarget;
+import javax.ws.rs.core.Form;
+import javax.ws.rs.core.HttpHeaders;
+import javax.ws.rs.core.Response;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.Assert;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.Version;
+import org.keycloak.constants.AdapterConstants;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.page.CustomerDb;
+import org.keycloak.testsuite.adapter.page.CustomerDbErrorPage;
+import org.keycloak.testsuite.adapter.page.CustomerPortal;
+import org.keycloak.testsuite.adapter.page.InputPortal;
+import org.keycloak.testsuite.adapter.page.ProductPortal;
+import org.keycloak.testsuite.adapter.page.SecurePortal;
+import org.keycloak.testsuite.arquillian.jira.Jira;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import org.keycloak.util.BasicAuthHelper;
+import org.keycloak.util.Time;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractDemoServletsAdapterTest extends AbstractServletsAdapterTest {
+
+ @Page
+ private CustomerPortal customerPortal;
+ @Page
+ private SecurePortal securePortal;
+ @Page
+ private CustomerDb customerDb;
+ @Page
+ private CustomerDbErrorPage customerDbErrorPage;
+ @Page
+ private ProductPortal productPortal;
+ @Page
+ private InputPortal inputPortal;
+
+ @Deployment(name = CustomerPortal.DEPLOYMENT_NAME)
+ protected static WebArchive customerPortal() {
+ return servletDeployment(CustomerPortal.DEPLOYMENT_NAME, CustomerServlet.class, ErrorServlet.class);
+ }
+
+ @Deployment(name = SecurePortal.DEPLOYMENT_NAME)
+ protected static WebArchive securePortal() {
+ return servletDeployment(SecurePortal.DEPLOYMENT_NAME, CallAuthenticatedServlet.class);
+ }
+
+ @Deployment(name = CustomerDb.DEPLOYMENT_NAME)
+ protected static WebArchive customerDb() {
+ return servletDeployment(CustomerDb.DEPLOYMENT_NAME, CustomerDatabaseServlet.class);
+ }
+
+ @Deployment(name = CustomerDbErrorPage.DEPLOYMENT_NAME)
+ protected static WebArchive customerDbErrorPage() {
+ return servletDeployment(CustomerDbErrorPage.DEPLOYMENT_NAME, CustomerDatabaseServlet.class, ErrorServlet.class);
+ }
+
+ @Deployment(name = ProductPortal.DEPLOYMENT_NAME)
+ protected static WebArchive productPortal() {
+ return servletDeployment(ProductPortal.DEPLOYMENT_NAME, ProductServlet.class);
+ }
+
+ @Deployment(name = InputPortal.DEPLOYMENT_NAME)
+ protected static WebArchive inputPortal() {
+ return servletDeployment(InputPortal.DEPLOYMENT_NAME, "keycloak.json", InputServlet.class);
+ }
+
+ @Test
+ public void testSavedPostRequest() throws InterruptedException {
+ // test login to customer-portal which does a bearer request to customer-db
+ inputPortal.navigateTo();
+ assertCurrentUrlEquals(inputPortal);
+ inputPortal.execute("hello");
+
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertEquals(driver.getCurrentUrl(), inputPortal + "/secured/post");
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("parameter=hello"));
+
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, customerPortal.toString())
+ .build("demo").toString();
+ driver.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ // test unsecured POST KEYCLOAK-901
+ Client client = ClientBuilder.newClient();
+ Form form = new Form();
+ form.param("parameter", "hello");
+ String text = client.target(inputPortal + "/unsecured").request().post(Entity.form(form), String.class);
+ assertTrue(text.contains("parameter=hello"));
+ client.close();
+ }
+
+ @Test
+ public void testLoginSSOAndLogout() {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ // test SSO
+ productPortal.navigateTo();
+ assertCurrentUrlEquals(productPortal);
+ pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
+
+ // View stats
+ List<Map<String, String>> stats = testRealmResource().getClientSessionStats();
+ Map<String, String> customerPortalStats = null;
+ Map<String, String> productPortalStats = null;
+ for (Map<String, String> s : stats) {
+ switch (s.get("clientId")) {
+ case "customer-portal":
+ customerPortalStats = s;
+ break;
+ case "product-portal":
+ productPortalStats = s;
+ break;
+ }
+ }
+ assertEquals(1, Integer.parseInt(customerPortalStats.get("active")));
+ assertEquals(1, Integer.parseInt(productPortalStats.get("active")));
+
+ // test logout
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, customerPortal.toString()).build("demo").toString();
+ driver.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+// testRealmLoginPage.form().cancel();
+// assertTrue(driver.getPageSource().contains("Error Page"));
+ }
+
+ @Test
+ public void testServletRequestLogout() {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ // test SSO
+ productPortal.navigateTo();
+ assertCurrentUrlEquals(productPortal);
+ pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("iPhone") && pageSource.contains("iPad"));
+
+ // back
+ customerPortal.navigateTo();
+ assertCurrentUrlEquals(customerPortal);
+ pageSource = driver.getPageSource();
+ Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+ // test logout
+
+ driver.navigate().to(customerPortal + "/logout");
+ assertTrue(driver.getPageSource().contains("servlet logout ok"));
+
+ customerPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+
+ @Test
+ public void testLoginSSOIdle() {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ RealmRepresentation demoRealmRep = testRealmResource().toRepresentation();
+ int originalIdle = demoRealmRep.getSsoSessionIdleTimeout();
+ demoRealmRep.setSsoSessionIdleTimeout(1);
+ testRealmResource().update(demoRealmRep);
+
+// Thread.sleep(2000);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ demoRealmRep.setSsoSessionIdleTimeout(originalIdle);
+ testRealmResource().update(demoRealmRep);
+ }
+
+ @Test
+ @Jira(value = "KEYCLOAK-1478") // rejected
+ public void testLoginSSOIdleRemoveExpiredUserSessions() {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ log.info("Current url: " + driver.getCurrentUrl());
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ log.info("Current url: " + driver.getCurrentUrl());
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ log.info(pageSource);
+ Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ RealmRepresentation demoRealmRep = testRealmResource().toRepresentation();
+ int originalIdle = demoRealmRep.getSsoSessionIdleTimeout();
+ demoRealmRep.setSsoSessionIdleTimeout(1);
+ testRealmResource().update(demoRealmRep);
+
+ Time.setOffset(2);
+
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ // need to cleanup so other tests don't fail, so invalidate http sessions on remote clients.
+ demoRealmRep.setSsoSessionIdleTimeout(originalIdle);
+ // note: sessions invalidated after each test, see: AbstractKeycloakTest.afterAbstractKeycloakTest()
+
+ Time.setOffset(0);
+ }
+
+ @Test
+ public void testLoginSSOMax() throws InterruptedException {
+ // test login to customer-portal which does a bearer request to customer-db
+ customerPortal.navigateTo();
+ testRealmLoginPage.form().waitForUsernameInputPresent();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(customerPortal);
+ String pageSource = driver.getPageSource();
+ Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+
+ RealmRepresentation demoRealmRep = testRealmResource().toRepresentation();
+ int originalIdle = demoRealmRep.getSsoSessionMaxLifespan();
+ demoRealmRep.setSsoSessionMaxLifespan(1);
+ testRealmResource().update(demoRealmRep);
+
+ TimeUnit.SECONDS.sleep(2);
+ productPortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ demoRealmRep.setSsoSessionIdleTimeout(originalIdle);
+ testRealmResource().update(demoRealmRep);
+ }
+
+ @Jira("KEYCLOAK-518")
+ @Test
+ public void testNullBearerToken() {
+ Client client = ClientBuilder.newClient();
+ WebTarget target = client.target(customerDb.toString());
+ Response response = target.request().get();
+ assertEquals(401, response.getStatus());
+ response.close();
+ response = target.request().header(HttpHeaders.AUTHORIZATION, "Bearer null").get();
+ assertEquals(401, response.getStatus());
+ response.close();
+ client.close();
+ }
+
+ @Jira("KEYCLOAK-1368")
+ @Test
+ public void testNullBearerTokenCustomErrorPage() {
+ Client client = ClientBuilder.newClient();
+ WebTarget target = client.target(customerDbErrorPage.toString());
+ Response response = target.request().get();
+
+ // TODO: follow redirects automatically if possible
+ if (response.getStatus() == 302) {
+ String location = response.getHeaderString(HttpHeaders.LOCATION);
+ response.close();
+ response = client.target(location).request().get();
+ }
+ assertEquals(200, response.getStatus());
+ String errorPageResponse = response.readEntity(String.class);
+ assertTrue(errorPageResponse.contains("Error Page"));
+ response.close();
+
+ response = target.request().header(HttpHeaders.AUTHORIZATION, "Bearer null").get();
+ // TODO: follow redirects automatically if possible
+ if (response.getStatus() == 302) {
+ String location = response.getHeaderString(HttpHeaders.LOCATION);
+ response.close();
+ response = client.target(location).request().get();
+ }
+ assertEquals(200, response.getStatus());
+ errorPageResponse = response.readEntity(String.class);
+ assertTrue(errorPageResponse.contains("Error Page"));
+ response.close();
+
+ client.close();
+ }
+
+ @Jira("KEYCLOAK-518")
+ @Test
+ public void testBadUser() {
+ Client client = ClientBuilder.newClient();
+ URI uri = OIDCLoginProtocolService.tokenUrl(authServerPage.createUriBuilder()).build("demo");
+ WebTarget target = client.target(uri);
+ String header = BasicAuthHelper.createHeader("customer-portal", "password");
+ Form form = new Form();
+ form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD)
+ .param("username", "monkey@redhat.com")
+ .param("password", "password");
+ Response response = target.request()
+ .header(HttpHeaders.AUTHORIZATION, header)
+ .post(Entity.form(form));
+ assertEquals(401, response.getStatus());
+ response.close();
+ client.close();
+
+ }
+
+ @Test
+ public void testVersion() {
+ Client client = ClientBuilder.newClient();
+ WebTarget target = client.target(authServerPage.createUriBuilder()).path("version");
+ Version version = target.request().get(Version.class);
+ assertNotNull(version);
+ assertNotNull(version.getVersion());
+ assertNotNull(version.getBuildTime());
+ assertNotEquals(version.getVersion(), Version.UNKNOWN);
+ assertNotEquals(version.getBuildTime(), Version.UNKNOWN);
+
+ Version version2 = client.target(securePortal.toString()).path(AdapterConstants.K_VERSION).request().get(Version.class);
+ assertNotNull(version2);
+ assertNotNull(version2.getVersion());
+ assertNotNull(version2.getBuildTime());
+ assertEquals(version.getVersion(), version2.getVersion());
+ assertEquals(version.getBuildTime(), version2.getBuildTime());
+ client.close();
+ }
+
+ @Test
+ public void testAuthenticated() {
+ // test login to customer-portal which does a bearer request to customer-db
+ securePortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertCurrentUrlEquals(securePortal);
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
+ // test logout
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, securePortal.toString()).build("demo").toString();
+ driver.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ securePortal.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSessionServletAdapterTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSessionServletAdapterTest.java
new file mode 100644
index 0000000..79c0bef
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/adapter/servlet/AbstractSessionServletAdapterTest.java
@@ -0,0 +1,184 @@
+package org.keycloak.testsuite.adapter.servlet;
+
+import org.keycloak.testsuite.adapter.AbstractServletsAdapterTest;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.drone.api.annotation.Drone;
+import org.jboss.arquillian.graphene.page.Page;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.junit.After;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.resource.ClientResource;
+import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.testsuite.adapter.page.SessionPortal;
+import org.keycloak.testsuite.arquillian.jira.Jira;
+import static org.keycloak.testsuite.auth.page.AuthRealm.DEMO;
+import org.keycloak.testsuite.auth.page.account.Sessions;
+import org.keycloak.testsuite.auth.page.login.Login;
+import static org.keycloak.testsuite.admin.ApiUtil.findClientResourceByClientId;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import org.keycloak.testsuite.util.SecondBrowser;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractSessionServletAdapterTest extends AbstractServletsAdapterTest {
+
+ @Page
+ private SessionPortal sessionPortalPage;
+
+ @Page
+ private Sessions testRealmSessions;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmSessions.setAuthRealm(DEMO);
+ }
+
+ @Deployment(name = SessionPortal.DEPLOYMENT_NAME)
+ protected static WebArchive sessionPortal() {
+ return servletDeployment(SessionPortal.DEPLOYMENT_NAME, "keycloak.json", SessionServlet.class);
+ }
+
+ @After
+ public void afterSessionServletAdapterTest() {
+ sessionPortalPage.navigateTo();
+ driver.manage().deleteAllCookies();
+ }
+
+ @Drone
+ @SecondBrowser
+ protected WebDriver driver2;
+
+ @Jira("KEYCLOAK-732")
+ @Test
+ public void testSingleSessionInvalidated() {
+
+ loginAndCheckSession(driver, testRealmLoginPage);
+
+ // cannot pass to loginAndCheckSession becayse loginPage is not working together with driver2, therefore copypasta
+ driver2.navigate().to(sessionPortalPage.toString());
+ assertCurrentUrlStartsWithLoginUrlOf(driver2, testRealmPage);
+ driver2.findElement(By.id("username")).sendKeys("bburke@redhat.com");
+ driver2.findElement(By.id("password")).sendKeys("password");
+ driver2.findElement(By.id("password")).submit();
+ assertCurrentUrlEquals(driver2, sessionPortalPage);
+ String pageSource = driver2.getPageSource();
+ assertTrue(pageSource.contains("Counter=1"));
+ // Counter increased now
+ driver2.navigate().to(sessionPortalPage.toString());
+ pageSource = driver2.getPageSource();
+ assertTrue(pageSource.contains("Counter=2"));
+
+ // Logout in browser1
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, sessionPortalPage.toString()).build("demo").toString();
+ driver.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ // Assert that I am logged out in browser1
+ sessionPortalPage.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+
+ // Assert that I am still logged in browser2 and same session is still preserved
+ driver2.navigate().to(sessionPortalPage.toString());
+ assertCurrentUrlEquals(driver2, sessionPortalPage);
+ pageSource = driver2.getPageSource();
+ assertTrue(pageSource.contains("Counter=3"));
+
+ driver2.navigate().to(logoutUri);
+ assertCurrentUrlStartsWithLoginUrlOf(driver2, testRealmPage);
+
+ }
+
+ @Test
+ @Jira("KEYCLOAK-741, KEYCLOAK-1485")
+ public void testSessionInvalidatedAfterFailedRefresh() {
+ RealmRepresentation testRealmRep = testRealmResource().toRepresentation();
+ ClientResource sessionPortalRes = null;
+ for (ClientRepresentation clientRep : testRealmResource().clients().findAll()) {
+ if ("session-portal".equals(clientRep.getClientId())) {
+ sessionPortalRes = testRealmResource().clients().get(clientRep.getId());
+ }
+ }
+ assertNotNull(sessionPortalRes);
+ sessionPortalRes.toRepresentation().setAdminUrl("");
+ int origTokenLifespan = testRealmRep.getAccessCodeLifespan();
+ testRealmRep.setAccessCodeLifespan(1);
+ testRealmResource().update(testRealmRep);
+
+ // Login
+ loginAndCheckSession(driver, testRealmLoginPage);
+
+ // Logout
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, sessionPortalPage.toString()).build("demo").toString();
+ driver.navigate().to(logoutUri);
+
+ // Assert that http session was invalidated
+ sessionPortalPage.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ testRealmLoginPage.form().login("bburke@redhat.com", "password");
+ assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Counter=1"));
+
+ sessionPortalRes.toRepresentation().setAdminUrl(sessionPortalPage.toString());
+ testRealmRep.setAccessCodeLifespan(origTokenLifespan);
+ testRealmResource().update(testRealmRep);
+ }
+
+ @Test
+ @Jira("KEYCLOAK-942")
+ public void testAdminApplicationLogout() {
+ // login as bburke
+ loginAndCheckSession(driver, testRealmLoginPage);
+ // logout mposolda with admin client
+ findClientResourceByClientId(testRealmResource(), "session-portal")
+ .logoutUser("mposolda");
+ // bburke should be still logged with original httpSession in our browser window
+ sessionPortalPage.navigateTo();
+ assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Counter=3"));
+ String logoutUri = OIDCLoginProtocolService.logoutUrl(authServerPage.createUriBuilder())
+ .queryParam(OAuth2Constants.REDIRECT_URI, sessionPortalPage.toString()).build("demo").toString();
+ driver.navigate().to(logoutUri);
+ }
+
+ @Test
+ @Jira("KEYCLOAK-1216, KEYCLOAK-1485")
+ public void testAccountManagementSessionsLogout() {
+ // login as bburke
+ loginAndCheckSession(driver, testRealmLoginPage);
+ testRealmSessions.navigateTo();
+ testRealmSessions.logoutAll();
+ // Assert I need to login again (logout was propagated to the app)
+ loginAndCheckSession(driver, testRealmLoginPage);
+ }
+
+ private void loginAndCheckSession(WebDriver driver, Login login) {
+ sessionPortalPage.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(testRealmPage);
+ login.form().login("bburke@redhat.com", "password");
+ assertEquals(driver.getCurrentUrl(), sessionPortalPage.toString());
+ String pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Counter=1"));
+
+ // Counter increased now
+ sessionPortalPage.navigateTo();
+ pageSource = driver.getPageSource();
+ assertTrue(pageSource.contains("Counter=2"));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java
new file mode 100644
index 0000000..573363a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/AbstractConsoleTest.java
@@ -0,0 +1,111 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.AbstractAuthTest;
+import org.keycloak.testsuite.console.page.AdminConsole;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm.ConfigureMenu;
+import org.keycloak.testsuite.console.page.AdminConsoleRealm.ManageMenu;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.login.Login;
+import org.keycloak.testsuite.console.page.fragment.ModalDialog;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWithLoginUrlOf;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public abstract class AbstractConsoleTest extends AbstractAuthTest {
+
+ @Page
+ protected AdminConsole adminConsolePage;
+ @Page
+ protected AdminConsoleRealm adminConsoleRealmPage;
+
+ @Page
+ protected AdminConsole testRealmAdminConsolePage;
+
+ @FindBy(xpath = "//div[@class='modal-dialog']")
+ protected ModalDialog modalDialog;
+
+ protected boolean adminLoggedIn = false;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmPage.setAuthRealm(TEST);
+ testRealmAdminConsolePage.setAdminRealm(TEST);
+ }
+
+ @Before
+ public void beforeConsoleTest() {
+ createTestUserWithAdminClient();
+ if (!testContext.isAdminLoggedIn()) {
+ loginToMasterRealmAdminConsoleAs(adminUser);
+ testContext.setAdminLoggedIn(true);
+ } else {
+// adminConsoleRealmPage.navigateTo();
+ }
+ }
+
+ public void loginToMasterRealmAdminConsoleAs(UserRepresentation user) {
+ loginToAdminConsoleAs(adminConsolePage, loginPage, user);
+ }
+
+ public void logoutFromMasterRealmConsole() {
+ logoutFromAdminConsole(adminConsolePage);
+ }
+
+ public void loginToTestRealmConsoleAs(UserRepresentation user) {
+ loginToAdminConsoleAs(testRealmAdminConsolePage, testRealmLoginPage, user);
+ }
+
+ public void logoutFromTestRealmConsole() {
+ logoutFromAdminConsole(testRealmAdminConsolePage);
+ }
+
+ public void loginToAdminConsoleAs(AdminConsole adminConsole, Login login, UserRepresentation user) {
+ adminConsole.navigateTo();
+ assertCurrentUrlStartsWithLoginUrlOf(adminConsole);
+ login.form().login(user);
+ assertCurrentUrlStartsWith(adminConsole);
+ }
+
+ public void logoutFromAdminConsole(AdminConsole adminConsole) {
+ adminConsole.navigateTo();
+ assertCurrentUrlStartsWith(adminConsole);
+ adminConsole.logOut();
+ assertCurrentUrlStartsWithLoginUrlOf(adminConsole);
+ }
+
+ public ConfigureMenu configure() {
+ return adminConsoleRealmPage.configure();
+ }
+
+ public ManageMenu manage() {
+ return adminConsoleRealmPage.manage();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java
new file mode 100644
index 0000000..75cf70f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/PasswordPolicyTest.java
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.authentication;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.authentication.PasswordPolicy;
+
+import static org.keycloak.testsuite.console.page.authentication.PasswordPolicy.Type.*;
+import org.keycloak.testsuite.console.page.users.UserCredentials;
+
+/**
+ * @author Petr Mensik
+ * @author mhajas
+ */
+//@Ignore // FIXME still unstable
+public class PasswordPolicyTest extends AbstractConsoleTest {
+
+ @Page
+ private PasswordPolicy passwordPolicyPage;
+
+ @Page
+ private UserCredentials testUserCredentialsPage;
+
+ @Before
+ public void beforePasswordPolicyTest() {
+ testUserCredentialsPage.setId(testUser.getId());
+ passwordPolicyPage.navigateTo();
+ }
+
+ @Test
+ public void testAddAndRemovePolicy() {
+ passwordPolicyPage.addPolicy(HASH_ITERATIONS, 5);
+ passwordPolicyPage.removePolicy(HASH_ITERATIONS);
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testInvalidPolicyValues() {
+ passwordPolicyPage.addPolicy(HASH_ITERATIONS, "asd");
+ assertFlashMessageDanger();
+ passwordPolicyPage.removePolicy(HASH_ITERATIONS);
+
+ passwordPolicyPage.addPolicy(REGEX_PATTERN, "^[A-Z]{8,5}");
+ assertFlashMessageDanger();
+ }
+
+ @Test
+ public void testLengthPolicy() {
+ passwordPolicyPage.addPolicy(LENGTH, 8);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("1234567");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("12345678");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testDigitsPolicy() {
+ passwordPolicyPage.addPolicy(DIGITS, 2);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("invalidPassword1");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("validPassword12");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testLowerCasePolicy() {
+ passwordPolicyPage.addPolicy(LOWER_CASE, 2);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("iNVALIDPASSWORD");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("vaLIDPASSWORD");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testUpperCasePolicy() {
+ passwordPolicyPage.addPolicy(UPPER_CASE, 2);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("Invalidpassword");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("VAlidpassword");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testSpecialCharsPolicy() {
+ passwordPolicyPage.addPolicy(SPECIAL_CHARS, 2);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("invalidPassword*");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("validPassword*#");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testNotUsernamePolicy() {
+ passwordPolicyPage.addPolicy(NOT_USERNAME);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword(testUser.getUsername());
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("validpassword");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testRegexPatternsPolicy() {
+ passwordPolicyPage.addPolicy(REGEX_PATTERN, "^[A-Z]+#[a-z]{8}$");
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("invalidPassword");
+ assertFlashMessageDanger();
+
+ testUserCredentialsPage.resetPassword("VALID#password");
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void testPasswordHistoryPolicy() {
+ passwordPolicyPage.addPolicy(PASSWORD_HISTORY, 2);
+
+ testUserCredentialsPage.navigateTo();
+ testUserCredentialsPage.resetPassword("firstPassword");
+ assertFlashMessageSuccess();
+
+ testUserCredentialsPage.resetPassword("secondPassword");
+ assertFlashMessageSuccess();
+
+ testUserCredentialsPage.resetPassword("firstPassword");
+ assertFlashMessageDanger();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/RequiredActionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/RequiredActionsTest.java
new file mode 100644
index 0000000..9c818d1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/authentication/RequiredActionsTest.java
@@ -0,0 +1,137 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.authentication;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.auth.page.login.Registration;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.authentication.RequiredActions;
+import org.keycloak.testsuite.console.page.realm.LoginSettings;
+import org.openqa.selenium.By;
+
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+
+/**
+ * @author Petr Mensik
+ * @author mhajas
+ */
+public class RequiredActionsTest extends AbstractConsoleTest {
+
+ @Page
+ private RequiredActions requiredActionsPage;
+
+ @Page
+ private LoginSettings loginSettingsPage;
+
+ @Page
+ private Registration testRealmRegistrationPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmRegistrationPage.setAuthRealm("test");
+ }
+
+ @Before
+ public void beforeRequiredActionsTest() {
+ requiredActionsPage.navigateTo();
+ }
+
+ @Test
+ public void requiredActionsTest() {
+ requiredActionsPage.clickTermsAndConditionEnabled();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickTermsAndConditionDefaultAction();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickVerifyEmailEnabled();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickVerifyEmailDefaultAction();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickUpdatePasswordEnabled();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickUpdatePasswordDefaultAction();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickConfigureTotpEnabled();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickConfigureTotpDefaultAction();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickUpdateProfileEnabled();
+ assertFlashMessageSuccess();
+
+ requiredActionsPage.clickUpdateProfileDefaultAction();
+ assertFlashMessageSuccess();
+ }
+
+ @Test
+ public void termsAndConditionsDefaultActionTest() {
+ requiredActionsPage.clickTermsAndConditionEnabled();
+ requiredActionsPage.clickTermsAndConditionDefaultAction();
+
+ allowTestRealmUserRegistration();
+
+ navigateToTestRealmRegistration();
+
+ registerTestUser();
+
+ driver.findElement(By.xpath("//div[@id='kc-header-wrapper' and text()[contains(.,'Terms and Conditions')]]"));
+ }
+
+ @Test
+ public void configureTotpDefaultActionTest() {
+ requiredActionsPage.clickConfigureTotpDefaultAction();
+
+ allowTestRealmUserRegistration();
+
+ navigateToTestRealmRegistration();
+
+ registerTestUser();
+
+ driver.findElement(By.xpath("//div[@id='kc-header-wrapper' and text()[contains(.,'Mobile Authenticator Setup')]]"));
+ }
+
+ private void allowTestRealmUserRegistration() {
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setRegistrationAllowed(true);
+ loginSettingsPage.form().save();
+ }
+
+ private void navigateToTestRealmRegistration() {
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().register();
+ }
+
+ private void registerTestUser() {
+ UserRepresentation user = createUserRepresentation("testUser", "testUser@email.test", "test", "user", true);
+ setPasswordFor(user, PASSWORD);
+
+ testRealmRegistrationPage.register(user);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java
new file mode 100644
index 0000000..6c40d64
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/AbstractClientTest.java
@@ -0,0 +1,74 @@
+package org.keycloak.testsuite.console.clients;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.auth.page.login.OIDCLogin.OIDC;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.clients.Client;
+import org.keycloak.testsuite.console.page.clients.Clients;
+import org.keycloak.testsuite.console.page.clients.CreateClient;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractClientTest extends AbstractConsoleTest {
+
+ @Page
+ protected Clients clientsPage;
+ @Page
+ protected Client clientPage; // note: cannot call navigateTo() unless client id is set
+ @Page
+ protected CreateClient createClientPage;
+
+ @Before
+ public void beforeClientTest() {
+// configure().clients();
+ clientsPage.navigateTo();
+ }
+
+ public void createClient(ClientRepresentation client) {
+ assertCurrentUrlEquals(clientsPage);
+ clientsPage.table().createClient();
+ createClientPage.form().setValues(client);
+ createClientPage.form().save();
+ }
+
+ public void deleteClientViaTable(String clientId) {
+ assertCurrentUrlEquals(clientsPage);
+ clientsPage.deleteClient(clientId);
+ }
+
+ public void deleteClientViaPage(String clientId) {
+ assertCurrentUrlEquals(clientsPage);
+ clientsPage.table().search(clientId);
+ clientsPage.table().clickClient(clientId);
+ clientPage.delete();
+ }
+
+ public static ClientRepresentation createClientRepresentation(String clientId, String... redirectUris) {
+ ClientRepresentation client = new ClientRepresentation();
+ client.setClientId(clientId);
+ client.setEnabled(true);
+ client.setConsentRequired(false);
+ client.setDirectGrantsOnly(false);
+
+ client.setProtocol(OIDC);
+
+ client.setBearerOnly(false);
+ client.setPublicClient(false);
+ client.setServiceAccountsEnabled(false);
+
+ List<String> redirectUrisList = new ArrayList();
+ redirectUrisList.addAll(Arrays.asList(redirectUris));
+ client.setRedirectUris(redirectUrisList);
+
+ return client;
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java
new file mode 100644
index 0000000..a8a4c35
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientRolesTest.java
@@ -0,0 +1,273 @@
+package org.keycloak.testsuite.console.clients;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.testsuite.console.page.users.UserRoleMappingsForm;
+
+import static org.junit.Assert.*;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.clients.ClientRole;
+import org.keycloak.testsuite.console.page.clients.ClientRoles;
+import org.keycloak.testsuite.console.page.clients.CreateClientRole;
+import org.keycloak.testsuite.console.page.users.User;
+
+/**
+ * Created by fkiss.
+ */
+public class ClientRolesTest extends AbstractClientTest {
+
+ @Page
+ private ClientRoles clientRolesPage;
+ @Page
+ private CreateClientRole createClientRolePage;
+ @Page
+ private ClientRole clientRolePage;
+
+ @Page
+ private User userPage; // note: cannot call navigateTo() unless user id is set
+
+ @Page
+ private UserRoleMappingsForm userRolesPage;
+
+ public void addClientRole(RoleRepresentation roleRep) {
+// assertCurrentUrl(clientRoles);
+ clientRolesPage.roles().addRole();
+// assertCurrentUrl(createClientRole); // can't do this, need client id to build uri
+ createClientRolePage.form().setBasicAttributes(roleRep);
+ createClientRolePage.form().save();
+ assertFlashMessageSuccess();
+ createClientRolePage.form().setCompositeRoles(roleRep);
+ // TODO add verification of notification message when KEYCLOAK-1497 gets resolved
+ }
+
+ @Test
+ public void testAddClientRole() {
+ ClientRepresentation newClient = createClientRepresentation("test-client1", "http://example.com/*");
+ RoleRepresentation newRole = new RoleRepresentation("client-role", "", false);
+
+ createClient(newClient);
+ assertFlashMessageSuccess();
+
+ clientPage.tabs().roles();
+ addClientRole(newRole);
+ assertFlashMessageSuccess();
+
+ clientRolePage.backToClientRolesViaBreadcrumb();
+ assertFalse(clientRolesPage.roles().getRolesFromTableRows().isEmpty());
+
+ configure().clients();
+ clientsPage.table().search(newClient.getClientId());
+ clientsPage.table().deleteClient(newClient.getClientId());
+ modalDialog.confirmDeletion();
+ assertFlashMessageSuccess();
+ assertNull(clientsPage.table().findClient(newClient.getClientId()));
+ }
+
+// @Test
+// @Jira("KEYCLOAK-1497")
+// public void testAddClientRoleToUser() {
+// ClientRepresentation newClient = createClientRepresentation("test-client2", "http://example.com/*");
+// RoleRepresentation newRole = new RoleRepresentation("client-role2", "");
+// String testUsername = "test-user2";
+// UserRepresentation newUser = new UserRepresentation();
+// newUser.setUsername(testUsername);
+// newUser.credential(PASSWORD, "pass");
+//
+// createClient(newClient);
+// assertFlashMessageSuccess();
+//
+// client.tabs().roles();
+// addClientRole(newRole);
+// assertFlashMessageSuccess();
+//
+// clientRole.backToClientRolesViaBreadcrumb();
+// assertFalse(clientRoles.table().searchRoles(newRole.getName()).isEmpty());
+//
+// users.navigateTo();
+// createUser(newUser);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// users.navigateTo();
+// users.findUser(testUsername);
+// users.clickUser(testUsername);
+//
+// user.tabs().roleMappings();
+// roleMappings.selectClientRole(newClient.getClientId());
+// roleMappings.addAvailableClientRole(newRole.getName());
+// //flashMessage.waitUntilPresent();
+// //assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// //KEYCLOAK-1497
+// assertTrue(roleMappings.isAssignedClientRole(newRole.getName()));
+//
+// users.navigateTo();
+// users.deleteUser(testUsername);
+//
+// clients.navigateTo();
+// clients.deleteClient(newClient.getClientId());
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// assertNull(clients.findClient(newClient.getClientId()));
+// }
+//
+// @Test
+// @Jira("KEYCLOAK-1496, KEYCLOAK-1497")
+// @Ignore // TODO use REST to create test data (user/roles)
+// public void testAddCompositeRealmClientRoleToUser() {
+// ClientRepresentation newClient = createClientRepresentation("test-client3", "http://example.com/*");
+// RoleRepresentation clientCompositeRole = new RoleRepresentation("client-composite-role", "");
+// String testUsername = "test-user3";
+// UserRepresentation newUser = new UserRepresentation();
+// newUser.setUsername(testUsername);
+// newUser.credential(PASSWORD, "pass");
+//
+// RoleRepresentation subRole1 = new RoleRepresentation("sub-role1", "");
+// RoleRepresentation subRole2 = new RoleRepresentation("sub-role2", "");
+// List<RoleRepresentation> testRoles = new ArrayList<>();
+// clientCompositeRole.setComposite(true);
+// testRoles.add(subRole1);
+// testRoles.add(subRole2);
+//
+// //create sub-roles
+// configure().roles();
+// for (RoleRepresentation role : testRoles) {
+// realmRoles.addRole(role);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// configure().roles();
+// assertEquals(role.getName(), realmRoles.findRole(role.getName()).getName());
+// }
+//
+// //create client
+// clients.navigateTo();
+// createClient(newClient);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //add client role
+// configure().roles();
+// realmRoles.addRole(clientCompositeRole);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //add realm composite roles
+// realmRoles.setCompositeRole(clientCompositeRole);
+// roleMappings.addAvailableRole(subRole1.getName(), subRole2.getName());
+// //flashMessage.waitUntilPresent();
+// //assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// //KEYCLOAK-1497
+//
+// //create user
+// users.navigateTo();
+// createUser(newUser);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //add client role to user and verify
+// users.navigateTo();
+// users.findUser(testUsername);
+// users.clickUser(testUsername);
+//
+// user.tabs().roleMappings();
+// roleMappings.selectClientRole(newClient.getClientId());
+// roleMappings.addAvailableClientRole(clientCompositeRole.getName());
+// //flashMessage.waitUntilPresent();
+// //assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// //KEYCLOAK-1497
+// assertTrue(roleMappings.isAssignedClientRole(clientCompositeRole.getName()));
+// assertTrue(roleMappings.isEffectiveRealmRolesComplete(subRole1, subRole2)); //KEYCLOAK-1496
+// assertTrue(roleMappings.isEffectiveClientRolesComplete(clientCompositeRole));
+//
+// //delete everything
+// users.navigateTo();
+// users.deleteUser(testUsername);
+//
+// configure().roles();
+// realmRoles.deleteRole(subRole1);
+// configure().roles();
+// realmRoles.deleteRole(subRole2);
+//
+// clients.navigateTo();
+// clients.deleteClient(newClient.getClientId());
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// assertNull(clients.findClient(newClient.getClientId()));
+// }
+//
+// @Test
+// @Jira("KEYCLOAK-1504, KEYCLOAK-1497")
+// public void testAddCompositeClientRoleToUser() {
+// ClientRepresentation newClient = createClientRepresentation("test-client4", "http://example.com/*");
+// RoleRepresentation clientCompositeRole = new RoleRepresentation("client-composite-role2", "");
+// String testUsername = "test-user4";
+// UserRepresentation newUser = new UserRepresentation();
+// newUser.setUsername(testUsername);
+// newUser.credential(PASSWORD, "pass");
+//
+// RoleRepresentation subRole1 = new RoleRepresentation("client-sub-role1", "");
+// RoleRepresentation subRole2 = new RoleRepresentation("client-sub-role2", "");
+// List<RoleRepresentation> testRoles = new ArrayList<>();
+// clientCompositeRole.setComposite(true);
+// testRoles.add(clientCompositeRole);
+// testRoles.add(subRole1);
+// testRoles.add(subRole2);
+//
+// //create client
+// createClient(newClient);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //create sub-roles
+// configure().roles();
+// for (RoleRepresentation role : testRoles) {
+// clients.navigateTo();
+// clients.clickClient(newClient.getClientId());
+// configure().roles();
+// realmRoles.addRole(role);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// }
+//
+// //add client composite roles
+// clients.navigateTo();
+// clients.clickClient(newClient);
+// configure().roles();
+// realmRoles.clickRole(clientCompositeRole);
+// realmRoles.setCompositeRole(clientCompositeRole);
+// roleMappings.selectClientRole(newClient.getClientId());
+// roleMappings.addAvailableClientRole(subRole1.getName(), subRole2.getName());
+// //flashMessage.waitUntilPresent();
+// //assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// //KEYCLOAK-1504, KEYCLOAK-1497
+//
+// //create user
+// users.navigateTo();
+// createUser(newUser);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //add client role to user and verify
+// users.navigateTo();
+// users.findUser(testUsername);
+// users.clickUser(testUsername);
+//
+// user.tabs().roleMappings();
+// roleMappings.selectClientRole(newClient.getClientId());
+// roleMappings.addAvailableClientRole(clientCompositeRole.getName());
+// assertTrue(roleMappings.isAssignedClientRole(clientCompositeRole.getName()));
+// assertTrue(roleMappings.isEffectiveClientRolesComplete(clientCompositeRole, subRole1, subRole2));
+//
+// //delete everything
+// users.navigateTo();
+// users.deleteUser(testUsername);
+//
+// configure().roles();
+// realmRoles.deleteRole(subRole1);
+// configure().roles();
+// realmRoles.deleteRole(subRole2);
+//
+// clients.navigateTo();
+// clients.deleteClient(newClient.getClientId());
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// assertNull(clients.findClient(newClient.getClientId()));
+// }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java
new file mode 100644
index 0000000..5229527
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/clients/ClientSettingsTest.java
@@ -0,0 +1,163 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.clients;
+
+import javax.ws.rs.core.Response;
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import org.junit.Test;
+
+import org.keycloak.representations.idm.ClientRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.getCreatedId;
+import org.keycloak.testsuite.console.page.clients.ClientSettings;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsBooleanAttributes;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsListAttributes;
+import static org.keycloak.testsuite.util.AttributesAssert.assertEqualsStringAttributes;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.keycloak.testsuite.util.Timer;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class ClientSettingsTest extends AbstractClientTest {
+
+ @Page
+ private ClientSettings clientSettingsPage;
+
+ private ClientRepresentation newClient;
+
+ public void crudOIDCConfidential() {
+ newClient = createClientRepresentation("oidc-confidential", "http://example.test/app/*");
+ createClient(newClient);
+ assertFlashMessageSuccess();
+
+ clientPage.backToClientsViaBreadcrumb();
+ assertCurrentUrlEquals(clientsPage);
+ assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
+
+ // read & verify
+ clientsPage.table().clickClient(newClient);
+ ClientRepresentation found = clientSettingsPage.form().getValues();
+ assertClientSettingsEqual(newClient, found);
+
+ // update & verify
+ // TODO change attributes, add redirect uris and weborigins
+ // delete
+ // TODO
+ clientPage.backToClientsViaBreadcrumb();
+ }
+
+ public void createOIDCPublic() {
+ newClient = createClientRepresentation("oidc-public", "http://example.test/app/*");
+ newClient.setPublicClient(true);
+ createClient(newClient);
+ assertFlashMessageSuccess();
+
+ clientPage.backToClientsViaBreadcrumb();
+ assertCurrentUrlEquals(clientsPage);
+ assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
+ }
+
+ public void createOIDCBearerOnly() {
+ newClient = createClientRepresentation("oidc-bearer-only", "http://example.test/app/*");
+ newClient.setBearerOnly(true);
+ createClient(newClient);
+ assertFlashMessageSuccess();
+
+ clientPage.backToClientsViaBreadcrumb();
+ assertCurrentUrlEquals(clientsPage);
+ assertEquals(1, clientsPage.table().searchClients(newClient.getClientId()).size());
+ }
+
+ @Test
+ public void successfulCRUD() {
+ crudOIDCConfidential();
+ createOIDCPublic();
+ createOIDCBearerOnly();
+ }
+
+ @Test
+ public void invalidSettings() {
+ clientsPage.table().createClient();
+ createClientPage.form().save();
+ assertFlashMessageDanger();
+
+ createClientPage.form().setClientId("test-client");
+ createClientPage.form().save();
+ assertFlashMessageDanger();
+ }
+
+ public void assertClientSettingsEqual(ClientRepresentation c1, ClientRepresentation c2) {
+ assertEqualsStringAttributes(c1.getClientId(), c2.getClientId());
+ assertEqualsStringAttributes(c1.getName(), c2.getName());
+ assertEqualsBooleanAttributes(c1.isEnabled(), c2.isEnabled());
+ assertEqualsBooleanAttributes(c1.isConsentRequired(), c2.isConsentRequired());
+ assertEqualsBooleanAttributes(c1.isDirectGrantsOnly(), c2.isDirectGrantsOnly());
+ assertEqualsStringAttributes(c1.getProtocol(), c2.getProtocol());
+
+ assertEqualsBooleanAttributes(c1.isBearerOnly(), c2.isBearerOnly());
+ assertEqualsBooleanAttributes(c1.isPublicClient(), c2.isPublicClient());
+ assertEqualsBooleanAttributes(c1.isSurrogateAuthRequired(), c2.isSurrogateAuthRequired());
+
+ assertEqualsBooleanAttributes(c1.isFrontchannelLogout(), c2.isFrontchannelLogout());
+
+ assertEqualsBooleanAttributes(c1.isServiceAccountsEnabled(), c2.isServiceAccountsEnabled());
+ assertEqualsListAttributes(c1.getRedirectUris(), c2.getRedirectUris());
+ assertEqualsStringAttributes(c1.getBaseUrl(), c2.getBaseUrl());
+ assertEqualsStringAttributes(c1.getAdminUrl(), c2.getAdminUrl());
+ assertEqualsListAttributes(c1.getWebOrigins(), c2.getWebOrigins());
+ }
+
+// @Test
+ public void createInconsistentClient() {
+ ClientRepresentation c = createClientRepresentation("inconsistent_client");
+ c.setPublicClient(true);
+ c.setBearerOnly(true);
+
+ Response r = clientsPage.clientsResource().create(c);
+ r.close();
+ clientSettingsPage.setId(getCreatedId(r));
+
+ c = clientSettingsPage.clientResource().toRepresentation();
+ assertTrue(c.isBearerOnly());
+ assertTrue(c.isPublicClient());
+ }
+
+ public void createClients(String clientIdPrefix, int count) {
+ for (int i = 0; i < count; i++) {
+ String clientId = String.format("%s%02d", clientIdPrefix, i);
+ ClientRepresentation cr = createClientRepresentation(clientId, "http://example.test/*");
+ Timer.time();
+ Response r = testRealmResource().clients().create(cr);
+ r.close();
+ Timer.time("create client");
+ }
+ }
+
+// @Test
+ public void clientsPagination() {
+ createClients("test_client_", 100);
+ clientsPage.navigateTo();
+ pause(120000);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java
new file mode 100644
index 0000000..5b22ca4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/AdminEventsTest.java
@@ -0,0 +1,84 @@
+package org.keycloak.testsuite.console.events;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.clients.AbstractClientTest;
+import org.keycloak.testsuite.console.page.clients.Clients;
+import org.keycloak.testsuite.console.page.events.AdminEvents;
+import org.keycloak.testsuite.console.page.events.Config;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import static org.junit.Assert.assertEquals;
+
+import javax.ws.rs.core.Response;
+import java.util.List;
+
+
+/**
+ * @author mhajas
+ */
+public class AdminEventsTest extends AbstractConsoleTest {
+
+ @Page
+ private AdminEvents adminEventsPage;
+
+ @Page
+ private Config configPage;
+
+ @Page
+ private Clients clientsPage;
+
+ private ClientRepresentation newClient;
+
+ @Before
+ public void beforeAdminEventsTest() {
+ configPage.navigateTo();
+ configPage.form().setSaveAdminEvents(true);
+ configPage.form().setIncludeRepresentation(true);
+ configPage.form().save();
+ }
+
+ @Test
+ public void clientsAdminEventsTest() {
+ newClient = AbstractClientTest.createClientRepresentation("test_client", "http://example.test/test_client/*");
+ Response response = clientsPage.clientsResource().create(newClient);
+ String id = ApiUtil.getCreatedId(response);
+ response.close();
+ newClient.setClientId("test_client2");
+ clientsPage.clientsResource().get(id).update(newClient);
+ clientsPage.clientsResource().get(id).remove();
+
+ adminEventsPage.navigateTo();
+ adminEventsPage.table().filter();
+ adminEventsPage.table().filterForm().addOperationType("CREATE");
+ adminEventsPage.table().update();
+
+ List<WebElement> resultList = adminEventsPage.table().rows();
+ assertEquals(1, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='CREATE']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
+
+ adminEventsPage.table().reset();
+ adminEventsPage.table().filterForm().addOperationType("UPDATE");
+ adminEventsPage.table().update();
+
+ resultList = adminEventsPage.table().rows();
+ assertEquals(1, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='UPDATE']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
+
+ adminEventsPage.table().reset();
+ adminEventsPage.table().filterForm().addOperationType("DELETE");
+ adminEventsPage.table().update();
+
+ resultList = adminEventsPage.table().rows();
+ assertEquals(1, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='DELETE']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='clients/" + id + "']"));
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/LoginEventsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/LoginEventsTest.java
new file mode 100644
index 0000000..078c56d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/events/LoginEventsTest.java
@@ -0,0 +1,85 @@
+package org.keycloak.testsuite.console.events;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.admin.Users;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.events.Config;
+import org.keycloak.testsuite.console.page.events.LoginEvents;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+
+import java.util.List;
+
+import static org.junit.Assert.assertEquals;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+
+/**
+ * @author mhajas
+ */
+public class LoginEventsTest extends AbstractConsoleTest {
+ @Page
+ private LoginEvents loginEventsPage;
+
+ @Page
+ private Config configPage;
+
+ @Before
+ public void beforeLoginEventsTest() {
+ configPage.navigateTo();
+ configPage.form().setSaveEvents(true);
+ configPage.form().addSaveType("LOGIN");
+ configPage.form().addSaveType("LOGIN_ERROR");
+ configPage.form().addSaveType("LOGOUT");
+ configPage.form().save();
+ }
+
+ @Test
+ public void userAccessEventsTest() {
+ testRealmAdminConsolePage.navigateTo();
+ Users.setPasswordFor(testUser, "Wrong_password");
+ testRealmLoginPage.form().login(testUser);
+ Users.setPasswordFor(testUser, PASSWORD);
+ testRealmLoginPage.form().login(testUser);
+ testRealmAdminConsolePage.logOut();
+
+ loginEventsPage.navigateTo();
+ loginEventsPage.table().filter();
+
+ loginEventsPage.table().filterForm().addEventType("LOGIN");
+ loginEventsPage.table().update();
+
+ List<WebElement> resultList = loginEventsPage.table().rows();
+
+ assertEquals(7, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='LOGIN']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='User']/../td[text()='" + testUser.getId() + "']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='Client']/../td[text()='security-admin-console']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+
+ loginEventsPage.table().reset();
+ loginEventsPage.table().filterForm().addEventType("LOGOUT");
+ loginEventsPage.table().update();
+
+ resultList = loginEventsPage.table().rows();
+
+ assertEquals(2, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='LOGOUT']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='User']/../td[text()='" + testUser.getId() + "']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+
+ loginEventsPage.table().reset();
+ loginEventsPage.table().filterForm().addEventType("LOGIN_ERROR");
+ loginEventsPage.table().update();
+
+ resultList = loginEventsPage.table().rows();
+
+ assertEquals(6, resultList.size());
+ resultList.get(0).findElement(By.xpath("//td[text()='LOGIN_ERROR']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='User']/../td[text()='" + testUser.getId() + "']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='Client']/../td[text()='security-admin-console']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='Error']/../td[text()='invalid_user_credentials']"));
+ resultList.get(0).findElement(By.xpath("//td[text()='IP Address']/../td[text()='127.0.0.1']"));
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java
new file mode 100644
index 0000000..e70da46
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/federation/LdapUserFederationTest.java
@@ -0,0 +1,71 @@
+package org.keycloak.testsuite.console.federation;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.*;
+import org.keycloak.models.LDAPConstants;
+
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.federation.LdapUserProviderForm;
+import org.keycloak.testsuite.console.page.federation.UserFederation;
+import org.keycloak.testsuite.console.page.users.Users;
+import org.keycloak.testsuite.util.LDAPTestConfiguration;
+
+import java.util.Map;
+
+import static org.junit.Assert.assertTrue;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+
+/**
+ * Created by fkiss.
+ */
+public class LdapUserFederationTest extends AbstractConsoleTest {
+
+ @Page
+ private LdapUserProviderForm ldapUserProviderForm;
+
+ @Page
+ private UserFederation userFederationPage;
+
+ @Page
+ private Users usersPage;
+
+ @Before
+ public void beforeTestLdapUserFederation() {
+ //configure().userFederation();
+ }
+
+ @Ignore
+ @Test
+ public void addAndConfigureProvider() {
+ adminConsolePage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+
+ String name = "ldapname";
+
+ String LDAP_CONNECTION_PROPERTIES_LOCATION = "ldap/ldap-connection.properties";
+ LDAPTestConfiguration ldapTestConfiguration = LDAPTestConfiguration.readConfiguration(LDAP_CONNECTION_PROPERTIES_LOCATION);
+
+ UserRepresentation newUser = new UserRepresentation();
+ String testUsername = "defaultrole tester";
+ newUser.setUsername(testUsername);
+ setPasswordFor(newUser, PASSWORD);
+
+ Map<String,String> ldapConfig = ldapTestConfiguration.getLDAPConfig();
+
+ //addLdapProviderTest
+ configure().userFederation();
+ userFederationPage.addProvider("ldap");
+ ldapUserProviderForm.configureLdap(ldapConfig.get(LDAPConstants.LDAP_PROVIDER), ldapConfig.get(LDAPConstants.EDIT_MODE), ldapConfig.get(LDAPConstants.VENDOR), ldapConfig.get(LDAPConstants.CONNECTION_URL), ldapConfig.get(LDAPConstants.USERS_DN), ldapConfig.get(LDAPConstants.BIND_DN), ldapConfig.get(LDAPConstants.BIND_CREDENTIAL));
+ }
+
+ @Ignore
+ @Test
+ public void caseSensitiveSearch() {
+ // This should fail for now due to case-sensitivity
+ adminConsolePage.navigateTo();
+ testRealmLoginPage.form().login("johnKeycloak", "Password1");
+ assertTrue(flashMessage.getText(), flashMessage.isDanger());
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/AbstractRealmTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/AbstractRealmTest.java
new file mode 100644
index 0000000..84de989
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/AbstractRealmTest.java
@@ -0,0 +1,27 @@
+package org.keycloak.testsuite.console.realm;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.realm.RealmSettings;
+import org.keycloak.testsuite.console.page.realm.RealmSettings.RealmTabs;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractRealmTest extends AbstractConsoleTest {
+
+ @Page
+ protected RealmSettings realmSettingsPage;
+
+ public RealmTabs tabs() {
+ return realmSettingsPage.tabs();
+ }
+
+ @Before
+ public void beforeRealmTest() {
+// configure().realmSettings();
+ }
+
+}
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
new file mode 100644
index 0000000..13edb9d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/LoginSettingsTest.java
@@ -0,0 +1,266 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.realm;
+
+import java.util.HashSet;
+import java.util.Set;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Assert;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserAndResetPasswordWithAdminClient;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.console.page.realm.LoginSettings;
+import org.keycloak.testsuite.auth.page.login.Registration;
+import org.keycloak.testsuite.auth.page.login.ResetCredentials;
+import org.keycloak.testsuite.auth.page.login.VerifyEmail;
+import org.keycloak.testsuite.console.page.realm.LoginSettings.RequireSSLOption;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+import org.openqa.selenium.Cookie;
+
+/**
+ *
+ * @author tkyjovsk
+ * @author vramik
+ */
+public class LoginSettingsTest extends AbstractRealmTest {
+
+ private static final String NEW_USERNAME = "newUsername";
+
+ @Page
+ private LoginSettings loginSettingsPage;
+ @Page
+ private Registration testRealmRegistrationPage;
+ @Page
+ private ResetCredentials testRealmForgottenPasswordPage;
+ @Page
+ private VerifyEmail testRealmVerifyEmailPage;
+ @Page
+ private Account testAccountPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmRegistrationPage.setAuthRealm(TEST);
+ testRealmForgottenPasswordPage.setAuthRealm(TEST);
+ testRealmVerifyEmailPage.setAuthRealm(TEST);
+ testAccountPage.setAuthRealm(TEST);
+ }
+
+ @Before
+ public void beforeLoginSettingsTest() {
+// tabs().login();
+ loginSettingsPage.navigateTo();
+ assertCurrentUrlEquals(loginSettingsPage);
+ }
+
+ @Test
+ public void userRegistration() {
+
+ log.info("enabling registration");
+ loginSettingsPage.form().setRegistrationAllowed(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().register();
+ assertCurrentUrlStartsWith(testRealmRegistrationPage);
+ testRealmRegistrationPage.waitForConfirmPasswordInputPresent();
+ testRealmRegistrationPage.waitForUsernameInputPresent();
+ log.info("verified registration is enabled");
+
+ // test email as username
+ log.info("enabling email as username");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setEmailAsUsername(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().register();
+ assertCurrentUrlStartsWith(testRealmRegistrationPage);
+ testRealmRegistrationPage.waitForConfirmPasswordInputPresent();
+ testRealmRegistrationPage.waitForUsernameInputNotPresent();
+ log.info("verified email as username");
+
+ // test user reg. disabled
+ log.info("disabling registration");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setRegistrationAllowed(false);
+ loginSettingsPage.form().save();
+ assertFalse(loginSettingsPage.form().isRegistrationAllowed());
+ log.debug("disabled");
+
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().waitForRegisterLinkNotPresent();
+ log.info("verified regisration is disabled");
+ }
+
+ @Test
+ public void editUsername() {
+ log.info("enabling edit username");
+ loginSettingsPage.form().setEditUsernameAllowed(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ log.info("edit username");
+ testAccountPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ testAccountPage.waitForAccountLinkPresent();
+ testAccountPage.setUsername(NEW_USERNAME);
+ testAccountPage.save();
+ testAccountPage.signOut();
+ log.debug("edited");
+
+ log.info("log in with edited username");
+ testRealmLoginPage.form().login(NEW_USERNAME, PASSWORD);
+ testAccountPage.waitForAccountLinkPresent();
+ log.debug("user is logged in with edited username");
+
+ log.info("disabling edit username");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setEditUsernameAllowed(false);
+ loginSettingsPage.form().save();
+ log.debug("disabled");
+
+
+ }
+
+ @Test
+ public void resetPassword() {
+
+ log.info("enabling reset password");
+ loginSettingsPage.form().setResetPasswordAllowed(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().forgotPassword();
+
+ Assert.assertEquals("Enter your username or email address and we will send you instructions on how to create a new password.",
+ testRealmForgottenPasswordPage.getInfoMessage());
+ log.info("verified reset password is enabled");
+
+ log.info("disabling reset password");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setResetPasswordAllowed(false);
+ loginSettingsPage.form().save();
+ assertFalse(loginSettingsPage.form().isResetPasswordAllowed());
+ log.debug("disabled");
+
+ testRealmAdminConsolePage.navigateTo();
+ testRealmLoginPage.form().waitForResetPasswordLinkNotPresent();
+ log.info("verified reset password is disabled");
+ }
+
+ @Test
+ public void rememberMe() {
+
+ log.info("enabling remember me");
+ loginSettingsPage.form().setRememberMeAllowed(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ log.info("login with remember me checked");
+ testAccountPage.navigateTo();
+ testRealmLoginPage.form().rememberMe(true);
+ testRealmLoginPage.form().login(testUser);
+
+ assertTrue("Cookie KEYCLOAK_REMEMBER_ME should be present.", getCookieNames().contains("KEYCLOAK_REMEMBER_ME"));
+
+ log.info("verified remember me is enabled");
+
+ log.info("disabling remember me");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setRememberMeAllowed(false);
+ loginSettingsPage.form().save();
+ assertFalse(loginSettingsPage.form().isRememberMeAllowed());
+ log.debug("disabled");
+
+ testAccountPage.navigateTo();
+ testAccountPage.signOut();
+ testRealmLoginPage.form().waitForLoginButtonPresent();
+ testRealmLoginPage.form().waitForRememberMeNotPresent();
+ log.info("verified remember me is disabled");
+
+ }
+
+ @Test
+ public void verifyEmail() {
+
+ log.info("enabling verify email");
+ loginSettingsPage.form().setVerifyEmailAllowed(true);
+ loginSettingsPage.form().save();
+ log.debug("enabled");
+
+ testAccountPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ Assert.assertEquals("Failed to send email, please try again later.",
+ testRealmVerifyEmailPage.getErrorMessage());
+
+ log.info("verified verify email is enabled");
+
+ log.info("disabling verify email");
+ loginSettingsPage.navigateTo();
+ loginSettingsPage.form().setVerifyEmailAllowed(false);
+ loginSettingsPage.form().save();
+ assertFalse(loginSettingsPage.form().isVerifyEmailAllowed());
+ log.debug("disabled");
+
+ log.debug("create new test user");
+ UserRepresentation newUser = createUserRepresentation("new_user", "new_user@email.test", "new", "user", true);
+ setPasswordFor(newUser, PASSWORD);
+ String id = createUserAndResetPasswordWithAdminClient(testRealmResource(), newUser, PASSWORD);
+ newUser.setId(id);
+
+ log.info("log in as new user");
+ testAccountPage.navigateTo();
+ testRealmLoginPage.form().login(newUser);
+ testAccountPage.waitForAccountLinkPresent();
+
+ log.info("verified verify email is disabled");
+ }
+
+ @Test
+ public void requireSSLAllRequests() throws InterruptedException {
+ log.info("set require ssl for all requests");
+ loginSettingsPage.form().selectRequireSSL(RequireSSLOption.all);
+ loginSettingsPage.form().save();
+ log.debug("set");
+
+ log.info("check HTTPS required");
+ testAccountPage.navigateTo();
+ Assert.assertEquals("HTTPS required", testAccountPage.getErrorMessage());
+ }
+
+ private Set<String> getCookieNames() {
+ Set<Cookie> cookies = driver.manage().getCookies();
+ Set<String> cookieNames = new HashSet<>();
+ for (Cookie cookie : cookies) {
+ cookieNames.add(cookie.getName());
+ }
+ return cookieNames;
+ }
+}
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
new file mode 100644
index 0000000..84a9930
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/realm/SecurityDefensesTest.java
@@ -0,0 +1,255 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.realm;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.console.page.realm.SecurityDefenses;
+import org.keycloak.testsuite.console.page.users.UserAttributes;
+import org.keycloak.testsuite.console.page.users.Users;
+import org.openqa.selenium.By;
+
+import java.util.Date;
+
+import static org.jboss.arquillian.graphene.Graphene.waitGui;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ * @author Filip Kiss
+ * @author mhajas
+ */
+public class SecurityDefensesTest extends AbstractRealmTest {
+
+ @Page
+ private SecurityDefenses.BruteForceDetection bruteForceDetectionPage;
+
+ @Page
+ private Account testRealmAccountPage;
+
+ @Page
+ private Users usersPage;
+
+ @Page
+ private UserAttributes userAttributesPage;
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmAccountPage.setAuthRealm(TEST);
+ }
+
+ @Before
+ public void beforeSecurityDefensesTest() {
+ bruteForceDetectionPage.navigateTo();
+ }
+
+ @Test
+ public void maxLoginFailuresTest() {
+ int secondsToWait = 3;
+
+ bruteForceDetectionPage.form().setProtectionEnabled(true);
+ bruteForceDetectionPage.form().setMaxLoginFailures("1");
+ bruteForceDetectionPage.form().setWaitIncrementSelect(SecurityDefenses.TimeSelectValues.SECONDS);
+ bruteForceDetectionPage.form().setWaitIncrementInput(String.valueOf(secondsToWait));
+ bruteForceDetectionPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD + "-mismatch");
+
+ testRealmLoginPage.form().login(testUser);
+ waitForFeedbackText("Invalid username or password.");
+ Date endTime = new Date(new Date().getTime() + secondsToWait * 1000);
+
+ testRealmLoginPage.form().login(testUser);
+ waitGui().until().element(By.className("instruction"))
+ .text().contains("Account is temporarily disabled, contact admin or try again later.");
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+ testRealmAccountPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+
+ while (new Date().compareTo(endTime) < 0) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ setPasswordFor(testUser, PASSWORD);
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void quickLoginCheck() {
+ int secondsToWait = 3;
+
+ bruteForceDetectionPage.form().setProtectionEnabled(true);
+ bruteForceDetectionPage.form().setMaxLoginFailures("100");
+ bruteForceDetectionPage.form().setQuickLoginCheckInput("1500");
+ bruteForceDetectionPage.form().setMinQuickLoginWaitSelect(SecurityDefenses.TimeSelectValues.SECONDS);
+ bruteForceDetectionPage.form().setMinQuickLoginWaitInput(String.valueOf(secondsToWait));
+ bruteForceDetectionPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD + "-mismatch");
+
+ testRealmLoginPage.form().login(testUser);
+ testRealmLoginPage.form().login(testUser);
+ Date endTime = new Date(new Date().getTime() + secondsToWait * 1000);
+ testRealmLoginPage.form().login(testUser);
+ waitGui().until().element(By.className("instruction"))
+ .text().contains("Account is temporarily disabled, contact admin or try again later.");
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+
+ testRealmAccountPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+
+ while (new Date().compareTo(endTime) < 0) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ setPasswordFor(testUser, PASSWORD);
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void maxWaitLoginFailures() {
+ int secondsToWait = 5;
+
+ bruteForceDetectionPage.form().setProtectionEnabled(true);
+ bruteForceDetectionPage.form().setMaxLoginFailures("1");
+ bruteForceDetectionPage.form().setMaxWaitSelect(SecurityDefenses.TimeSelectValues.SECONDS);
+ bruteForceDetectionPage.form().setMaxWaitInput(String.valueOf(secondsToWait));
+ bruteForceDetectionPage.form().save();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD + "-mismatch");
+
+ testRealmLoginPage.form().login(testUser);
+ Date endTime = new Date(new Date().getTime() + secondsToWait * 1000);
+ waitForFeedbackText("Invalid username or password.");
+
+ testRealmLoginPage.form().login(testUser);
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+ waitGui().until().element(By.className("instruction"))
+ .text().contains("Account is temporarily disabled, contact admin or try again later.");
+ testRealmAccountPage.navigateTo();
+ testRealmLoginPage.form().login(testUser);
+ endTime = new Date(endTime.getTime() + secondsToWait * 1000);
+ waitForFeedbackText("Account is temporarily disabled, contact admin or try again later.");
+
+ while (new Date().compareTo(endTime) < 0) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ setPasswordFor(testUser, PASSWORD);
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void failureResetTime() {
+ int secondsToWait = 3;
+
+ bruteForceDetectionPage.form().setProtectionEnabled(true);
+ bruteForceDetectionPage.form().setMaxLoginFailures("2");
+ bruteForceDetectionPage.form().setFailureResetTimeSelect(SecurityDefenses.TimeSelectValues.SECONDS);
+ bruteForceDetectionPage.form().setFailureResetTimeInput(String.valueOf(secondsToWait));
+ bruteForceDetectionPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD + "-mismatch");
+
+ testRealmLoginPage.form().login(testUser);
+ waitForFeedbackText("Invalid username or password.");
+ Date endTime = new Date(new Date().getTime() + secondsToWait * 1000);
+
+ while (new Date().compareTo(endTime) < 0) {
+ try {
+ Thread.sleep(50);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }
+ }
+
+ testRealmLoginPage.form().login(testUser);
+ waitForFeedbackText("Invalid username or password.");
+
+ setPasswordFor(testUser, PASSWORD);
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void userUnlockTest() {
+ bruteForceDetectionPage.form().setProtectionEnabled(true);
+ bruteForceDetectionPage.form().setMaxLoginFailures("1");
+ bruteForceDetectionPage.form().setWaitIncrementSelect(SecurityDefenses.TimeSelectValues.MINUTES);
+ bruteForceDetectionPage.form().setWaitIncrementInput("10");
+ bruteForceDetectionPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD + "-mismatch");
+
+ testRealmLoginPage.form().login(testUser);
+
+ usersPage.navigateTo();
+
+ usersPage.table().searchUsers(testUser.getUsername());
+ usersPage.table().editUser(testUser.getUsername());
+ userAttributesPage.form().unlockUser();
+
+ testRealmAccountPage.navigateTo();
+
+ setPasswordFor(testUser, PASSWORD);
+
+ testRealmLoginPage.form().login(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ private void waitForFeedbackText(String text) {
+ waitGui().until().element(By.className("kc-feedback-text"))
+ .text().contains(text);
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/AbstractRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/AbstractRolesTest.java
new file mode 100644
index 0000000..701e798
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/AbstractRolesTest.java
@@ -0,0 +1,26 @@
+package org.keycloak.testsuite.console.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.roles.Roles;
+import org.keycloak.testsuite.console.page.users.User;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractRolesTest extends AbstractConsoleTest {
+
+ @Page
+ protected Roles rolesPage;
+
+ @Page
+ protected User userPage;
+
+ @Before
+ public void beforeRolesTest() {
+// configure().roles();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/DefaultRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/DefaultRolesTest.java
new file mode 100644
index 0000000..e156c39
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/DefaultRolesTest.java
@@ -0,0 +1,61 @@
+package org.keycloak.testsuite.console.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+import static org.junit.Assert.assertTrue;
+import org.junit.Before;
+import org.junit.Test;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.createUserWithAdminClient;
+import org.keycloak.testsuite.console.page.roles.DefaultRoles;
+import org.keycloak.testsuite.console.page.users.UserRoleMappings;
+import org.keycloak.testsuite.console.page.users.Users;
+
+/**
+ * Created by fkiss.
+ */
+public class DefaultRolesTest extends AbstractRolesTest {
+
+ @Page
+ private DefaultRoles defaultRolesPage;
+
+ @Page
+ private UserRoleMappings userRolesPage;
+
+ private RoleRepresentation defaultRoleRep;
+
+ @Page
+ private Users users;
+
+ @Before
+ public void beforeDefaultRolesTest() {
+ // create a role via admin client
+ defaultRoleRep = new RoleRepresentation("default-role", "", false);
+ rolesPage.rolesResource().create(defaultRoleRep);
+
+ defaultRolesPage.navigateTo();
+ // navigate to default roles page
+// rolesPage.tabs().defaultRoles();
+ }
+
+ @Test
+ public void defaultRoleAssignedToNewUser() {
+
+ String defaultRoleName = defaultRoleRep.getName();
+
+ defaultRolesPage.form().addAvailableRole(defaultRoleName);
+ assertFlashMessageSuccess();
+
+ UserRepresentation newUser = new UserRepresentation();
+ newUser.setUsername("new_user");
+
+ createUserWithAdminClient(testRealmResource(), newUser);
+ users.navigateTo();
+ users.table().search(newUser.getUsername());
+ users.table().clickUser(newUser.getUsername());
+
+ userPage.tabs().roleMappings();
+ assertTrue(userRolesPage.form().isAssignedRole(defaultRoleName));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/RealmRolesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/RealmRolesTest.java
new file mode 100644
index 0000000..0df9c15
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/roles/RealmRolesTest.java
@@ -0,0 +1,229 @@
+package org.keycloak.testsuite.console.roles;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+import org.keycloak.testsuite.console.page.roles.RealmRoles;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.keycloak.representations.idm.RoleRepresentation;
+import org.keycloak.testsuite.console.page.roles.CreateRole;
+import org.keycloak.testsuite.console.page.roles.Role;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.keycloak.testsuite.util.Timer;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class RealmRolesTest extends AbstractRolesTest {
+
+ @Page
+ private RealmRoles realmRolesPage;
+ @Page
+ private CreateRole createRolePage;
+ @Page
+ private Role rolePage;
+
+ private RoleRepresentation testRole;
+
+ @Before
+ public void beforeTestAddNewRole() {
+ testRole = new RoleRepresentation("test_role", "role description", false);
+ realmRolesPage.navigateTo();
+ }
+
+ public void addRole(RoleRepresentation roleRep) {
+ assertCurrentUrlEquals(realmRolesPage);
+ realmRolesPage.table().addRole();
+ assertCurrentUrlEquals(createRolePage);
+ createRolePage.form().setBasicAttributes(roleRep);
+ createRolePage.form().save();
+ assertFlashMessageSuccess();
+ createRolePage.form().setCompositeRoles(roleRep);
+ // TODO add verification of notification message when KEYCLOAK-1497 gets resolved
+ }
+
+ public void updateRole(RoleRepresentation roleRep) {
+ assertCurrentUrlEquals(realmRolesPage);
+ realmRolesPage.table().editRole(roleRep.getName());
+// assertCurrentUrl(role); // can't do this, role id needed as uri param
+ rolePage.form().setBasicAttributes(roleRep);
+ rolePage.form().save();
+ assertFlashMessageSuccess();
+ rolePage.form().setCompositeRoles(roleRep);
+ }
+
+ public void assertBasicRoleAttributesEqual(RoleRepresentation r1, RoleRepresentation r2) {
+ assertEquals(r1.getName(), r2.getName());
+ assertEquals(r1.getDescription(), r2.getDescription());
+ assertEquals(r1.isComposite(), r2.isComposite());
+ }
+
+ @Test
+ public void crudRole() {
+ addRole(testRole);
+
+ configure().roles();
+ RoleRepresentation foundRole = realmRolesPage.table().findRole(testRole.getName()); // search & get role from table
+ assertBasicRoleAttributesEqual(testRole, foundRole);
+ realmRolesPage.table().editRole(testRole.getName());
+ foundRole = rolePage.form().getBasicAttributes();
+ assertBasicRoleAttributesEqual(testRole, foundRole);
+
+ testRole.setDescription("updated role description");
+ rolePage.form().setDescription(testRole.getDescription());
+ rolePage.form().save();
+ assertFlashMessageSuccess();
+
+ configure().roles();
+ foundRole = realmRolesPage.table().findRole(testRole.getName()); // search & get role from table
+ assertBasicRoleAttributesEqual(testRole, foundRole);
+
+ // delete from table
+ realmRolesPage.table().deleteRole(testRole.getName());
+ modalDialog.cancel();
+ assertTrue(realmRolesPage.table().containsRole(testRole.getName()));
+ realmRolesPage.table().deleteRole(testRole.getName());
+ modalDialog.confirmDeletion();
+ pause(250);
+ assertFalse(realmRolesPage.table().containsRole(testRole.getName()));
+
+ // add again
+ addRole(testRole);
+ // delete from page
+ rolePage.form().delete();
+ modalDialog.confirmDeletion();
+ assertCurrentUrlEquals(realmRolesPage);
+ }
+
+ @Test
+ @Ignore
+ public void testAddRoleWithLongName() {
+ String name = "hjewr89y1894yh98(*&*&$jhjkashd)*(&y8934h*&@#hjkahsdj";
+ addRole(new RoleRepresentation(name, "", false));
+ assertNotNull(realmRolesPage.table().findRole(name));
+ }
+
+ @Test
+ public void testAddExistingRole() {
+ addRole(testRole);
+ assertFlashMessageSuccess();
+
+ configure().roles();
+ realmRolesPage.table().addRole();
+ createRolePage.form().setBasicAttributes(testRole);
+ createRolePage.form().save();
+ assertFlashMessageDanger();
+ }
+
+ public void createTestRoles(String namePrefix, int count) {
+ Timer.time();
+ for (int i = 0; i < count; i++) {
+ String roleName = String.format("%s%02d", namePrefix, i);
+ RoleRepresentation rr = new RoleRepresentation(roleName, "", false);
+ testRealmResource().roles().create(rr);
+ }
+ Timer.time("create " + count + " roles");
+ }
+
+// @Test
+ public void rolesPagination() {
+ createTestRoles("test_role_", 100);
+ realmRolesPage.navigateTo();
+ pause(100000);
+ }
+
+// @Test
+// public void addAndRemoveUserAndAssignRole() {
+// roleMappings.form().addAvailableRole("create-realm");
+// assertFlashMessageSuccess();
+//
+// roleMappings.form().removeAssignedRole("create-realm");
+// assertFlashMessageSuccess();
+//
+// users.navigateTo();
+// users.table().deleteUser(testUsername);
+// }
+// @Test // this should be moved to users tests
+// public void testRoleIsAvailableForUsers() {
+// RoleRepresentation role = new RoleRepresentation("User role", "");
+// roles.addRole(role);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// users.navigateTo();
+// users.viewAllUsers();
+// users.clickUser("admin");
+// user.tabs().roleMappings();
+// Select rolesSelect = new Select(driver.findElement(id("available")));
+// assertEquals("User role should be present in admin role mapping",
+// role.getName(), rolesSelect.getOptions().get(0).getText());
+// roles.navigateTo();
+// roles.deleteRole(role);
+// }
+//
+// @Ignore//KEYCLOAK-1497
+// @Test
+// public void testAddCompositeRole() {
+// UserRepresentation testUserRep = new UserRepresentation();
+// testUserRep.setUsername("usercomposite");
+//
+// RoleRepresentation compositeRole = new RoleRepresentation("compositeRole", "");
+// RoleRepresentation subRole1 = new RoleRepresentation("subRole1", "");
+// RoleRepresentation subRole2 = new RoleRepresentation("subRole2", "");
+// List<RoleRepresentation> testRoles = new ArrayList<>();
+// compositeRole.setComposite(true);
+// testRoles.add(compositeRole);
+// testRoles.add(subRole1);
+// testRoles.add(subRole2);
+//
+// //create roles and user
+// for (RoleRepresentation role : testRoles) {
+// roles.addRole(role);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+// roles.navigateTo();
+// assertEquals(role.getName(), roles.findRole(role.getName()).getName());
+// }
+// users.navigateTo();
+// createUser(testUserRep);
+// flashMessage.waitUntilPresent();
+// assertTrue(flashMessage.getText(), flashMessage.isSuccess());
+//
+// //adding subroles to composite role
+// roles.navigateTo();
+// roles.findRole(compositeRole.getName());
+// roles.clickRole(compositeRole);
+// roles.setCompositeRole(compositeRole);
+// roleMappings.addAvailableRole(subRole1.getName(), subRole2.getName());
+// //flashMessage.waitUntilPresent();
+// //assertTrue(flashMessage.getText(), flashMessage.isSuccess()); KEYCLOAK-1497
+//
+// //check if subroles work as expected
+// users.navigateTo();
+// users.findUser(testUserRep.getUsername());
+// users.clickUser(testUserRep.getUsername());
+// user.tabs().roleMappings();
+// roleMappings.addAvailableRole(compositeRole.getName());
+// assertTrue(roleMappings.isEffectiveRealmRolesComplete(compositeRole, subRole1, subRole2));
+//
+// //delete everything
+// roles.navigateTo();
+// roles.deleteRole(compositeRole);
+// roles.navigateTo();
+// roles.deleteRole(subRole1);
+// roles.navigateTo();
+// roles.deleteRole(subRole2);
+// try {
+// Thread.sleep(2000);
+// } catch (InterruptedException e) {
+// e.printStackTrace();
+// }
+// users.navigateTo();
+// users.deleteUser(testUserRep.getUsername());
+// }
+//
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/AbstractUserTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/AbstractUserTest.java
new file mode 100644
index 0000000..3ddf899
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/AbstractUserTest.java
@@ -0,0 +1,39 @@
+package org.keycloak.testsuite.console.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.keycloak.representations.idm.UserRepresentation;
+import org.keycloak.testsuite.console.AbstractConsoleTest;
+import org.keycloak.testsuite.console.page.users.CreateUser;
+import org.keycloak.testsuite.console.page.users.Users;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlEquals;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public abstract class AbstractUserTest extends AbstractConsoleTest {
+
+ @Page
+ protected Users usersPage;
+ @Page
+ protected CreateUser createUserPage;
+
+ protected UserRepresentation newTestRealmUser;
+
+ @Before
+ public void beforeUserTest() {
+ newTestRealmUser = new UserRepresentation();
+// manage().users();
+ }
+
+ public void createUser(UserRepresentation user) {
+ assertCurrentUrlEquals(usersPage);
+ usersPage.table().addUser();
+ assertCurrentUrlStartsWith(createUserPage);
+ createUserPage.form().setValues(user);
+ createUserPage.form().save();
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/RequiredUserActionsTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/RequiredUserActionsTest.java
new file mode 100644
index 0000000..9151698
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/RequiredUserActionsTest.java
@@ -0,0 +1,147 @@
+package org.keycloak.testsuite.console.users;
+
+import static org.jboss.arquillian.graphene.Graphene.waitGui;
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Before;
+import org.junit.Test;
+import static org.keycloak.representations.idm.CredentialRepresentation.PASSWORD;
+import static org.keycloak.testsuite.auth.page.AuthRealm.TEST;
+import org.keycloak.testsuite.auth.page.account.Account;
+import org.keycloak.testsuite.auth.page.login.UpdateAccount;
+import org.keycloak.testsuite.auth.page.login.UpdatePassword;
+import org.keycloak.testsuite.console.page.authentication.RequiredActions;
+import org.keycloak.testsuite.console.page.users.UserAttributes;
+
+import static org.keycloak.testsuite.model.RequiredUserAction.TERMS_AND_CONDITIONS;
+import static org.keycloak.testsuite.model.RequiredUserAction.UPDATE_PASSWORD;
+import static org.keycloak.testsuite.model.RequiredUserAction.UPDATE_PROFILE;
+import static org.keycloak.testsuite.util.URLAssert.assertCurrentUrlStartsWith;
+
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebElement;
+import org.openqa.selenium.support.FindBy;
+
+/**
+ *
+ * @author tkyjovsk
+ * @author mhajas
+ */
+public class RequiredUserActionsTest extends AbstractUserTest {
+
+ @Page
+ private UserAttributes userAttributesPage;
+
+ @Page
+ private Account testRealmAccountPage;
+
+ @Page
+ private UpdateAccount testRealmUpdateAccountPage;
+
+ @Page
+ private UpdatePassword testRealmUpdatePasswordPage;
+
+ @Page
+ private RequiredActions requiredActionsPage;
+
+ @FindBy(css = "kc-feedback-text")
+ protected WebElement feedbackText;
+
+ public void waitForFeedbackText(String text) {
+ waitGui().until().element(By.className("kc-feedback-text"))
+ .text().contains(text);
+ }
+
+ @Override
+ public void setDefaultPageUriParameters() {
+ super.setDefaultPageUriParameters();
+ testRealmAccountPage.setAuthRealm(TEST);
+ testRealmUpdateAccountPage.setAuthRealm(TEST);
+ testRealmUpdatePasswordPage.setAuthRealm(TEST);
+ }
+
+ @Before
+ public void beforeRequiredActionsTest() {
+// usersPage.table().viewAllUsers();
+// usersPage.table().clickUser(testUser.getUsername());
+ userAttributesPage.setId(testUser.getId());
+ userAttributesPage.navigateTo();
+ }
+
+ @Test
+ public void updatePassword() {
+ userAttributesPage.form().addRequiredAction(UPDATE_PASSWORD.getActionName());
+ userAttributesPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ testRealmLoginPage.form().login(testUser);
+ waitForFeedbackText("You need to change your password to activate your account.");
+
+ testRealmUpdatePasswordPage.updatePasswords(null, null);
+ waitForFeedbackText("Please specify password.");
+
+ testRealmUpdatePasswordPage.updatePasswords(PASSWORD, null);
+ waitForFeedbackText("Passwords don't match.");
+
+ testRealmUpdatePasswordPage.updatePasswords(PASSWORD, PASSWORD + "-mismatch");
+ waitForFeedbackText("Passwords don't match.");
+
+ testRealmUpdatePasswordPage.updatePasswords(PASSWORD, PASSWORD);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void updateProfile() {
+ userAttributesPage.form().addRequiredAction(UPDATE_PROFILE.getActionName());
+ userAttributesPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ testRealmLoginPage.form().login(testUser);
+ waitForFeedbackText("You need to update your user profile to activate your account.");
+
+ testUser.setEmail(null);
+ testUser.setFirstName(null);
+ testUser.setLastName(null);
+ testRealmUpdateAccountPage.updateAccount(testUser);
+ waitForFeedbackText("Please specify email.");
+
+ testUser.setEmail("test@email.test");
+ testRealmUpdateAccountPage.updateAccount(testUser);
+ waitForFeedbackText("Please specify first name.");
+
+ testUser.setFirstName("test");
+ testRealmUpdateAccountPage.updateAccount(testUser);
+ waitForFeedbackText("Please specify last name.");
+
+ testUser.setLastName("user");
+ testRealmUpdateAccountPage.updateAccount(testUser);
+ assertCurrentUrlStartsWith(testRealmAccountPage);
+ }
+
+ @Test
+ public void termsAndConditions() {
+ requiredActionsPage.navigateTo();
+ requiredActionsPage.clickTermsAndConditionEnabled();
+
+ manage().users();
+ usersPage.table().viewAllUsers();
+ usersPage.table().clickUser(testUser.getUsername());
+
+ userAttributesPage.form().addRequiredAction(TERMS_AND_CONDITIONS.getActionName());
+ userAttributesPage.form().save();
+ assertFlashMessageSuccess();
+
+ testRealmAccountPage.navigateTo();
+
+ testRealmLoginPage.form().login(testUser);
+
+ driver.findElement(By.xpath("//div[@id='kc-header-wrapper' and text()[contains(.,'Terms and Conditions')]]"));
+ }
+
+
+
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UserAttributesTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UserAttributesTest.java
new file mode 100644
index 0000000..9d99c85
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UserAttributesTest.java
@@ -0,0 +1,90 @@
+/*
+ * JBoss, Home of Professional Open Source
+ *
+ * Copyright 2013 Red Hat, Inc. and/or its affiliates.
+ *
+ * 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.testsuite.console.users;
+
+import org.jboss.arquillian.graphene.page.Page;
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+import org.junit.Before;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.Users.setPasswordFor;
+import org.keycloak.testsuite.console.page.users.UserAttributes;
+
+/**
+ *
+ * @author Filip Kiss
+ * @author tkyjovsk
+ */
+public class UserAttributesTest extends AbstractUserTest {
+
+ @Page
+ private UserAttributes userAttributesPage;
+
+ @Before
+ public void beforeUserAttributesTest() {
+ usersPage.navigateTo();
+ }
+
+ @Test
+ public void invalidEmail() {
+ String testUsername = "testUserInvEmail";
+ String invalidEmail = "user.redhat.com";
+ newTestRealmUser.setUsername(testUsername);
+ setPasswordFor(newTestRealmUser, "pass");
+ newTestRealmUser.setEmail(invalidEmail);
+ createUser(newTestRealmUser);
+ assertFlashMessageDanger();
+
+ userAttributesPage.backToUsersViaBreadcrumb();
+ assertNull(usersPage.table().findUser(testUsername));
+ }
+
+ @Test
+ public void noUsername() {
+ createUser(newTestRealmUser);
+ assertFlashMessageDanger();
+ }
+
+ @Test
+ public void existingUser() {
+ String testUsername = "test_duplicated_user";
+ newTestRealmUser.setUsername(testUsername);
+ createUser(newTestRealmUser);
+ assertFlashMessageSuccess();
+
+ userAttributesPage.backToUsersViaBreadcrumb();
+ assertNotNull(usersPage.table().findUser(testUsername));
+
+ UserRepresentation testUser2 = new UserRepresentation();
+ testUser2.setUsername(testUsername);
+ createUser(testUser2);
+ assertFlashMessageDanger();
+ }
+
+ @Test
+ public void disabledUser() {
+ UserRepresentation disabledUser = new UserRepresentation();
+ disabledUser.setEnabled(false);
+ disabledUser.setUsername("disabled_user");
+ createUser(disabledUser);
+ assertFlashMessageSuccess();
+ // TODO try to log in
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UsersTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UsersTest.java
new file mode 100644
index 0000000..ed4000d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/console/users/UsersTest.java
@@ -0,0 +1,52 @@
+package org.keycloak.testsuite.console.users;
+
+import javax.ws.rs.core.Response;
+import org.junit.Before;
+import org.junit.Ignore;
+import org.junit.Test;
+import org.keycloak.representations.idm.UserRepresentation;
+import static org.keycloak.testsuite.admin.ApiUtil.getCreatedId;
+import static org.keycloak.testsuite.util.WaitUtils.pause;
+import org.keycloak.testsuite.util.Timer;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class UsersTest extends AbstractUserTest {
+
+ @Before
+ public void beforeUserAttributesTest() {
+ usersPage.navigateTo();
+ }
+
+ public void createTestUsers(String usernamePrefix, int count) {
+// Timer.time();
+ for (int i = 0; i < count; i++) {
+ String username = String.format("%s%03d", usernamePrefix, i);
+ UserRepresentation u = createUserRepresentation(
+ username,
+ username + "@email.test",
+ "First",
+ "Last",
+ true);
+ Timer.time();
+ Response r = testRealmResource().users().create(u);
+ String id = getCreatedId(r);
+ r.close();
+ Timer.time("create user");
+ }
+// Timer.time("create " + count + " users");
+ }
+
+ @Test
+ @Ignore
+ public void usersPagination() {
+ createTestUsers("test_user_", 100);
+
+ usersPage.navigateTo();
+ usersPage.table().viewAllUsers();
+ pause(120000);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AttributesAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AttributesAssert.java
new file mode 100644
index 0000000..2974a90
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/AttributesAssert.java
@@ -0,0 +1,42 @@
+package org.keycloak.testsuite.util;
+
+import java.util.List;
+import static org.junit.Assert.assertEquals;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class AttributesAssert {
+
+ public static void assertEqualsStringAttributes(String a1, String a2) {
+ if (a1 == null) {
+ a1 = "";
+ }
+ if (a2 == null) {
+ a2 = "";
+ }
+ assertEquals(a1, a2);
+ }
+
+ public static void assertEqualsBooleanAttributes(Boolean a1, Boolean a2) {
+ if (a1 == null) {
+ a1 = false;
+ }
+ if (a2 == null) {
+ a2 = false;
+ }
+ assertEquals(a1, a2);
+ }
+
+ public static void assertEqualsListAttributes(List a1, List a2) {
+ if (a1 == null || a1.isEmpty()) {
+ a1 = null;
+ }
+ if (a2 == null || a2.isEmpty()) {
+ a2 = null;
+ }
+ assertEquals(a1, a2);
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java
new file mode 100644
index 0000000..4ade10b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailAssert.java
@@ -0,0 +1,54 @@
+package org.keycloak.testsuite.util;
+
+import java.io.IOException;
+import javax.mail.MessagingException;
+
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMessage.RecipientType;
+import javax.mail.internet.MimeMultipart;
+import org.jboss.logging.Logger;
+import static org.junit.Assert.*;
+
+public class MailAssert {
+
+ private static final Logger log = Logger.getLogger(MailAssert.class);
+
+ public static String assertEmailAndGetUrl(String from, String recipient, String content) {
+
+ try {
+ MimeMessage message = MailServer.getLastReceivedMessage();
+ assertNotNull("There is no received email.", message);
+ assertEquals(recipient, message.getRecipients(RecipientType.TO)[0].toString());
+ assertEquals(from, message.getFrom()[0].toString());
+
+ String messageContent;
+ if (message.getContent() instanceof MimeMultipart) {
+ MimeMultipart mimeMultipart = (MimeMultipart) message.getContent();
+
+ messageContent = String.valueOf(mimeMultipart.getBodyPart(0).getContent());
+ } else {
+ messageContent = String.valueOf(message.getContent());
+ }
+ logMessageContent(messageContent);
+ String errorMessage = "Email content should contains \"" + content
+ + "\", but it doesn't.\nEmail content:\n" + messageContent + "\n";
+
+ assertTrue(errorMessage, messageContent.contains(content));
+ for (String string : messageContent.split("\n")) {
+ if (string.contains("http://")) {
+ return string;
+ }
+ }
+ return null;
+ } catch (IOException | MessagingException | InterruptedException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private static void logMessageContent(String messageContent) {
+ log.debug("---------------------");
+ log.debug(messageContent);
+ log.debug("---------------------");
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailServer.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailServer.java
new file mode 100644
index 0000000..9d3a8c1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/MailServer.java
@@ -0,0 +1,90 @@
+package org.keycloak.testsuite.util;
+
+import com.icegreen.greenmail.util.GreenMail;
+import com.icegreen.greenmail.util.ServerSetup;
+import java.io.IOException;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.net.SocketException;
+import javax.mail.MessagingException;
+
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMessage.RecipientType;
+import javax.mail.internet.MimeMultipart;
+import org.jboss.logging.Logger;
+import static org.keycloak.testsuite.util.MailServerConfiguration.*;
+
+public class MailServer {
+
+ private static final Logger log = Logger.getLogger(MailServer.class);
+
+ private static GreenMail greenMail;
+
+ public static void main(String[] args) throws Exception {
+ MailServer.start();
+ MailServer.createEmailAccount("test@email.test", "password");
+
+ try {
+ while (true) {
+ int c = greenMail.getReceivedMessages().length;
+
+ if (greenMail.waitForIncomingEmail(Long.MAX_VALUE, c + 1)) {
+ MimeMessage m = greenMail.getReceivedMessages()[c++];
+ log.info("-------------------------------------------------------");
+ log.info("Received mail to " + m.getRecipients(RecipientType.TO)[0]);
+ if (m.getContent() instanceof MimeMultipart) {
+ MimeMultipart mimeMultipart = (MimeMultipart) m.getContent();
+ for (int i = 0; i < mimeMultipart.getCount(); i++) {
+ log.info("----");
+ log.info(mimeMultipart.getBodyPart(i).getContentType() + ":\n");
+ log.info(mimeMultipart.getBodyPart(i).getContent());
+ }
+ } else {
+ log.info("\n" + m.getContent());
+ }
+ log.info("-------------------------------------------------------");
+ }
+ }
+ } catch (IOException | InterruptedException | MessagingException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ public static void start() {
+ ServerSetup setup = new ServerSetup(Integer.parseInt(PORT), HOST, "smtp");
+
+ greenMail = new GreenMail(setup);
+ greenMail.start();
+
+ log.info("Started mail server (" + HOST + ":" + PORT + ")");
+ }
+
+ public static void stop() {
+ if (greenMail != null) {
+ log.info("Stopping mail server (localhost:3025)");
+ // Suppress error from GreenMail on shutdown
+ Thread.setDefaultUncaughtExceptionHandler(new UncaughtExceptionHandler() {
+ @Override
+ public void uncaughtException(Thread t, Throwable e) {
+ if (!(e.getCause() instanceof SocketException && e.getStackTrace()[0].getClassName()
+ .equals("com.icegreen.greenmail.smtp.SmtpHandler"))) {
+ log.error("Exception in thread \"" + t.getName() + "\" ");
+ log.error(e.getMessage(), e);
+ }
+ }
+ });
+ greenMail.stop();
+ }
+ }
+
+ public static void createEmailAccount(String email, String password) {
+ log.debug("Creating email account " + email);
+ greenMail.setUser(email, password);
+ }
+
+ public static MimeMessage getLastReceivedMessage() throws InterruptedException {
+ if (greenMail.waitForIncomingEmail(1)) {
+ return greenMail.getReceivedMessages()[greenMail.getReceivedMessages().length - 1];
+ }
+ return null;
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/TestEventsLogger.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/TestEventsLogger.java
new file mode 100644
index 0000000..249a19b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/TestEventsLogger.java
@@ -0,0 +1,44 @@
+package org.keycloak.testsuite.util;
+
+import org.jboss.logging.Logger;
+import org.junit.runner.Description;
+import org.junit.runner.notification.Failure;
+import org.junit.runner.notification.RunListener;
+
+/**
+ *
+ * @author Petr Mensik
+ * @author tkyjovsk
+ */
+public class TestEventsLogger extends RunListener {
+
+ private Logger log(Description d) {
+ return Logger.getLogger(d.getClassName());
+ }
+
+ private String getMessage(Description d, String status) {
+ return String.format("[%s] %s() %s", d.getTestClass().getSimpleName(), d.getMethodName(), status);
+ }
+
+ @Override
+ public void testStarted(Description d) throws Exception {
+ log(d).info(getMessage(d, "STARTED"));
+ }
+
+ @Override
+ public void testFailure(Failure f) throws Exception {
+ Description d = f.getDescription();
+ log(d).error(getMessage(d, "FAILED"));
+ }
+
+ @Override
+ public void testIgnored(Description d) throws Exception {
+ log(d).warn(getMessage(d, "IGNORED\n\n"));
+ }
+
+ @Override
+ public void testFinished(Description d) throws Exception {
+ log(d).info(getMessage(d, "FINISHED\n\n"));
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
new file mode 100644
index 0000000..255e450
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/util/URLAssert.java
@@ -0,0 +1,95 @@
+package org.keycloak.testsuite.util;
+
+import javax.ws.rs.core.UriBuilder;
+import org.keycloak.testsuite.page.AbstractPage;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import org.keycloak.testsuite.auth.page.login.PageWithLoginUrl;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.support.ui.ExpectedCondition;
+import org.openqa.selenium.support.ui.WebDriverWait;
+
+/**
+ *
+ * @author tkyjovsk
+ */
+public class URLAssert {
+
+ public static void assertCurrentUrlEquals(AbstractPage page) {
+ assertCurrentUrlEquals(page.getDriver(), page);
+ }
+
+ public static void assertCurrentUrlEquals(WebDriver driver, final AbstractPage page) {
+// WebDriverWait wait = new WebDriverWait(driver, 1);
+// ExpectedCondition<Boolean> urlStartsWith = new ExpectedCondition<Boolean>() {
+//
+// @Override
+// public Boolean apply(WebDriver wd) {
+// return startsWithNormalized(wd.getCurrentUrl(), page.toString());
+// }
+// };
+// wait.until(urlStartsWith);
+ assertEqualsNormalized(driver.getCurrentUrl(), page.toString());
+ }
+
+ public static void assertCurrentUrlStartsWith(AbstractPage page) {
+ assertCurrentUrlStartsWith(page.getDriver(), page.toString());
+ }
+
+ public static void assertCurrentUrlStartsWith(WebDriver driver, final String url) {
+// WebDriverWait wait = new WebDriverWait(driver, 1);
+// ExpectedCondition<Boolean> urlStartsWith = new ExpectedCondition<Boolean>() {
+//
+// @Override
+// public Boolean apply(WebDriver wd) {
+// return startsWithNormalized(wd.getCurrentUrl(), url);
+// }
+// };
+// wait.until(urlStartsWith);
+ assertTrue(startsWithNormalized(driver.getCurrentUrl(), url));
+ }
+
+ public static void assertCurrentUrlDoesntStartWith(AbstractPage page) {
+ assertCurrentUrlDoesntStartWith(page.getDriver(), page.toString());
+ }
+
+ public static void assertCurrentUrlDoesntStartWith(WebDriver driver, final String url) {
+// WebDriverWait wait = new WebDriverWait(driver, 1, 250);
+// ExpectedCondition<Boolean> urlDoesntStartWith = new ExpectedCondition<Boolean>() {
+//
+// @Override
+// public Boolean apply(WebDriver wd) {
+// return !startsWithNormalized(wd.getCurrentUrl(), url);
+// }
+// };
+// wait.until(urlDoesntStartWith);
+ assertFalse(startsWithNormalized(driver.getCurrentUrl(), url));
+ }
+
+ // this normalization is needed because of slash-encoding in uri fragment (the part after #)
+ public static String normalizeUri(String uri) {
+ return UriBuilder.fromUri(uri).build().toASCIIString();
+ }
+
+ public static boolean startsWithNormalized(String str1, String str2) {
+ String uri1 = normalizeUri(str1);
+ String uri2 = normalizeUri(str2);
+ return uri1.startsWith(uri2);
+ }
+
+ public static void assertEqualsNormalized(String str1, String str2) {
+ assertEquals(normalizeUri(str1), normalizeUri(str2));
+ }
+
+
+
+ public static void assertCurrentUrlStartsWithLoginUrlOf(PageWithLoginUrl page) {
+ assertCurrentUrlStartsWithLoginUrlOf(page.getDriver(), page);
+ }
+
+ public static void assertCurrentUrlStartsWithLoginUrlOf(WebDriver driver, PageWithLoginUrl page) {
+ assertCurrentUrlStartsWith(driver, page.getOIDCLoginUrl().toString());
+ }
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/context.xml
new file mode 100644
index 0000000..ef49048
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/context.xml
@@ -0,0 +1,3 @@
+<Context path="/%CONTEXT_PATH%">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak.json
new file mode 100644
index 0000000..3620170
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "customer-db",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "bearer-only" : true,
+ "enable-cors" : true
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak-relative.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak-relative.json
new file mode 100644
index 0000000..c457468
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/keycloak-relative.json
@@ -0,0 +1,9 @@
+{
+ "realm" : "demo",
+ "resource" : "customer-db",
+ "auth-server-url": "/auth",
+ "ssl-required" : "external",
+ "bearer-only" : true,
+ "enable-cors" : true
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/web.xml
new file mode 100644
index 0000000..8fbc2d2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db/WEB-INF/web.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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-db</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.CustomerDatabaseServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</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>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/META-INF/context.xml
new file mode 100644
index 0000000..aa10ca2
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-db-error-page">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/keycloak.json
new file mode 100644
index 0000000..3620170
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "customer-db",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "bearer-only" : true,
+ "enable-cors" : true
+
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/web.xml
new file mode 100644
index 0000000..496490d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-db-error-page/WEB-INF/web.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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-db-error-page</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.CustomerDatabaseServlet</servlet-class>
+ </servlet>
+ <servlet>
+ <servlet-name>Error Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.ErrorServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>Error Servlet</servlet-name>
+ <url-pattern>/error.html</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Errors</web-resource-name>
+ <url-pattern>/error.html</url-pattern>
+ </web-resource-collection>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ <form-login-config>
+ <form-login-page>/error.html</form-login-page>
+ <form-error-page>/error.html</form-error-page>
+ </form-login-config>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json
new file mode 100644
index 0000000..b75c1d5
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak.json
@@ -0,0 +1,11 @@
+{
+ "realm": "demo",
+ "resource": "customer-portal",
+ "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "expose-token": true,
+ "credentials": {
+ "secret": "password"
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-cookie.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-cookie.json
new file mode 100644
index 0000000..9d59c69
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-cookie.json
@@ -0,0 +1,12 @@
+{
+ "realm": "demo",
+ "resource": "customer-cookie-portal",
+ "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url": "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "expose-token": true,
+ "token-store": "cookie",
+ "credentials": {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-relative.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-relative.json
new file mode 100644
index 0000000..a796d1a
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/keycloak-relative.json
@@ -0,0 +1,9 @@
+{
+ "realm": "demo",
+ "resource": "customer-portal",
+ "auth-server-url": "/auth",
+ "ssl-required" : "external",
+ "credentials": {
+ "secret": "password"
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..285d226
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/customer-portal/WEB-INF/web.xml
@@ -0,0 +1,59 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.CustomerServlet</servlet-class>
+ </servlet>
+ <servlet>
+ <servlet-name>Error Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.ErrorServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <servlet-mapping>
+ <servlet-name>Error Servlet</servlet-name>
+ <url-pattern>/error.html</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</web-resource-name>
+ <url-pattern>/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Errors</web-resource-name>
+ <url-pattern>/error.html</url-pattern>
+ </web-resource-collection>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ <form-login-config>
+ <form-login-page>/error.html</form-login-page>
+ <form-error-page>/error.html</form-error-page>
+ </form-login-config>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
new file mode 100644
index 0000000..5b82ec6
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/demorealm.json
@@ -0,0 +1,161 @@
+{
+ "id": "demo",
+ "realm": "demo",
+ "enabled": true,
+ "accessTokenLifespan": 3000,
+ "accessCodeLifespan": 10,
+ "accessCodeLifespanUserAction": 6000,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "bburke@redhat.com",
+ "enabled": true,
+ "email" : "bburke@redhat.com",
+ "firstName": "Bill",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user", "admin" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
+ },
+ {
+ "username" : "mposolda",
+ "enabled": true,
+ "email" : "mposolda@redhat.com",
+ "firstName": "Marek",
+ "lastName": "Posolda",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "account": [ "manage-account" ]
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ },
+ {
+ "name": "admin",
+ "description": "Administrator privileges"
+ }
+ ]
+ },
+ "scopeMappings": [
+ {
+ "client": "third-party",
+ "roles": ["user"]
+ },
+ {
+ "client": "customer-portal",
+ "roles": ["user"]
+ },
+ {
+ "client": "product-portal",
+ "roles": ["user"]
+ }
+
+ ],
+ "clients": [
+ {
+ "clientId": "customer-portal",
+ "enabled": true,
+ "adminUrl": "/customer-portal",
+ "baseUrl": "/customer-portal",
+ "redirectUris": [
+ "/customer-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "customer-cookie-portal",
+ "enabled": true,
+ "baseUrl": "/customer-cookie-portal",
+ "redirectUris": [
+ "/customer-cookie-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "customer-portal-js",
+ "enabled": true,
+ "publicClient": true,
+ "adminUrl": "/customer-portal-js",
+ "baseUrl": "/customer-portal-js",
+ "redirectUris": [
+ "/customer-portal-js/*"
+ ]
+ },
+ {
+ "clientId": "customer-portal-cli",
+ "enabled": true,
+ "publicClient": true,
+ "redirectUris": [
+ "urn:ietf:wg:oauth:2.0:oob",
+ "http://localhost"
+ ]
+ },
+ {
+ "clientId": "product-portal",
+ "enabled": true,
+ "adminUrl": "/product-portal",
+ "baseUrl": "/product-portal",
+ "redirectUris": [
+ "/product-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "secure-portal",
+ "enabled": true,
+ "adminUrl": "/secure-portal",
+ "baseUrl": "/secure-portal",
+ "redirectUris": [
+ "/secure-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "session-portal",
+ "enabled": true,
+ "adminUrl": "/session-portal",
+ "baseUrl": "/session-portal",
+ "redirectUris": [
+ "/session-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "input-portal",
+ "enabled": true,
+ "adminUrl": "/input-portal",
+ "baseUrl": "/input-portal",
+ "redirectUris": [
+ "/input-portal/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "third-party",
+ "enabled": true,
+ "redirectUris": [
+ "/oauth-client/*",
+ "/oauth-client-cdi/*"
+ ],
+ "secret": "password"
+ }
+ ]
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
new file mode 100644
index 0000000..a934e97
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "input-portal",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://${my.host.name}:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..81c4e28
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/input-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>input-portal</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.InputServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</web-resource-name>
+ <url-pattern>/secured/*</url-pattern>
+ </web-resource-collection>
+ <auth-constraint>
+ <role-name>user</role-name>
+ </auth-constraint>
+ </security-constraint>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml
new file mode 100644
index 0000000..af5341b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/jboss-deployment-structure.xml
@@ -0,0 +1,16 @@
+<jboss-deployment-structure>
+ <deployment>
+ <dependencies>
+ <!-- the Demo code uses classes in these modules. These are optional to import if you are not using
+ Apache Http Client or the HttpClientBuilder that comes with the adapter core -->
+ <module name="org.apache.httpcomponents"/>
+
+ <!--These are needed when keycloak adapter libs are bundled in war.-->
+ <module name="org.codehaus.jackson.jackson-xc" />
+ <module name="org.codehaus.jackson.jackson-mapper-asl" />
+ <module name="org.bouncycastle" />
+ <module name="org.jboss.xnio" />
+
+ </dependencies>
+ </deployment>
+</jboss-deployment-structure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant1-keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant1-keycloak.json
new file mode 100644
index 0000000..14b3f7b
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant1-keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "tenant1",
+ "resource" : "multi-tenant",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant2-keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant2-keycloak.json
new file mode 100644
index 0000000..bfb1814
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/classes/tenant2-keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "tenant2",
+ "resource" : "multi-tenant",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/web.xml
new file mode 100644
index 0000000..6f830ae
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/multi-tenant/WEB-INF/web.xml
@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>multi-tenant</module-name>
+
+ <servlet>
+ <servlet-name>MultiTenantServlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.MultiTenantServlet</servlet-class>
+ </servlet>
+
+ <context-param>
+ <param-name>keycloak.config.resolver</param-name>
+ <param-value>org.keycloak.testsuite.adapter.servlet.MultiTenantResolver</param-value>
+ </context-param>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</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>KEYCLOAK</auth-method>
+ </login-config>
+
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak.json
new file mode 100644
index 0000000..7dbe680
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "product-portal",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak-relative.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak-relative.json
new file mode 100644
index 0000000..9ef62ff
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/keycloak-relative.json
@@ -0,0 +1,9 @@
+{
+ "realm" : "demo",
+ "resource" : "product-portal",
+ "auth-server-url" : "/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..ebefac0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/product-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>product-portal</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.ProductServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</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>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json
new file mode 100644
index 0000000..6b8d13f
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "secure-portal",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://localhost:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..4ee9cd1
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/secure-portal/WEB-INF/web.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>secure-portal</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.CallAuthenticatedServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <login-config>
+ <auth-method>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/META-INF/context.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/META-INF/context.xml
new file mode 100644
index 0000000..8e2c70d
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/META-INF/context.xml
@@ -0,0 +1,3 @@
+<Context path="/customer-portal">
+ <Valve className="org.keycloak.adapters.tomcat.KeycloakAuthenticatorValve"/>
+</Context>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/jetty-web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/jetty-web.xml
new file mode 100644
index 0000000..5456b16
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/jetty-web.xml
@@ -0,0 +1,29 @@
+<?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.jetty.KeycloakJettyAuthenticator">
+ <!--
+ <Set name="adapterConfig">
+ <New class="org.keycloak.representations.adapters.config.AdapterConfig">
+ <Set name="realm">tomcat</Set>
+ <Set name="resource">customer-portal</Set>
+ <Set name="authServerUrl">http://localhost:8180/auth</Set>
+ <Set name="sslRequired">external</Set>
+ <Set name="credentials">
+ <Map>
+ <Entry>
+ <Item>secret</Item>
+ <Item>password</Item>
+ </Entry>
+ </Map>
+ </Set>
+ <Set name="realmKey">MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB</Set>
+ </New>
+ </Set>
+ -->
+ </New>
+ </Set>
+ </Get>
+</Configure>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/keycloak.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/keycloak.json
new file mode 100644
index 0000000..d07b738
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/keycloak.json
@@ -0,0 +1,10 @@
+{
+ "realm" : "demo",
+ "resource" : "session-portal",
+ "realm-public-key" : "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "auth-server-url" : "http://${my.host.name}:8180/auth",
+ "ssl-required" : "external",
+ "credentials" : {
+ "secret": "password"
+ }
+}
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/web.xml
new file mode 100644
index 0000000..da08586
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/session-portal/WEB-INF/web.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>session-portal</module-name>
+
+ <servlet>
+ <servlet-name>Servlet</servlet-name>
+ <servlet-class>org.keycloak.testsuite.adapter.servlet.SessionServlet</servlet-class>
+ </servlet>
+
+ <servlet-mapping>
+ <servlet-name>Servlet</servlet-name>
+ <url-pattern>/*</url-pattern>
+ </servlet-mapping>
+
+ <security-constraint>
+ <web-resource-collection>
+ <web-resource-name>Users</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>KEYCLOAK</auth-method>
+ <realm-name>demo</realm-name>
+ </login-config>
+
+ <security-role>
+ <role-name>admin</role-name>
+ </security-role>
+ <security-role>
+ <role-name>user</role-name>
+ </security-role>
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant1-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant1-realm.json
new file mode 100644
index 0000000..b565c05
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant1-realm.json
@@ -0,0 +1,82 @@
+{
+ "id": "tenant1",
+ "realm": "tenant1",
+ "enabled": true,
+ "accessTokenLifespan": 3000,
+ "accessCodeLifespan": 10,
+ "accessCodeLifespanUserAction": 6000,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "bburke@redhat.com",
+ "enabled": true,
+ "email" : "bburke@redhat.com",
+ "firstName": "Bill",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "multi-tenant": [ "user" ]
+ }
+ },
+ {
+ "username" : "user-tenant1",
+ "enabled": true,
+ "email" : "user-tenant1@redhat.com",
+ "firstName": "Bill",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "user-tenant1" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "multi-tenant": [ "user" ]
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ }
+ ]
+ },
+ "scopeMappings": [
+ {
+ "client": "multi-tenant",
+ "roles": ["user"]
+ }
+
+ ],
+ "clients": [
+ {
+ "clientId": "multi-tenant",
+ "enabled": true,
+ "adminUrl": "/multi-tenant",
+ "baseUrl": "/multi-tenant",
+ "redirectUris": [
+ "/multi-tenant/*"
+ ],
+ "secret": "password"
+ },
+ {
+ "clientId": "multitenant",
+ "enabled": true,
+ "adminUrl": "/multitenant/tenant1",
+ "baseUrl": "/multitenant/tenant1",
+ "redirectUris": [
+ "/multitenant/tenant1/*"
+ ],
+ "secret": "password"
+ }
+ ]
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant2-realm.json b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant2-realm.json
new file mode 100644
index 0000000..54b28a4
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/tenant2-realm.json
@@ -0,0 +1,72 @@
+{
+ "id": "tenant2",
+ "realm": "tenant2",
+ "enabled": true,
+ "accessTokenLifespan": 3000,
+ "accessCodeLifespan": 10,
+ "accessCodeLifespanUserAction": 6000,
+ "sslRequired": "external",
+ "registrationAllowed": false,
+ "privateKey": "MIICXAIBAAKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQABAoGAfmO8gVhyBxdqlxmIuglbz8bcjQbhXJLR2EoS8ngTXmN1bo2L90M0mUKSdc7qF10LgETBzqL8jYlQIbt+e6TH8fcEpKCjUlyq0Mf/vVbfZSNaVycY13nTzo27iPyWQHK5NLuJzn1xvxxrUeXI6A2WFpGEBLbHjwpx5WQG9A+2scECQQDvdn9NE75HPTVPxBqsEd2z10TKkl9CZxu10Qby3iQQmWLEJ9LNmy3acvKrE3gMiYNWb6xHPKiIqOR1as7L24aTAkEAtyvQOlCvr5kAjVqrEKXalj0Tzewjweuxc0pskvArTI2Oo070h65GpoIKLc9jf+UA69cRtquwP93aZKtW06U8dQJAF2Y44ks/mK5+eyDqik3koCI08qaC8HYq2wVl7G2QkJ6sbAaILtcvD92ToOvyGyeE0flvmDZxMYlvaZnaQ0lcSQJBAKZU6umJi3/xeEbkJqMfeLclD27XGEFoPeNrmdx0q10Azp4NfJAY+Z8KRyQCR2BEG+oNitBOZ+YXF9KCpH3cdmECQHEigJhYg+ykOvr1aiZUMFT72HU0jnmQe2FVekuG+LJUt2Tm7GtMjTFoGpf0JwrVuZN39fOYAlo+nTixgeW7X8Y=",
+ "publicKey": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
+ "requiredCredentials": [ "password" ],
+ "users" : [
+ {
+ "username" : "bburke@redhat.com",
+ "enabled": true,
+ "email" : "bburke@redhat.com",
+ "firstName": "Bill",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "password" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "multi-tenant": [ "user" ]
+ }
+ },
+ {
+ "username" : "user-tenant2",
+ "enabled": true,
+ "email" : "user-tenant2@redhat.com",
+ "firstName": "Bill",
+ "lastName": "Burke",
+ "credentials" : [
+ { "type" : "password",
+ "value" : "user-tenant2" }
+ ],
+ "realmRoles": [ "user" ],
+ "applicationRoles": {
+ "multi-tenant": [ "user" ]
+ }
+ }
+ ],
+ "roles" : {
+ "realm" : [
+ {
+ "name": "user",
+ "description": "User privileges"
+ }
+ ]
+ },
+ "scopeMappings": [
+ {
+ "client": "multi-tenant",
+ "roles": ["user"]
+ }
+
+ ],
+ "clients": [
+ {
+ "clientId": "multi-tenant",
+ "enabled": true,
+ "adminUrl": "/multi-tenant",
+ "baseUrl": "/multi-tenant",
+ "redirectUris": [
+ "/multi-tenant/*"
+ ],
+ "secret": "password"
+ }
+ ]
+}
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/web.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/web.xml
new file mode 100644
index 0000000..1b6dc5e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/adapter-test/web.xml
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<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>%CONTEXT_PATH%</module-name>
+
+</web-app>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
new file mode 100644
index 0000000..7b2713e
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/arquillian.xml
@@ -0,0 +1,91 @@
+<arquillian xmlns="http://jboss.org/schema/arquillian"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://jboss.org/schema/arquillian
+ http://jboss.org/schema/arquillian/arquillian_1_0.xsd">
+
+ <defaultProtocol type="Servlet 3.0" />
+
+ <extension qualifier="webdriver">
+ <property name="browser">${browser}</property>
+ </extension>
+
+ <extension qualifier="graphene-secondbrowser">
+ <property name="browser">${browser}</property>
+ </extension>
+
+ <engine>
+ <!-- This allows manual inspection of deployed archives. -->
+ <property name="deploymentExportPath">target/deployments</property>
+ </engine>
+
+ <!-- PREVIOUS VERSIONS KEYCLOAK FOR MIGRATION TESTS -->
+ <!-- IT HAS TO BE LISTED ABOWE KEYCLOAK AUTH SERVERS -->
+
+ <container qualifier="keycloak-1.4.0.Final" mode="suite" >
+ <configuration>
+ <property name="enabled">${migration.kc14}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${keycloak-1.4.0.Final.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${auth.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m</property>
+ <property name="managementPort">${auth.server.management.port}</property>
+ <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+ </configuration>
+ </container>
+
+ <container qualifier="keycloak-1.3.1.Final" mode="suite" >
+ <configuration>
+ <property name="enabled">${migration.kc13}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${keycloak-1.3.1.Final.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${auth.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m</property>
+ <property name="managementPort">${auth.server.management.port}</property>
+ <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+ </configuration>
+ </container>
+
+ <container qualifier="keycloak-1.2.0.Final" mode="suite" >
+ <configuration>
+ <property name="enabled">${migration.kc12}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${keycloak-1.2.0.Final.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${auth.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m</property>
+ <property name="managementPort">${auth.server.management.port}</property>
+ <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+ </configuration>
+ </container>
+
+ <!-- KEYCLOAK AUTH SERVERS -->
+
+ <container qualifier="auth-server-undertow" mode="suite" >
+ <configuration>
+ <property name="enabled">${auth.server.undertow}</property>
+ <property name="bindAddress">localhost</property>
+ <property name="adapterImplClass">org.keycloak.testsuite.arquillian.undertow.CustomUndertowContainer</property>
+ <property name="bindHttpPort">${auth.server.http.port}</property>
+ </configuration>
+ </container>
+
+ <container qualifier="auth-server-wildfly" mode="suite" >
+ <configuration>
+ <property name="enabled">${auth.server.wildfly}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${auth.server.wildfly.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${auth.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementPort">${auth.server.management.port}</property>
+ <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+ </configuration>
+ </container>
+
+ <container qualifier="auth-server-eap6" mode="suite" >
+ <configuration>
+ <property name="enabled">${auth.server.eap6}</property>
+ <property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
+ <property name="jbossHome">${auth.server.eap6.home}</property>
+ <property name="javaVmArguments">-Djboss.socket.binding.port-offset=${auth.server.port.offset} -Xms64m -Xmx512m -XX:MaxPermSize=256m ${adapter.test.props}</property>
+ <property name="managementAddress">localhost</property>
+ <property name="managementPort">${auth.server.management.port.jmx}</property>
+ <property name="startupTimeoutInSeconds">${startup.timeout.sec}</property>
+ </configuration>
+ </container>
+
+</arquillian>
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
new file mode 100644
index 0000000..50dab50
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/log4j.properties
@@ -0,0 +1,43 @@
+log4j.rootLogger=info, keycloak
+
+log4j.appender.keycloak=org.apache.log4j.ConsoleAppender
+log4j.appender.keycloak.layout=org.apache.log4j.PatternLayout
+log4j.appender.keycloak.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p [%c] %m%n
+
+log4j.appender.testsuite=org.apache.log4j.ConsoleAppender
+log4j.appender.testsuite.layout=org.apache.log4j.PatternLayout
+log4j.appender.testsuite.layout.ConversionPattern=%d{HH:mm:ss,SSS} %-5p %m%n
+
+log4j.logger.org.keycloak=off, keycloak
+
+log4j.logger.org.keycloak.testsuite=debug, testsuite
+log4j.additivity.org.keycloak.testsuite=false
+
+# Enable to view events
+# log4j.logger.org.keycloak.events=debug
+
+# Enable to view loaded SPI and Providers
+# log4j.logger.org.keycloak.services.DefaultKeycloakSessionFactory=debug
+# log4j.logger.org.keycloak.provider.ProviderManager=debug
+# log4j.logger.org.keycloak.provider.FileSystemProviderLoaderFactory=debug
+
+# Liquibase updates logged with "info" by default. Logging level can be changed by system property "keycloak.liquibase.logging.level"
+keycloak.liquibase.logging.level=info
+log4j.logger.org.keycloak.connections.jpa.updater.liquibase.LiquibaseJpaUpdaterProvider=${keycloak.liquibase.logging.level}
+
+# Enable to view database updates
+# log4j.logger.org.keycloak.connections.mongo.updater.DefaultMongoUpdaterProvider=debug
+# log4j.logger.org.keycloak.connections.jpa.DefaultJpaConnectionProviderFactory=debug
+# log4j.logger.org.keycloak.migration.MigrationModelManager=debug
+
+# Enable to view kerberos/spnego logging
+# log4j.logger.org.keycloak.broker.kerberos=trace
+
+# Enable to view detailed AS REQ and TGS REQ requests to embedded Kerberos server
+# log4j.logger.org.apache.directory.server.kerberos=debug
+
+log4j.logger.org.xnio=off
+log4j.logger.org.hibernate=off
+log4j.logger.org.jboss.resteasy=warn
+log4j.logger.org.apache.directory.api=warn
+log4j.logger.org.apache.directory.server.core=warn
diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
new file mode 100644
index 0000000..c760152
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/base/src/test/resources/META-INF/keycloak-server.json
@@ -0,0 +1,99 @@
+{
+ "admin": {
+ "realm": "master"
+ },
+
+ "eventsStore": {
+ "provider": "${keycloak.eventsStore.provider:jpa}"
+ },
+
+ "eventsListener": {
+ "jboss-logging" : {
+ "success-level": "debug",
+ "error-level": "warn"
+ }
+ },
+
+ "realm": {
+ "provider": "${keycloak.realm.provider:jpa}"
+ },
+
+ "user": {
+ "provider": "${keycloak.user.provider:jpa}"
+ },
+
+ "userSessions": {
+ "provider" : "${keycloak.userSessions.provider:infinispan}"
+ },
+
+ "realmCache": {
+ "provider": "${keycloak.realm.cache.provider:infinispan}"
+ },
+
+ "userCache": {
+ "provider": "${keycloak.user.cache.provider:infinispan}",
+ "mem": {
+ "maxSize": 20000
+ }
+ },
+
+ "timer": {
+ "provider": "basic"
+ },
+
+ "theme": {
+ "default": "keycloak",
+ "staticMaxAge": "${keycloak.theme.staticMaxAge:2592000}",
+ "cacheTemplates": "${keycloak.theme.cacheTemplates:true}",
+ "cacheThemes": "${keycloak.theme.cacheThemes:true}",
+ "folder": {
+ "dir": "${keycloak.theme.dir}"
+ }
+ },
+
+ "login": {
+ "provider": "freemarker"
+ },
+
+ "account": {
+ "provider": "freemarker"
+ },
+
+ "email": {
+ "provider": "freemarker"
+ },
+
+ "scheduled": {
+ "interval": 900
+ },
+
+ "connectionsHttpClient": {
+ "default": {
+ "disable-trust-manager": true
+ }
+ },
+
+
+ "connectionsJpa": {
+ "default": {
+ "url": "${keycloak.connectionsJpa.url:jdbc:h2:mem:test}",
+ "driver": "${keycloak.connectionsJpa.driver:org.h2.Driver}",
+ "driverDialect": "${keycloak.connectionsJpa.driverDialect:}",
+ "user": "${keycloak.connectionsJpa.user:sa}",
+ "password": "${keycloak.connectionsJpa.password:}",
+ "databaseSchema": "${keycloak.connectionsJpa.databaseSchema:update}",
+ "showSql": "${keycloak.connectionsJpa.showSql:false}",
+ "formatSql": "${keycloak.connectionsJpa.formatSql:true}"
+ }
+ },
+
+ "connectionsMongo": {
+ "default": {
+ "host": "${keycloak.connectionsMongo.host:127.0.0.1}",
+ "port": "${keycloak.connectionsMongo.port:27017}",
+ "db": "${keycloak.connectionsMongo.db:keycloak}",
+ "databaseSchema": "${keycloak.connectionsMongo.databaseSchema:update}",
+ "connectionsPerHost": "${keycloak.connectionsMongo.connectionsPerHost:100}"
+ }
+ }
+}
\ No newline at end of file
testsuite/integration-arquillian/tests/pom.xml 597(+597 -0)
diff --git a/testsuite/integration-arquillian/tests/pom.xml b/testsuite/integration-arquillian/tests/pom.xml
new file mode 100644
index 0000000..9ac97d0
--- /dev/null
+++ b/testsuite/integration-arquillian/tests/pom.xml
@@ -0,0 +1,597 @@
+<?xml version="1.0"?>
+<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
+ <parent>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian</artifactId>
+ <version>1.6.0.Final-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>integration-arquillian-tests</artifactId>
+ <packaging>pom</packaging>
+ <name>Tests</name>
+
+ <modules>
+ <module>base</module>
+ <module>adapters</module>
+ </modules>
+
+ <properties>
+ <containers.home>${project.build.directory}/containers</containers.home>
+ <examples.home>${project.build.directory}/examples</examples.home>
+
+ <auth.server.container>auth-server-undertow</auth.server.container>
+ <auth.server.port.offset>100</auth.server.port.offset>
+ <auth.server.http.port>8180</auth.server.http.port>
+ <auth.server.management.port>10090</auth.server.management.port>
+ <auth.server.management.port.jmx>10099</auth.server.management.port.jmx>
+ <startup.timeout.sec>60</startup.timeout.sec>
+
+ <browser>phantomjs</browser>
+
+ <arquillian-core.version>1.1.8.Final</arquillian-core.version>
+ <selenium.version>2.45.0</selenium.version>
+ <arquillian-drone.version>2.0.0.Alpha4</arquillian-drone.version>
+ <arquillian-graphene.version>2.1.0.Alpha2</arquillian-graphene.version>
+ <arquillian-wildfly-container.version>8.2.0.Final</arquillian-wildfly-container.version>
+ <version.shrinkwrap.resolvers>2.1.1</version.shrinkwrap.resolvers>
+ </properties>
+
+ <dependencyManagement>
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.arquillian.selenium</groupId>
+ <artifactId>selenium-bom</artifactId>
+ <version>${selenium.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian</groupId>
+ <artifactId>arquillian-bom</artifactId>
+ <version>${arquillian-core.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-drone-bom</artifactId>
+ <version>${arquillian-drone.version}</version>
+ <type>pom</type>
+ <scope>import</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ <version>${arquillian-wildfly-container.version}</version>
+ </dependency>
+ </dependencies>
+ </dependencyManagement>
+
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <browser>${browser}</browser>
+ <shouldDeploy>false</shouldDeploy>
+ <auth.server.container>${auth.server.container}</auth.server.container>
+ <auth.server.undertow>true</auth.server.undertow>
+ <auth.server.port.offset>${auth.server.port.offset}</auth.server.port.offset>
+ <auth.server.http.port>${auth.server.http.port}</auth.server.http.port>
+ <auth.server.management.port>${auth.server.management.port}</auth.server.management.port>
+ <auth.server.management.port.jmx>${auth.server.management.port.jmx}</auth.server.management.port.jmx>
+ <startup.timeout.sec>${startup.timeout.sec}</startup.timeout.sec>
+ </systemPropertyVariables>
+ <properties>
+ <property>
+ <name>listener</name>
+ <value>org.keycloak.testsuite.util.TestEventsLogger</value>
+ </property>
+ </properties>
+ <failIfNoTests>false</failIfNoTests>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+
+ <profiles>
+
+ <profile>
+ <id>common-for-tests</id>
+ <activation>
+ <file>
+ <exists>src</exists>
+ <!-- ^ only activate this profile in submodules that have actual tests -->
+ </file>
+ </activation>
+ <dependencies>
+ <!-- TEST DEPENDENCIES -->
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.junit</groupId>
+ <artifactId>arquillian-junit-container</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.arquillian.graphene</groupId>
+ <artifactId>graphene-webdriver</artifactId>
+ <version>${arquillian-graphene.version}</version>
+ <type>pom</type>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.protocol</groupId>
+ <artifactId>arquillian-protocol-servlet</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.arquillian.extension</groupId>
+ <artifactId>arquillian-phantom-driver</artifactId>
+ </dependency>
+ <!-- TODO: investigate if we need this dependency
+ <dependency>
+ <groupId>org.jboss.arquillian.graphene</groupId>
+ <artifactId>arquillian-graphene-impl</artifactId>
+ <version>1.0.0.CR3</version>
+ </dependency>-->
+ <dependency>
+ <groupId>org.jboss.arquillian.graphene</groupId>
+ <artifactId>arquillian-browser-screenshooter</artifactId>
+ <version>2.1.0.Alpha2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-log4j12</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>javax.servlet</groupId>
+ <artifactId>javax.servlet-api</artifactId>
+ <version>3.1.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.ant</groupId>
+ <artifactId>ant</artifactId>
+ <version>1.9.2</version>
+ <type>jar</type>
+ </dependency>
+
+ <!-- Email Test Server -->
+ <dependency>
+ <groupId>com.icegreen</groupId>
+ <artifactId>greenmail</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+
+ <!-- Keycloak deps for tests -->
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-dependencies-server-all</artifactId>
+ <type>pom</type>
+ </dependency>
+
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-admin-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-services</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ </dependency>
+
+
+ <!-- Keycloak Server on Undertow -->
+
+ <dependency>
+ <groupId>org.jboss.arquillian.container</groupId>
+ <artifactId>undertow-embedded</artifactId>
+ <version>1.0.0.Alpha1-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>jaxrs-api</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>async-http-servlet-3.0</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jaxrs</artifactId>
+ <exclusions>
+ <exclusion>
+ <groupId>log4j</groupId>
+ <artifactId>log4j</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ </exclusion>
+ <exclusion>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-simple</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-client</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-undertow</artifactId>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-multipart-provider</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.resteasy</groupId>
+ <artifactId>resteasy-jackson-provider</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.hibernate.javax.persistence</groupId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.h2database</groupId>
+ <artifactId>h2</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hibernate</groupId>
+ <artifactId>hibernate-entitymanager</artifactId>
+ </dependency>
+
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcpkix-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.hamcrest</groupId>
+ <artifactId>hamcrest-all</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.infinispan</groupId>
+ <artifactId>infinispan-core</artifactId>
+ </dependency>
+
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
+
+ <profile>
+ <id>auth-server-wildfly</id>
+ <properties>
+ <auth.server.container>auth-server-wildfly</auth.server.container>
+ <auth.server.wildfly.home>${containers.home}/keycloak-${project.version}</auth.server.wildfly.home>
+ <startup.timeout.sec>150</startup.timeout.sec>
+ <adapter.test.props/>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly</groupId>
+ <artifactId>wildfly-arquillian-container-managed</artifactId>
+ </dependency>
+ </dependencies>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-server-wildfly</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${containers.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <auth.server.wildfly>true</auth.server.wildfly>
+ <auth.server.undertow>false</auth.server.undertow>
+ <auth.server.wildfly.home>${auth.server.wildfly.home}</auth.server.wildfly.home>
+ <adapter.test.props>${adapter.test.props}</adapter.test.props>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <id>auth-server-eap6</id>
+ <properties>
+ <auth.server.container>auth-server-eap6</auth.server.container>
+ <auth.server.eap6.home>${containers.home}/keycloak-${project.version}</auth.server.eap6.home>
+ <startup.timeout.sec>150</startup.timeout.sec>
+ <adapter.test.props/>
+ </properties>
+ <dependencies>
+ <dependency>
+ <groupId>org.jboss.as</groupId>
+ <artifactId>jboss-as-arquillian-container-managed</artifactId>
+ <version>7.2.0.Final</version>
+ </dependency>
+ </dependencies>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-server-eap6</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${containers.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <auth.server.eap6>true</auth.server.eap6>
+ <auth.server.undertow>false</auth.server.undertow>
+ <auth.server.eap6.home>${auth.server.eap6.home}</auth.server.eap6.home>
+ <adapter.test.props>${adapter.test.props}</adapter.test.props>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <!-- Profiles for migration tests-->
+
+ <profile>
+ <id>migration-kc14</id>
+ <properties>
+ <keycloak-1.4.0.Final.home>${containers.home}/keycloak-1.4.0.Final</keycloak-1.4.0.Final.home>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack-previous</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-server-wildfly-kc14</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${containers.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <migration>true</migration>
+ <migration.kc14>true</migration.kc14>
+ <keycloak-1.4.0.Final.home>${keycloak-1.4.0.Final.home}</keycloak-1.4.0.Final.home>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <id>migration-kc13</id>
+ <properties>
+ <keycloak-1.3.1.Final.home>${containers.home}/keycloak-1.3.1.Final</keycloak-1.3.1.Final.home>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack-previous</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-server-wildfly-kc13</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${containers.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <migration>true</migration>
+ <migration.kc13>true</migration.kc13>
+ <keycloak-1.3.1.Final.home>${keycloak-1.3.1.Final.home}</keycloak-1.3.1.Final.home>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+
+ <profile>
+ <id>migration-kc12</id>
+ <properties>
+ <keycloak-1.2.0.Final.home>${containers.home}/keycloak-1.2.0.Final</keycloak-1.2.0.Final.home>
+ </properties>
+ <build>
+ <pluginManagement>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-dependency-plugin</artifactId>
+ <version>2.10</version>
+ <executions>
+ <execution>
+ <id>unpack-previous</id>
+ <phase>generate-test-resources</phase>
+ <goals>
+ <goal>unpack</goal>
+ </goals>
+ <configuration>
+ <artifactItems>
+ <artifactItem>
+ <groupId>org.keycloak.testsuite</groupId>
+ <artifactId>integration-arquillian-server-wildfly-kc12</artifactId>
+ <version>${project.version}</version>
+ <type>zip</type>
+ </artifactItem>
+ </artifactItems>
+ <outputDirectory>${containers.home}</outputDirectory>
+ <overWriteIfNewer>true</overWriteIfNewer>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-surefire-plugin</artifactId>
+ <configuration>
+ <systemPropertyVariables>
+ <migration>true</migration>
+ <migration.kc12>true</migration.kc12>
+ <keycloak-1.2.0.Final.home>${keycloak-1.2.0.Final.home}</keycloak-1.2.0.Final.home>
+ </systemPropertyVariables>
+ </configuration>
+ </plugin>
+ </plugins>
+ </pluginManagement>
+ </build>
+ </profile>
+ </profiles>
+
+</project>
testsuite/jetty/jetty81/pom.xml 2(+1 -1)
diff --git a/testsuite/jetty/jetty81/pom.xml b/testsuite/jetty/jetty81/pom.xml
index 5fd9e6a..a675c2d 100755
--- a/testsuite/jetty/jetty81/pom.xml
+++ b/testsuite/jetty/jetty81/pom.xml
@@ -144,7 +144,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/jetty/jetty91/pom.xml 2(+1 -1)
diff --git a/testsuite/jetty/jetty91/pom.xml b/testsuite/jetty/jetty91/pom.xml
index 7815e73..ead665b 100755
--- a/testsuite/jetty/jetty91/pom.xml
+++ b/testsuite/jetty/jetty91/pom.xml
@@ -144,7 +144,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/jetty/jetty92/pom.xml 2(+1 -1)
diff --git a/testsuite/jetty/jetty92/pom.xml b/testsuite/jetty/jetty92/pom.xml
index 09eaec7..ea9f7e3 100755
--- a/testsuite/jetty/jetty92/pom.xml
+++ b/testsuite/jetty/jetty92/pom.xml
@@ -144,7 +144,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/performance/pom.xml 2(+1 -1)
diff --git a/testsuite/performance/pom.xml b/testsuite/performance/pom.xml
index f553ab7..03bf3fe 100755
--- a/testsuite/performance/pom.xml
+++ b/testsuite/performance/pom.xml
@@ -193,7 +193,7 @@
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
<version>${hibernate.javax.persistence.version}</version>
</dependency>
<dependency>
testsuite/proxy/pom.xml 2(+1 -1)
diff --git a/testsuite/proxy/pom.xml b/testsuite/proxy/pom.xml
index 28c418a..df5896c 100755
--- a/testsuite/proxy/pom.xml
+++ b/testsuite/proxy/pom.xml
@@ -139,7 +139,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/tomcat6/pom.xml 2(+1 -1)
diff --git a/testsuite/tomcat6/pom.xml b/testsuite/tomcat6/pom.xml
index 0597e7d..498c4d6 100755
--- a/testsuite/tomcat6/pom.xml
+++ b/testsuite/tomcat6/pom.xml
@@ -130,7 +130,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/tomcat7/pom.xml 2(+1 -1)
diff --git a/testsuite/tomcat7/pom.xml b/testsuite/tomcat7/pom.xml
index b8a44e3..d807e73 100755
--- a/testsuite/tomcat7/pom.xml
+++ b/testsuite/tomcat7/pom.xml
@@ -150,7 +150,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/tomcat8/pom.xml 2(+1 -1)
diff --git a/testsuite/tomcat8/pom.xml b/testsuite/tomcat8/pom.xml
index 4862aeb..e2a9059 100755
--- a/testsuite/tomcat8/pom.xml
+++ b/testsuite/tomcat8/pom.xml
@@ -134,7 +134,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
testsuite/wildfly/pom.xml 2(+1 -1)
diff --git a/testsuite/wildfly/pom.xml b/testsuite/wildfly/pom.xml
index d91657e..fd5c195 100644
--- a/testsuite/wildfly/pom.xml
+++ b/testsuite/wildfly/pom.xml
@@ -141,7 +141,7 @@
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
- <artifactId>hibernate-jpa-2.1-api</artifactId>
+ <artifactId>${hibernate.javax.persistence.artifactId}</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>