diff --git a/adapters/oidc/js/src/main/resources/keycloak.js b/adapters/oidc/js/src/main/resources/keycloak.js index f0cae4211c..2def1e2a8e 100755 --- a/adapters/oidc/js/src/main/resources/keycloak.js +++ b/adapters/oidc/js/src/main/resources/keycloak.js @@ -87,6 +87,10 @@ } kc.flow = initOptions.flow; } + + if (initOptions.timeSkew != null) { + kc.timeSkew = initOptions.timeSkew; + } } if (!kc.responseMode) { @@ -162,12 +166,8 @@ kc.onAuthSuccess && kc.onAuthSuccess(); initPromise.setSuccess(); }).error(function () { - kc.onAuthError && kc.onAuthError(); - if (initOptions.onLoad) { - onLoad(); - } else { - initPromise.setError(); - } + setToken(null, null, null); + initPromise.setSuccess(); }); }); } else { @@ -369,6 +369,11 @@ throw 'Not authenticated'; } + if (kc.timeSkew == null) { + console.info('[KEYCLOAK] Unable to determine if token is expired as timeskew is not set'); + return true; + } + var expiresIn = kc.tokenParsed['exp'] - Math.ceil(new Date().getTime() / 1000) + kc.timeSkew; if (minValidity) { expiresIn -= minValidity; @@ -653,12 +658,7 @@ if (token) { kc.token = token; kc.tokenParsed = decodeToken(token); - - var sessionId = kc.realm + '/' + kc.tokenParsed.sub; - if (kc.tokenParsed.session_state) { - sessionId = sessionId + '/' + kc.tokenParsed.session_state; - } - kc.sessionId = sessionId; + kc.sessionId = kc.tokenParsed.session_state; kc.authenticated = true; kc.subject = kc.tokenParsed.sub; kc.realmAccess = kc.tokenParsed.realm_access; @@ -666,6 +666,9 @@ if (timeLocal) { kc.timeSkew = Math.floor(timeLocal / 1000) - kc.tokenParsed.iat; + } + + if (kc.timeSkew != null) { console.info('[KEYCLOAK] Estimated time difference between browser and server is ' + kc.timeSkew + ' seconds'); if (kc.onTokenExpired) { @@ -677,11 +680,7 @@ kc.tokenTimeoutHandle = setTimeout(kc.onTokenExpired, expiresIn); } } - } else { - kc.updateToken(-1); } - } else if (refreshToken) { - kc.updateToken(-1); } else { delete kc.token; delete kc.tokenParsed; diff --git a/adapters/oidc/js/src/main/resources/login-status-iframe.html b/adapters/oidc/js/src/main/resources/login-status-iframe.html index f941663e4f..b1012f7694 100755 --- a/adapters/oidc/js/src/main/resources/login-status-iframe.html +++ b/adapters/oidc/js/src/main/resources/login-status-iframe.html @@ -53,7 +53,8 @@ req.send(); } else { if (clientId === init.clientId && origin === init.origin) { - if (sessionState === cookie) { + var c = cookie.split('/'); + if (sessionState === c[2]) { callback('unchanged'); } else { callback('changed'); @@ -81,7 +82,7 @@ var origin = event.origin; var data = event.data.split(' '); if (data.length != 2) { - event.source.postMessage('error', origin); + return; } var clientId = data[0]; diff --git a/distribution/downloads/assembly.xml b/distribution/downloads/assembly.xml new file mode 100644 index 0000000000..9d408d1448 --- /dev/null +++ b/distribution/downloads/assembly.xml @@ -0,0 +1,33 @@ + + + + server-dist + + + dir + + + + + ${localRepository}/org/keycloak + + **/*.zip + + + + diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index a1f114e2d3..1a8a6fd3d4 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -25,16 +25,25 @@ keycloak-dist-downloads - pom + jar Keycloak Release Downloads - - - + + 1.8 + 1.8 + + + org.apache.maven.plugins + maven-compiler-plugin + + ${maven.compiler.source} + ${maven.compiler.target} + + org.apache.maven.plugins maven-deploy-plugin @@ -43,342 +52,22 @@ - org.apache.maven.plugins - maven-dependency-plugin + org.codehaus.mojo + exec-maven-plugin server-downloads - install + package - copy + java - - - org.keycloak - keycloak-server-dist - zip - keycloak-${project.version}.zip - - - org.keycloak - keycloak-server-dist - tar.gz - keycloak-${project.version}.tar.gz - - - - org.keycloak - keycloak-demo-dist - zip - keycloak-demo-${project.version}.zip - - - org.keycloak - keycloak-demo-dist - tar.gz - keycloak-demo-${project.version}.tar.gz - - - - org.keycloak - keycloak-server-overlay - zip - keycloak-overlay-${project.version}.zip - - - org.keycloak - keycloak-server-overlay - tar.gz - keycloak-overlay-${project.version}.tar.gz - - - - org.keycloak - keycloak-proxy-dist - zip - keycloak-proxy-${project.version}.zip - - - - org.keycloak - keycloak-api-docs-dist - zip - keycloak-api-docs-${project.version}.zip - - - org.keycloak - keycloak-examples-dist - zip - keycloak-examples-${project.version}.zip - - - target/${project.version} - - - - - adapter-downloads - install - - copy - - - - - org.keycloak - keycloak-as7-adapter-dist - zip - - - org.keycloak - keycloak-as7-adapter-dist - tar.gz - - - - org.keycloak - keycloak-eap6-adapter-dist - zip - - - org.keycloak - keycloak-eap6-adapter-dist - tar.gz - - - - org.keycloak - keycloak-jetty81-adapter-dist - zip - - - org.keycloak - keycloak-jetty81-adapter-dist - tar.gz - - - - org.keycloak - keycloak-jetty91-adapter-dist - zip - - - org.keycloak - keycloak-jetty91-adapter-dist - tar.gz - - - - org.keycloak - keycloak-jetty92-adapter-dist - zip - - - org.keycloak - keycloak-jetty92-adapter-dist - tar.gz - - - - org.keycloak - keycloak-jetty93-adapter-dist - zip - - - org.keycloak - keycloak-jetty93-adapter-dist - tar.gz - - - - org.keycloak - keycloak-js-adapter-dist - zip - - - org.keycloak - keycloak-js-adapter-dist - tar.gz - - - - org.keycloak - keycloak-tomcat6-adapter-dist - zip - - - org.keycloak - keycloak-tomcat6-adapter-dist - tar.gz - - - - org.keycloak - keycloak-tomcat7-adapter-dist - zip - - - org.keycloak - keycloak-tomcat7-adapter-dist - tar.gz - - - - org.keycloak - keycloak-tomcat8-adapter-dist - zip - - - org.keycloak - keycloak-tomcat8-adapter-dist - tar.gz - - - - org.keycloak - keycloak-wf8-adapter-dist - zip - - - org.keycloak - keycloak-wf8-adapter-dist - tar.gz - - - - org.keycloak - keycloak-wildfly-adapter-dist - zip - - - org.keycloak - keycloak-wildfly-adapter-dist - tar.gz - - - - org.keycloak - keycloak-fuse-adapter-dist - zip - - - org.keycloak - keycloak-fuse-adapter-dist - tar.gz - - - target/${project.version}/adapters/keycloak-oidc - - - - saml-adapter-downloads - install - - copy - - - - - org.keycloak - keycloak-saml-as7-adapter-dist - zip - - - org.keycloak - keycloak-saml-as7-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-eap6-adapter-dist - zip - - - org.keycloak - keycloak-saml-eap6-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-jetty81-adapter-dist - zip - - - org.keycloak - keycloak-saml-jetty81-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-jetty92-adapter-dist - zip - - - org.keycloak - keycloak-saml-jetty92-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-jetty93-adapter-dist - zip - - - org.keycloak - keycloak-saml-jetty93-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-tomcat6-adapter-dist - zip - - - org.keycloak - keycloak-saml-tomcat6-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-tomcat7-adapter-dist - zip - - - org.keycloak - keycloak-saml-tomcat7-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-tomcat8-adapter-dist - zip - - - org.keycloak - keycloak-saml-tomcat8-adapter-dist - tar.gz - - - - org.keycloak - keycloak-saml-wildfly-adapter-dist - zip - - - org.keycloak - keycloak-saml-wildfly-adapter-dist - tar.gz - - - target/${project.version}/adapters/saml + CopyDependencies + + ${settings.localRepository} + ${project.build.directory} + ${project.version} + diff --git a/distribution/downloads/src/main/java/CopyDependencies.java b/distribution/downloads/src/main/java/CopyDependencies.java new file mode 100644 index 0000000000..6ab527e57b --- /dev/null +++ b/distribution/downloads/src/main/java/CopyDependencies.java @@ -0,0 +1,49 @@ +import java.io.BufferedReader; +import java.io.File; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; + +/** + * Created by st on 06.02.17. + */ +public class CopyDependencies { + + public static void main(String[] args) throws IOException { + String version = args[2]; + + Path repository = new File(args[0]).toPath().resolve("org").resolve("keycloak"); + Path targetRoot = new File(args[1]).toPath().resolve(version); + + BufferedReader br = new BufferedReader(new InputStreamReader(CopyDependencies.class.getResourceAsStream("files"))); + + Path target = targetRoot; + for (String l = br.readLine(); l != null; l = br.readLine()) { + + if (l.startsWith("./")) { + target = targetRoot.resolve(l.replace("./", "").replace('/', File.separatorChar)); + if (!target.toFile().isDirectory()) { + target.toFile().mkdirs(); + } + } else if (l.trim().length() > 0) { + String[] t = l.trim().split(":"); + + String artifactName = t[0]; + String destName = t.length == 1 ? artifactName : t[1]; + + File artifactDir = repository.resolve(artifactName).resolve(version).toFile(); + + for (File f : artifactDir.listFiles((file, name) -> name.contains(".tar.gz") || name.contains(".zip"))) { + Files.copy(f.toPath(), target.resolve(f.getName().replace(artifactName, destName)), StandardCopyOption.REPLACE_EXISTING); + } + + System.out.println(artifactName); + } + } + + br.close(); + } + +} diff --git a/distribution/downloads/src/main/resources/files b/distribution/downloads/src/main/resources/files new file mode 100644 index 0000000000..e88c8e99a7 --- /dev/null +++ b/distribution/downloads/src/main/resources/files @@ -0,0 +1,33 @@ +./ + keycloak-server-dist:keycloak + keycloak-demo-dist:keycloak-demo + keycloak-server-overlay:keycloak-overlay + keycloak-proxy-dist:keycloak-proxy + keycloak-api-docs-dist:keycloak-api-docs + keycloak-examples-dist:keycloak-examples + +./adapters/keycloak-oidc + keycloak-as7-adapter-dist + keycloak-eap6-adapter-dist + keycloak-jetty81-adapter-dist + keycloak-jetty91-adapter-dist + keycloak-jetty92-adapter-dist + keycloak-jetty93-adapter-dist + keycloak-js-adapter-dist + keycloak-tomcat6-adapter-dist + keycloak-tomcat7-adapter-dist + keycloak-tomcat8-adapter-dist + keycloak-wf8-adapter-dist + keycloak-wildfly-adapter-dist + keycloak-fuse-adapter-dist + +./adapters/saml + keycloak-saml-as7-adapter-dist + keycloak-saml-eap6-adapter-dist + keycloak-saml-jetty81-adapter-dist + keycloak-saml-jetty92-adapter-dist + keycloak-saml-jetty93-adapter-dist + keycloak-saml-tomcat6-adapter-dist + keycloak-saml-tomcat7-adapter-dist + keycloak-saml-tomcat8-adapter-dist + keycloak-saml-wildfly-adapter-dist \ No newline at end of file diff --git a/distribution/feature-packs/adapter-feature-pack/pom.xml b/distribution/feature-packs/adapter-feature-pack/pom.xml index 9334e4e2c1..dcda6ee479 100755 --- a/distribution/feature-packs/adapter-feature-pack/pom.xml +++ b/distribution/feature-packs/adapter-feature-pack/pom.xml @@ -55,6 +55,13 @@ org.keycloak keycloak-undertow-adapter + + + + org.keycloak + keycloak-authz-client + + org.wildfly wildfly-feature-pack diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml index 673b5cd906..9603619bc0 100755 --- a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml +++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-adapter-core/main/module.xml @@ -34,6 +34,7 @@ + diff --git a/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-authz-client/main/module.xml b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-authz-client/main/module.xml new file mode 100755 index 0000000000..67cc62c319 --- /dev/null +++ b/distribution/feature-packs/adapter-feature-pack/src/main/resources/modules/system/add-ons/keycloak/org/keycloak/keycloak-authz-client/main/module.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli similarity index 52% rename from distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain.cli rename to distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli index 0ed854e5ef..d11e2b9f25 100644 --- a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain.cli +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-clustered.cli @@ -1,9 +1,5 @@ embed-host-controller --domain-config=domain.xml -# Early versions of keycloak used "default" for the standalone profile name. -# Yours maybe be something completely different. -set standaloneProfile=auth-server-standalone - # Early versions of keycloak used "ha" for the clustered profile name. # Yours maybe be something completely different. set clusteredProfile=auth-server-clustered @@ -12,125 +8,6 @@ set clusteredProfile=auth-server-clustered set pathToJson=../domain/configuration/keycloak-server.json -echo *** Begin Migration of /profile=$standaloneProfile *** -echo - -# Migrate from 1.8.1 to 1.9.1 -if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=work/:read-resource - echo Adding local-cache=work to keycloak cache container... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=work/:add(indexing=NONE,start=LAZY) - echo -end-if -# realmVersions cache deprecated in 2.1.0 -#if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:read-resource -# echo Adding local-cache=realmVersions to keycloak cache container... -# /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:add(indexing=NONE,start=LAZY) -# /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/component=transaction/:write-attribute(name=mode,value=BATCH) -# echo -#end-if - - -# Migrate from 1.9.1 to 1.9.2 -if (result == NONE) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:read-attribute(name=strategy) - echo Adding eviction strategy to keycloak users cache container... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:write-attribute(name=strategy,value=LRU) - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:write-attribute(name=max-entries,value=10000) - echo -end-if - -# Migrate from 1.9.2 to 1.9.8 -# NO CHANGES - -# Migrate from 1.9.8 to 2.0.0 -if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/:read-resource - echo Adding local-cache=authorization to keycloak cache container... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/:add(indexing=NONE,start=LAZY) - echo -end-if -if (result == undefined) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:read-attribute(name=strategy,include-defaults=false) - echo Updating authorization cache container.. - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:write-attribute(name=strategy,value=LRU) - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:write-attribute(name=max-entries,value=100) -end-if - -# Migrate from 2.0.0 to 2.1.0 -if (outcome == success) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:read-resource - echo Removing deprecated cache 'realmVersions' - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:remove - echo -end-if - -# Migrate kecloak-server.json (deprecated in 2.2.0) -if (result == []) of /profile=$standaloneProfile/subsystem=keycloak-server/:read-children-names(child-type=spi) - echo Migrating keycloak-server.json to keycloak-server subsystem... - /profile=$standaloneProfile/subsystem=keycloak-server/:migrate-json(file=$pathToJson) - echo -end-if - -# Find if we are using jpa or mongo -if (result == mongo) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=realm/:read-attribute(name=default-provider) - set persistenceProvider=mongo -else - set persistenceProvider=jpa -end-if - -# Migrate from 2.1.0 to 2.2.0 -if (result == update) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-get(name=properties,key=databaseSchema) - echo Updating connectionsJpa default properties... - /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-remove(name=properties,key=databaseSchema) - /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=initializeEmpty,value=true) - /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=migrationStrategy,value=update) - /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=migrationExport,value=${jboss.home.dir}/keycloak-database-update.sql) - echo -end-if -if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=userFederatedStorage/:read-resource - echo Adding spi=userFederatedStorage... - /profile=$standaloneProfile/subsystem=keycloak-server/spi=userFederatedStorage/:add(default-provider=$persistenceProvider) - echo -end-if -if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/:read-resource - echo Adding spi=jta-lookup... - /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/:add(default-provider=${keycloak.jta.lookup.provider:jboss}) - /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/provider=jboss/:add(enabled=true) - echo -end-if - -# Migrate from 2.2.0 to 2.2.1 -# NO CHANGES - -# Migrate from 2.2.1 to 2.3.0 -if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/:read-resource - echo Adding local-cache=keys to keycloak cache container... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/:add(indexing=NONE,start=LAZY) - echo -end-if -if (result == undefined) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:read-attribute(name=strategy,include-defaults=false) - echo Updating eviction and expiration in local-cache=keys... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:write-attribute(name=strategy,value=LRU) - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:write-attribute(name=max-entries,value=1000) - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=expiration/:write-attribute(name=max-idle,value=3600000) - echo -end-if -if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/:read-resource - echo Adding spi=publicKeyStorage... - /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/:add - /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/provider=infinispan/:add(properties={minTimeBetweenRequests => "10"},enabled=true) - echo -end-if - -# Migrate from 2.3.0 to 2.4.0 -# NO CHANGES - -# Migrate from 2.4.0 to 2.5.0 -if (result == NONE) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:read-attribute(name=strategy) - echo Adding eviction strategy to keycloak realms cache... - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:write-attribute(name=strategy,value=LRU) - /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:write-attribute(name=max-entries,value=10000) - echo -end-if - -echo *** End Migration of /profile=$standaloneProfile *** -echo echo echo *** Begin Migration of /profile=$clusteredProfile *** echo @@ -260,4 +137,4 @@ if (result == NONE) of /profile=$clusteredProfile/subsystem=infinispan/cache-con echo end-if -echo *** End Migration *** \ No newline at end of file +echo *** End Migration of /profile=$clusteredProfile *** \ No newline at end of file diff --git a/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli new file mode 100644 index 0000000000..b24819e3b0 --- /dev/null +++ b/distribution/feature-packs/server-feature-pack/src/main/resources/content/bin/migrate-domain-standalone.cli @@ -0,0 +1,128 @@ +embed-host-controller --domain-config=domain.xml + +# Early versions of keycloak used "default" for the standalone profile name. +# Yours maybe be something completely different. +set standaloneProfile=auth-server-standalone + +# keycloak-server.json is not normally on this path. +set pathToJson=../domain/configuration/keycloak-server.json + + +echo *** Begin Migration of /profile=$standaloneProfile *** +echo + +# Migrate from 1.8.1 to 1.9.1 +if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=work/:read-resource + echo Adding local-cache=work to keycloak cache container... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=work/:add(indexing=NONE,start=LAZY) + echo +end-if +# realmVersions cache deprecated in 2.1.0 +#if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:read-resource +# echo Adding local-cache=realmVersions to keycloak cache container... +# /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:add(indexing=NONE,start=LAZY) +# /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/component=transaction/:write-attribute(name=mode,value=BATCH) +# echo +#end-if + + +# Migrate from 1.9.1 to 1.9.2 +if (result == NONE) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:read-attribute(name=strategy) + echo Adding eviction strategy to keycloak users cache container... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:write-attribute(name=strategy,value=LRU) + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=users/component=eviction/:write-attribute(name=max-entries,value=10000) + echo +end-if + +# Migrate from 1.9.2 to 1.9.8 +# NO CHANGES + +# Migrate from 1.9.8 to 2.0.0 +if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/:read-resource + echo Adding local-cache=authorization to keycloak cache container... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/:add(indexing=NONE,start=LAZY) + echo +end-if +if (result == undefined) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:read-attribute(name=strategy,include-defaults=false) + echo Updating authorization cache container.. + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:write-attribute(name=strategy,value=LRU) + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=authorization/component=eviction/:write-attribute(name=max-entries,value=100) +end-if + +# Migrate from 2.0.0 to 2.1.0 +if (outcome == success) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:read-resource + echo Removing deprecated cache 'realmVersions' + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realmVersions/:remove + echo +end-if + +# Migrate kecloak-server.json (deprecated in 2.2.0) +if (result == []) of /profile=$standaloneProfile/subsystem=keycloak-server/:read-children-names(child-type=spi) + echo Migrating keycloak-server.json to keycloak-server subsystem... + /profile=$standaloneProfile/subsystem=keycloak-server/:migrate-json(file=$pathToJson) + echo +end-if + +# Find if we are using jpa or mongo +if (result == mongo) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=realm/:read-attribute(name=default-provider) + set persistenceProvider=mongo +else + set persistenceProvider=jpa +end-if + +# Migrate from 2.1.0 to 2.2.0 +if (result == update) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-get(name=properties,key=databaseSchema) + echo Updating connectionsJpa default properties... + /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-remove(name=properties,key=databaseSchema) + /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=initializeEmpty,value=true) + /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=migrationStrategy,value=update) + /profile=$standaloneProfile/subsystem=keycloak-server/spi=connectionsJpa/provider=default/:map-put(name=properties,key=migrationExport,value=${jboss.home.dir}/keycloak-database-update.sql) + echo +end-if +if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=userFederatedStorage/:read-resource + echo Adding spi=userFederatedStorage... + /profile=$standaloneProfile/subsystem=keycloak-server/spi=userFederatedStorage/:add(default-provider=$persistenceProvider) + echo +end-if +if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/:read-resource + echo Adding spi=jta-lookup... + /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/:add(default-provider=${keycloak.jta.lookup.provider:jboss}) + /profile=$standaloneProfile/subsystem=keycloak-server/spi=jta-lookup/provider=jboss/:add(enabled=true) + echo +end-if + +# Migrate from 2.2.0 to 2.2.1 +# NO CHANGES + +# Migrate from 2.2.1 to 2.3.0 +if (outcome == failed) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/:read-resource + echo Adding local-cache=keys to keycloak cache container... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/:add(indexing=NONE,start=LAZY) + echo +end-if +if (result == undefined) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:read-attribute(name=strategy,include-defaults=false) + echo Updating eviction and expiration in local-cache=keys... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:write-attribute(name=strategy,value=LRU) + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=eviction/:write-attribute(name=max-entries,value=1000) + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=keys/component=expiration/:write-attribute(name=max-idle,value=3600000) + echo +end-if +if (outcome == failed) of /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/:read-resource + echo Adding spi=publicKeyStorage... + /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/:add + /profile=$standaloneProfile/subsystem=keycloak-server/spi=publicKeyStorage/provider=infinispan/:add(properties={minTimeBetweenRequests => "10"},enabled=true) + echo +end-if + +# Migrate from 2.3.0 to 2.4.0 +# NO CHANGES + +# Migrate from 2.4.0 to 2.5.0 +if (result == NONE) of /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:read-attribute(name=strategy) + echo Adding eviction strategy to keycloak realms cache... + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:write-attribute(name=strategy,value=LRU) + /profile=$standaloneProfile/subsystem=infinispan/cache-container=keycloak/local-cache=realms/component=eviction/:write-attribute(name=max-entries,value=10000) + echo +end-if + +echo *** End Migration of /profile=$standaloneProfile *** \ No newline at end of file diff --git a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java index 249a3a0e78..3151a3b19f 100644 --- a/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java +++ b/federation/ldap/src/main/java/org/keycloak/storage/ldap/idm/store/ldap/LDAPIdentityStore.java @@ -124,6 +124,11 @@ public class LDAPIdentityStore implements IdentityStore { String rdnAttrVal = ldapObject.getAttributeAsString(rdnAttrName); + // Could be the case when RDN attribute of the target object is not included in Keycloak mappers + if (rdnAttrVal == null) { + return; + } + String oldRdnAttrVal = ldapObject.getDn().getFirstRdnAttrValue(); if (!oldRdnAttrVal.equals(rdnAttrVal)) { LDAPDn newLdapDn = ldapObject.getDn().getParentDn(); diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/MigrateUserFedToComponent.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/MigrateUserFedToComponent.java index bf3e92d33f..94cb5b1295 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/MigrateUserFedToComponent.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/MigrateUserFedToComponent.java @@ -40,7 +40,9 @@ public class MigrateUserFedToComponent extends AbstractUserFedToComponent { protected void generateStatementsImpl() throws CustomChangeException { List factories = kcSession.getKeycloakSessionFactory().getProviderFactories(UserStorageProvider.class); for (ProviderFactory factory : factories) { - convertFedProviderToComponent(factory.getId(), null); + if (!factory.getId().equals(LDAPConstants.LDAP_PROVIDER)) { + convertFedProviderToComponent(factory.getId(), null); + } } } diff --git a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java index dc9e6073c3..8d1e6795f4 100644 --- a/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java +++ b/model/jpa/src/main/java/org/keycloak/connections/jpa/updater/liquibase/custom/PortLdapUserFedToComponentModel.java @@ -27,7 +27,6 @@ public class PortLdapUserFedToComponentModel extends AbstractUserFedToComponent @Override protected void generateStatementsImpl() throws CustomChangeException { - String providerId = LDAPConstants.LDAP_PROVIDER; convertFedProviderToComponent(LDAPConstants.LDAP_PROVIDER, "org.keycloak.storage.ldap.mappers.LDAPStorageMapper"); } diff --git a/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java b/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java index be9bf51ddb..69fc05cae0 100644 --- a/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java +++ b/saml-core/src/main/java/org/keycloak/saml/processing/api/util/KeyInfoTools.java @@ -35,6 +35,9 @@ public class KeyInfoTools { * @return The object or {@code null} if not found. */ public static T getContent(Iterable objects, Class clazz) { + if (objects == null) { + return null; + } for (Object o : objects) { if (clazz.isInstance(o)) { return (T) o; @@ -45,11 +48,11 @@ public class KeyInfoTools { public static KeyName getKeyName(KeyInfo keyInfo) { - return getContent(keyInfo.getContent(), KeyName.class); + return keyInfo == null ? null : getContent(keyInfo.getContent(), KeyName.class); } public static X509Data getX509Data(KeyInfo keyInfo) { - return getContent(keyInfo.getContent(), X509Data.class); + return keyInfo == null ? null : getContent(keyInfo.getContent(), X509Data.class); } public static X509Certificate getX509Certificate(KeyInfo keyInfo) { diff --git a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java index 0ca04006c4..713025597f 100755 --- a/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java +++ b/server-spi-private/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java @@ -801,7 +801,7 @@ public class ModelToRepresentation { representation.setType(model.getType()); representation.setDecisionStrategy(model.getDecisionStrategy()); representation.setLogic(model.getLogic()); - representation.setConfig(model.getConfig()); + representation.setConfig(new HashMap<>(model.getConfig())); return representation; } diff --git a/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java b/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java index 8a71493fe5..cf550ed513 100755 --- a/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java +++ b/services/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java @@ -173,13 +173,17 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper { setIfNotEmpty(user::setLastName, attributeValuesInContext); } else { List currentAttributeValues = user.getAttributes().get(attribute); - if (attributeValuesInContext != null - && currentAttributeValues != null - && !CollectionUtil.collectionEquals(attributeValuesInContext, currentAttributeValues)) { - user.setAttribute(attribute, attributeValuesInContext); - } else if (attributeValuesInContext == null) { + if (attributeValuesInContext == null) { + // attribute no longer sent by brokered idp, remove it user.removeAttribute(attribute); + } else if (currentAttributeValues == null) { + // new attribute sent by brokered idp, add it + user.setAttribute(attribute, attributeValuesInContext); + } else if (!CollectionUtil.collectionEquals(attributeValuesInContext, currentAttributeValues)) { + // attribute sent by brokered idp has different values as before, update it + user.setAttribute(attribute, attributeValuesInContext); } + // attribute allready set } } diff --git a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java index 29b89428bd..403551fc3b 100755 --- a/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java +++ b/services/src/main/java/org/keycloak/exportimport/util/ExportUtils.java @@ -371,7 +371,7 @@ public class ExportUtils { Set policyResources = policy.getResources(); if (!policyResources.isEmpty()) { - List resourceNames = scopes.stream().map(Scope::getName).collect(Collectors.toList()); + List resourceNames = policyResources.stream().map(Resource::getName).collect(Collectors.toList()); config.put("resources", JsonSerialization.writeValueAsString(resourceNames)); } diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java index 5d467da5f6..9646ecceb2 100755 --- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java +++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java @@ -740,7 +740,7 @@ public class AuthenticationManager { if (!isSessionValid(realm, userSession)) { // Check if accessToken was for the offline session. if (!isCookie) { - UserSessionModel offlineUserSession = session.sessions().getUserSession(realm, token.getSessionState()); + UserSessionModel offlineUserSession = session.sessions().getOfflineUserSession(realm, token.getSessionState()); if (isOfflineSessionValid(realm, offlineUserSession)) { return new AuthResult(user, offlineUserSession, token); } diff --git a/testsuite/integration-arquillian/HOW-TO-RUN.md b/testsuite/integration-arquillian/HOW-TO-RUN.md new file mode 100644 index 0000000000..b11455b6af --- /dev/null +++ b/testsuite/integration-arquillian/HOW-TO-RUN.md @@ -0,0 +1,210 @@ +How To Run various testsuite configurations +=========================================== + +## Base steps + +It's recomended to build the workspace including distribution. + +```` +cd $KEYCLOAK_SOURCES +mvn clean install -DskipTests=true +cd distribution +mvn clean install +```` + +## Run adapter tests + +### Wildfly + +```` +# Prepare servers +mvn -f testsuite/integration-arquillian/servers/pom.xml clean install \ + -Pauth-server-wildfly \ + -Papp-server-wildfly + +# Run tests +mvn -f testsuite/integration-arquillian/tests/other/adapters/jboss/wildfly/pom.xml \ + clean install \ + -Pauth-server-wildfly \ + -Papp-server-wildfly +```` + +### JBoss Fuse 6.3 +---------------------------------------- +1) Download JBoss Fuse 6.3 to your filesystem. It can be downloaded from http://origin-repository.jboss.org/nexus/content/groups/m2-proxy/org/jboss/fuse/jboss-fuse-karaf +Assumed you downloaded `jboss-fuse-karaf-6.3.0.redhat-229.zip` + +2) Install to your local maven repository and change the properties according to your env (This step can be likely avoided if you somehow configure your local maven settings to point directly to Fuse repo): + +```` +mvn install:install-file \ + -DgroupId=org.jboss.fuse \ + -DartifactId=jboss-fuse-karaf \ + -Dversion=6.3.0.redhat-229 \ + -Dpackaging=zip \ + -Dfile=/mydownloads/jboss-fuse-karaf-6.3.0.redhat-229.zip +```` + +3) Prepare Fuse and run the tests (change props according to your environment, versions etc): + +```` +# Prepare Fuse server +mvn -f testsuite/integration-arquillian/servers \ + clean install \ + -Pauth-server-wildfly \ + -Papp-server-fuse63 \ + -Dfuse63.version=6.3.0.redhat-229 \ + -Dapp.server.karaf.update.config=true \ + -Dmaven.local.settings=$HOME/.m2/settings.xml \ + -Drepositories=,http://download.eng.bos.redhat.com/brewroot/repos/sso-7.1-build/latest/maven/ \ + -Dmaven.repo.local=$HOME/.m2/repository + +# Run the Fuse adapter tests +mvn -f testsuite/integration-arquillian/tests/other/adapters/karaf/fuse63/pom.xml \ + clean install \ + -Pauth-server-wildfly \ + -Papp-server-fuse63 \ + -Dfuse63.version=6.3.0.redhat-229 +```` + +### EAP6 with Hawtio + +1) Download JBoss EAP 6.4.0.GA zip + +2) Install to your local maven repository and change the properties according to your env (This step can be likely avoided if you somehow configure your local maven settings to point directly to EAP repo): + +```` +mvn install:install-file \ + -DgroupId=org.jboss.as \ + -DartifactId=jboss-as-dist \ + -Dversion=7.5.0.Final-redhat-21 \ + -Dpackaging=zip \ + -Dfile=/mydownloads/jboss-eap-6.4.0.zip +```` + +3) Download Fuse EAP installer (for example from http://origin-repository.jboss.org/nexus/content/groups/m2-proxy/com/redhat/fuse/eap/fuse-eap-installer/6.3.0.redhat-220/ ) + +4) Install previously downloaded file manually + +```` +mvn install:install-file \ + -DgroupId=com.redhat.fuse.eap \ + -DartifactId=fuse-eap-installer \ + -Dversion=6.3.0.redhat-220 \ + -Dpackaging=jar \ + -Dfile=/fuse-eap-installer-6.3.0.redhat-220.jar +```` + +5) Prepare EAP6 with Hawtio and run the test + +```` +# Prepare EAP6 and deploy hawtio +mvn -f testsuite/integration-arquillian/servers \ + clean install \ + -Pauth-server-wildfly \ + -Papp-server-eap6-fuse \ + -Dapp.server.jboss.version=7.5.0.Final-redhat-21 \ + -Dfuse.installer.version=6.3.0.redhat-220 + +# Run the test +mvn -f testsuite/integration-arquillian/tests/other/adapters/jboss/eap6-fuse/pom.xml \ + clean install \ + -Pauth-server-wildfly \ + -Papp-server-eap6-fuse +```` + +## Migration test + +### DB migration test + +This test will: + - start Keycloak 1.9.8 + - import realm and some data to MySQL DB + - stop Keycloak 1.9.8 + - start latest KEycloak, which automatically updates DB from 1.9.8 + - Do some test that data are correct + + +1) Prepare MySQL DB and ensure that MySQL DB is empty. See [../../misc/DatabaseTesting.md](../../misc/DatabaseTesting.md) for some hints for locally prepare Docker MySQL image. + +2) Run the test (Update according to your DB connection, versions etc): + +```` +export DB_HOST=localhost + +mvn -f testsuite/integration-arquillian/pom.xml \ + clean install \ + -Pauth-server-wildfly,jpa,clean-jpa,auth-server-migration \ + -Dtest=MigrationTest \ + -Dmigration.mode=auto \ + -Dmigrated.auth.server.version=1.9.8.Final \ + -Djdbc.mvn.groupId=mysql \ + -Djdbc.mvn.version=5.1.29 \ + -Djdbc.mvn.artifactId=mysql-connector-java \ + -Dkeycloak.connectionsJpa.url=jdbc:mysql://$DB_HOST/keycloak \ + -Dkeycloak.connectionsJpa.user=keycloak \ + -Dkeycloak.connectionsJpa.password=keycloak +```` + +### JSON export/import migration test +This will start latest Keycloak and import the realm JSON file, which was previously exported from Keycloak 1.9.8.Final + +```` +mvn -f testsuite/integration-arquillian/pom.xml \ + clean install \ + -Pauth-server-wildfly,migration-import \ + -Dtest=MigrationTest \ + -Dmigration.mode=import \ + -Dmigrated.auth.server.version=1.9.8.Final +```` + + +## Social Login +The social login tests require setup of all social networks including an example social user. These details can't be +shared as it would result in the clients and users eventually being blocked. By default these tests are skipped. + +To run the full test you need to configure clients in Google, Facebook, GitHub, Twitter, LinkedIn, Microsoft and +StackOverflow. See the server administration guide for details on how to do that. Further, you also need to create a +sample user that can login to the social network. + +The details should be added to a standard properties file. For some properties you can use shared common properties and +override when needed. Or you can specify these for all providers. All providers require at least clientId and +clientSecret (StackOverflow also requires clientKey). + +An example social.properties file looks like: + + common.username=sampleuser@example.org + common.password=commonpassword + common.profile.firstName=Foo + common.profile.lastName=Bar + common.profile.email=sampleuser@example.org + + google.clientId=asdfasdfasdfasdfsadf + google.clientSecret=zxcvzxcvzxcvzxcv + + facebook.clientId=asdfasdfasdfasdfsadf + facebook.clientSecret=zxcvzxcvzxcvzxcv + facebook.profile.lastName=Test + +In the example above the common username, password and profile are shared for all providers, but Facebook has a +different last name. + +Some providers actively block bots so you need to use a proper browser to test. Either Firefox or Chrome should work. + +To run the tests run: + + mvn -f testsuite/integration-arquillian/pom.xml \ + clean install \ + -Pauth-server-wildfly \ + -Dtest=SocialLoginTest \ + -Dbrowser=chrome \ + -Dsocial.config=/path/to/social.properties + + +## Different Browsers + +To run with Chrome add `-Dbrowser=chrome`. Depending on the Chrome version you have you may need to download the latest +chromedriver from https://sites.google.com/a/chromium.org/chromedriver/downloads and point to it with +`-Dwebdriver.chrome.driver=/path/to/chromedriver`. + + \ No newline at end of file diff --git a/testsuite/integration-arquillian/README.md b/testsuite/integration-arquillian/README.md index 965dead85b..14ec0e1f12 100644 --- a/testsuite/integration-arquillian/README.md +++ b/testsuite/integration-arquillian/README.md @@ -4,6 +4,9 @@ For overview see the **Modules Overview** section at the bottom of this README. +## How to run tests + +See the file [HOW-TO-RUN.md](HOW-TO-RUN.md) . ## Container Lifecycles diff --git a/testsuite/integration-arquillian/servers/app-server/karaf/common/install-features.sh b/testsuite/integration-arquillian/servers/app-server/karaf/common/install-features.sh index b8cba84bed..751219dd46 100755 --- a/testsuite/integration-arquillian/servers/app-server/karaf/common/install-features.sh +++ b/testsuite/integration-arquillian/servers/app-server/karaf/common/install-features.sh @@ -25,7 +25,8 @@ do if "$UPDATE_CONFIG" == "true"; then echo "Updating Config" ./client $CLIENT_AUTH -f update-config.cli - if [ $? -ne 0 ]; then + if [ $? -ne 0 ]; then + echo "Call update-config.cli failed!"; RESULT=1; else ./client $CLIENT_AUTH config:list | grep org.ops4j.pax.url.mvn. @@ -34,12 +35,16 @@ do echo "Installing features." ./client $CLIENT_AUTH -f install-features.cli - if [ $? -ne 0 ]; then RESULT=1; fi + if [ $? -ne 0 ]; then + echo "Call install-features.cli failed!"; + RESULT=1; + fi if "$UPDATE_CONFIG" == "true"; then echo "Updating Config - Keycloak authentication" ./client $CLIENT_AUTH -f update-config-auth.cli if [ $? -ne 0 ]; then + echo "Call update-config-auth.cli failed!"; RESULT=1; fi fi diff --git a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keystore/keycloak.truststore b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keystore/keycloak.truststore index 2df5170f9b..da0f709f5a 100644 Binary files a/testsuite/integration-arquillian/servers/auth-server/jboss/common/keystore/keycloak.truststore and b/testsuite/integration-arquillian/servers/auth-server/jboss/common/keystore/keycloak.truststore differ 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 index 3a4d0b97fc..bd6e5a051f 100644 --- 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 @@ -35,12 +35,17 @@ import static org.keycloak.testsuite.arquillian.AppServerTestEnricher.getAppServ */ public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenerator { + // Will be replaced in runtime by real auth-server-container + public static final String AUTH_SERVER_CURRENT = "auth-server-current"; + protected final Logger log = Logger.getLogger(this.getClass()); @Override public List generate(TestClass testClass) { List deployments = super.generate(testClass); + checkAuthServerTestDeployment(deployments, testClass); + String appServerQualifier = getAppServerQualifier( testClass.getJavaClass()); @@ -56,4 +61,17 @@ public class DeploymentTargetModifier extends AnnotationDeploymentScenarioGenera return deployments; } + private void checkAuthServerTestDeployment(List descriptions, TestClass testClass) { + for (DeploymentDescription deployment : descriptions) { + if (deployment.getTarget() != null) { + String containerQualifier = deployment.getTarget().getName(); + if (AUTH_SERVER_CURRENT.equals(containerQualifier)) { + String authServerQualifier = AuthServerTestEnricher.AUTH_SERVER_CONTAINER; + log.infof("Setting target container for deployment %s.%s: %s", testClass.getName(), deployment.getName(), authServerQualifier); + deployment.setTarget(new TargetDescription(authServerQualifier)); + } + } + } + } + } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java index 310a3bc464..105032e0d9 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakArquillianExtension.java @@ -18,6 +18,7 @@ package org.keycloak.testsuite.arquillian; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; +import org.jboss.arquillian.container.osgi.OSGiApplicationArchiveProcessor; 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; @@ -63,7 +64,8 @@ public class KeycloakArquillianExtension implements LoadableExtension { builder .override(ResourceProvider.class, URLResourceProvider.class, URLProvider.class) .override(ResourceProvider.class, CustomizableURLResourceProvider.class, URLProvider.class) - .override(ResourceProvider.class, ContainerCustomizableURLResourceProvider.class, URLProvider.class); + .override(ResourceProvider.class, ContainerCustomizableURLResourceProvider.class, URLProvider.class) + .override(ApplicationArchiveProcessor.class, OSGiApplicationArchiveProcessor.class, KeycloakOSGiApplicationArchiveProcessor.class); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakOSGiApplicationArchiveProcessor.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakOSGiApplicationArchiveProcessor.java new file mode 100644 index 0000000000..90d3b2e710 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/arquillian/KeycloakOSGiApplicationArchiveProcessor.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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; + +import org.jboss.arquillian.container.osgi.OSGiApplicationArchiveProcessor; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.logging.Logger; +import org.jboss.shrinkwrap.api.Archive; + +/** + * @author Marek Posolda + */ +public class KeycloakOSGiApplicationArchiveProcessor extends OSGiApplicationArchiveProcessor { + + private static final Logger log = Logger.getLogger(KeycloakOSGiApplicationArchiveProcessor.class); + + // We want to ignore OSGI for exampleAdapter tests + @Override + public void process(Archive appArchive, TestClass testClass) { + Class clazz = testClass.getJavaClass(); + boolean isExampleAdapterTest = isExampleAdapterTest(clazz); + + if (isExampleAdapterTest) { + log.infof("Ignore OSGiApplicationArchiveProcessor for test %s", clazz.getName()); + } else { + super.process(appArchive, testClass); + } + } + + public static boolean isExampleAdapterTest(Class clazz) { + Class parent = clazz; + while (true) { + parent = parent.getSuperclass(); + if (parent == null) { + return false; + } else if (parent.getName().equals("org.keycloak.testsuite.adapter.AbstractExampleAdapterTest")) { + return true; + } + } + } +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java index 9748a9f114..2a3f944c5f 100755 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/AccountUpdateProfilePage.java @@ -63,6 +63,10 @@ public class AccountUpdateProfilePage extends AbstractAccountPage { return RealmsResource.accountUrl(UriBuilder.fromUri(getAuthServerRoot())).build("test").toString(); } + public String getPath(String realm) { + return RealmsResource.accountUrl(UriBuilder.fromUri(getAuthServerRoot())).build(realm).toString(); + } + public void updateProfile(String firstName, String lastName, String email) { firstNameInput.clear(); firstNameInput.sendKeys(firstName); @@ -141,6 +145,10 @@ public class AccountUpdateProfilePage extends AbstractAccountPage { driver.navigate().to(getPath()); } + public void open(String realm) { + driver.navigate().to(getPath(realm)); + } + public void backToApplication() { backToApplicationLink.click(); } diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java index 704060bab3..239cfb9953 100644 --- a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/pages/LoginUpdateProfilePage.java @@ -41,12 +41,18 @@ public class LoginUpdateProfilePage extends AbstractPage { private WebElement loginErrorMessage; public void update(String firstName, String lastName, String email) { - firstNameInput.clear(); - firstNameInput.sendKeys(firstName); - lastNameInput.clear(); - lastNameInput.sendKeys(lastName); - emailInput.clear(); - emailInput.sendKeys(email); + if (firstName != null) { + firstNameInput.clear(); + firstNameInput.sendKeys(firstName); + } + if (lastName != null) { + lastNameInput.clear(); + lastNameInput.sendKeys(lastName); + } + if (email != null) { + emailInput.clear(); + emailInput.sendKeys(email); + } submitButton.click(); } 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 index f21a48c27b..d41b063dbb 100644 --- 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 @@ -116,6 +116,7 @@ public abstract class AbstractFuseExampleAdapterTest extends AbstractExampleAdap testRealmLoginPage.form().login("admin", "password"); assertCurrentUrlStartsWith(adminInterface); assertTrue(driver.getPageSource().contains("Hello admin!")); + assertTrue(driver.getPageSource().contains("This second sentence is returned from a Camel RestDSL endpoint")); customerListing.navigateTo(); customerListing.clickLogOut(); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java index b2af5ffca5..2e15ad16c5 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/AbstractUserAttributeMapperTest.java @@ -233,4 +233,24 @@ public abstract class AbstractUserAttributeMapperTest extends AbstractBaseBroker .build() ); } + + @Test + public void testAddBasicMappingMultipleValues() { + testValueMapping(ImmutableMap.>builder() + .build(), + ImmutableMap.>builder() + .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.builder().add("second value").add("second value 2").build()) + .build() + ); + } + + @Test + public void testDeleteBasicMappingMultipleValues() { + testValueMapping(ImmutableMap.>builder() + .put(ATTRIBUTE_TO_MAP_NAME, ImmutableList.builder().add("second value").add("second value 2").build()) + .build(), + ImmutableMap.>builder() + .build() + ); + } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java new file mode 100644 index 0000000000..6ac3970ca6 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/SocialLoginTest.java @@ -0,0 +1,232 @@ +package org.keycloak.testsuite.broker; + +import org.jboss.arquillian.graphene.Graphene; +import org.jboss.arquillian.graphene.page.Page; +import org.jboss.arquillian.graphene.wait.WebDriverWait; +import org.junit.Assume; +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.common.Profile; +import org.keycloak.representations.idm.IdentityProviderRepresentation; +import org.keycloak.representations.idm.RealmRepresentation; +import org.keycloak.testsuite.AbstractKeycloakTest; +import org.keycloak.testsuite.cli.exec.ExecutionException; +import org.keycloak.testsuite.pages.AccountUpdateProfilePage; +import org.keycloak.testsuite.pages.LoginPage; +import org.keycloak.testsuite.pages.LoginUpdateProfilePage; +import org.keycloak.testsuite.util.IdentityProviderBuilder; +import org.keycloak.testsuite.util.RealmBuilder; +import org.openqa.selenium.By; +import org.openqa.selenium.support.ui.ExpectedConditions; + +import java.io.FileInputStream; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeTrue; + +/** + * Created by st on 19.01.17. + */ +public class SocialLoginTest extends AbstractKeycloakTest { + + public static final String SOCIAL_CONFIG = "social.config"; + + private static Properties config = new Properties(); + + @Page + public AccountUpdateProfilePage account; + + @Page + public LoginPage loginPage; + + @Page + public LoginUpdateProfilePage updateProfilePage; + + @BeforeClass + public static void loadConfig() throws Exception { + assumeTrue(System.getProperties().containsKey(SOCIAL_CONFIG)); + + config.load(new FileInputStream(System.getProperty(SOCIAL_CONFIG))); + } + + @Override + public void addTestRealms(List testRealms) { + RealmRepresentation rep = RealmBuilder.create().name("social").build(); + List idps = new LinkedList<>(); + rep.setIdentityProviders(idps); + + idps.add(buildIdp("google")); + idps.add(buildIdp("facebook")); + idps.add(buildIdp("github")); + idps.add(buildIdp("twitter")); + idps.add(buildIdp("linkedin")); + idps.add(buildIdp("microsoft")); + idps.add(buildIdp("stackoverflow")); + + testRealms.add(rep); + } + + @Test + public void googleLogin() throws InterruptedException { + account.open("social"); + + loginPage.clickSocial("google"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("Email"))); + + driver.findElement(By.id("Email")).sendKeys(config.getProperty("google.username", config.getProperty("common.username"))); + driver.findElement(By.id("next")).click(); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("Passwd"))); + + driver.findElement(By.id("Passwd")).sendKeys(config.getProperty("google.password", config.getProperty("common.password"))); + driver.findElement(By.id("signIn")).click(); + + Graphene.waitGui().until(ExpectedConditions.elementToBeClickable(By.id("submit_approve_access"))); + + driver.findElement(By.id("submit_approve_access")).click(); + + assertEquals(config.getProperty("google.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("google.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("google.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void faceBookLogin() { + account.open("social"); + + loginPage.clickSocial("facebook"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("email"))); + driver.findElement(By.id("email")).sendKeys(config.getProperty("facebook.username", config.getProperty("common.username"))); + driver.findElement(By.id("pass")).sendKeys(config.getProperty("facebook.password", config.getProperty("common.password"))); + + driver.findElement(By.id("loginbutton")).click(); + + assertEquals(config.getProperty("facebook.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("facebook.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("facebook.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void githubLogin() { + account.open("social"); + + loginPage.clickSocial("github"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("login_field"))); + driver.findElement(By.id("login_field")).sendKeys(config.getProperty("github.username", config.getProperty("common.username"))); + driver.findElement(By.id("password")).sendKeys(config.getProperty("github.password", config.getProperty("common.password"))); + + driver.findElement(By.name("commit")).click(); + + assertEquals(config.getProperty("github.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("github.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("github.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void twitterLogin() { + account.open("social"); + + loginPage.clickSocial("twitter"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("username_or_email"))); + driver.findElement(By.id("username_or_email")).sendKeys(config.getProperty("twitter.username", config.getProperty("common.username"))); + driver.findElement(By.id("password")).sendKeys(config.getProperty("twitter.password", config.getProperty("common.password"))); + + driver.findElement(By.id("allow")).click(); + + assertTrue(updateProfilePage.isCurrent()); + + assertEquals(config.getProperty("twitter.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("twitter.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals("", updateProfilePage.getEmail()); + + updateProfilePage.update(null, null, "keycloakey@gmail.com"); + + assertEquals(config.getProperty("twitter.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("twitter.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("twitter.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void linkedinLogin() { + account.open("social"); + + loginPage.clickSocial("linkedin"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.id("session_key-oauth2SAuthorizeForm"))); + driver.findElement(By.id("session_key-oauth2SAuthorizeForm")).sendKeys(config.getProperty("linkedin.username", config.getProperty("common.username"))); + driver.findElement(By.id("session_password-oauth2SAuthorizeForm")).sendKeys(config.getProperty("linkedin.password", config.getProperty("common.password"))); + + driver.findElement(By.name("authorize")).click(); + + assertEquals(config.getProperty("linkedin.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("linkedin.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("linkedin.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void microsoftLogin() { + account.open("social"); + + loginPage.clickSocial("microsoft"); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("loginfmt"))); + driver.findElement(By.name("loginfmt")).sendKeys(config.getProperty("microsoft.username", config.getProperty("common.username"))); + driver.findElement(By.xpath("//input[@value='Next']")).click(); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("passwd"))); + driver.findElement(By.name("passwd")).sendKeys(config.getProperty("microsoft.password", config.getProperty("common.password"))); + driver.findElement(By.xpath("//input[@value='Sign in']")).click(); + + assertEquals(config.getProperty("microsoft.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("microsoft.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("microsoft.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + @Test + public void stackoverflowLogin() { + account.open("social"); + + loginPage.clickSocial("stackoverflow"); + + Graphene.waitModel().until(ExpectedConditions.visibilityOfElementLocated(By.xpath("//a[@title='log in with Stack_Exchange']"))); + driver.findElement(By.xpath("//a[@title='log in with Stack_Exchange']")).click(); + + driver.switchTo().frame(driver.findElement(By.id("affiliate-signin-iframe"))); + + Graphene.waitGui().until(ExpectedConditions.visibilityOfElementLocated(By.name("email"))); + driver.findElement(By.name("email")).sendKeys(config.getProperty("stackoverflow.username", config.getProperty("common.username"))); + driver.findElement(By.name("password")).sendKeys(config.getProperty("stackoverflow.password", config.getProperty("common.password"))); + + driver.findElement(By.xpath("//input[@value='Sign In']")).click(); + + assertEquals(config.getProperty("stackoverflow.profile.firstName", config.getProperty("common.profile.firstName")), updateProfilePage.getFirstName()); + assertEquals(config.getProperty("stackoverflow.profile.lastName", config.getProperty("common.profile.lastName")), updateProfilePage.getLastName()); + assertEquals("", updateProfilePage.getEmail()); + + updateProfilePage.update(null, null, "keycloakey@gmail.com"); + + assertEquals(config.getProperty("stackoverflow.profile.firstName", config.getProperty("common.profile.firstName")), account.getFirstName()); + assertEquals(config.getProperty("stackoverflow.profile.lastName", config.getProperty("common.profile.lastName")), account.getLastName()); + assertEquals(config.getProperty("stackoverflow.profile.email", config.getProperty("common.profile.email")), account.getEmail()); + } + + private IdentityProviderRepresentation buildIdp(String id) { + IdentityProviderRepresentation idp = IdentityProviderBuilder.create().alias(id).providerId(id).build(); + idp.setEnabled(true); + idp.getConfig().put("clientId", config.getProperty(id + ".clientId")); + idp.getConfig().put("clientSecret", config.getProperty(id + ".clientSecret")); + if (id.equals("stackoverflow")) { + idp.getConfig().put("key", config.getProperty(id + ".clientKey")); + } + return idp; + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java index 4f6d6886ab..0526ab227f 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/migration/MigrationTest.java @@ -23,6 +23,7 @@ import java.util.stream.Collectors; import javax.ws.rs.NotFoundException; import org.jboss.arquillian.container.test.api.Deployment; +import org.jboss.arquillian.container.test.api.TargetsContainer; import org.jboss.shrinkwrap.api.spec.WebArchive; import org.junit.Before; import org.junit.Test; @@ -52,6 +53,7 @@ import org.keycloak.representations.idm.authorization.PolicyRepresentation; import org.keycloak.storage.UserStorageProvider; import org.keycloak.testsuite.AbstractKeycloakTest; import org.keycloak.testsuite.Assert; +import org.keycloak.testsuite.arquillian.DeploymentTargetModifier; import org.keycloak.testsuite.arquillian.migration.Migration; import org.keycloak.testsuite.runonserver.RunHelpers; import org.keycloak.testsuite.runonserver.RunOnServerDeployment; @@ -78,6 +80,7 @@ public class MigrationTest extends AbstractKeycloakTest { private RealmResource masterRealm; @Deployment + @TargetsContainer(DeploymentTargetModifier.AUTH_SERVER_CURRENT) public static WebArchive deploy() { return RunOnServerDeployment.create(); } @@ -361,14 +364,27 @@ public class MigrationTest extends AbstractKeycloakTest { } private void testOfflineTokenLogin() { - log.info("test login with old offline token"); - String oldOfflineToken = suiteContext.getMigrationContext().getOfflineToken(); - Assert.assertNotNull(oldOfflineToken); + if (isImportMigrationMode()) { + log.info("Skip offline token login test in the 'import' migrationMode"); + } else { + log.info("test login with old offline token"); + String oldOfflineToken = suiteContext.getMigrationContext().getOfflineToken(); + Assert.assertNotNull(oldOfflineToken); - oauth.realm(MIGRATION); - oauth.clientId("migration-test-client"); - OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(oldOfflineToken, "b2c07929-69e3-44c6-8d7f-76939000b3e4"); - AccessToken accessToken = oauth.verifyToken(response.getAccessToken()); - assertEquals("migration-test-user", accessToken.getPreferredUsername()); + oauth.realm(MIGRATION); + oauth.clientId("migration-test-client"); + OAuthClient.AccessTokenResponse response = oauth.doRefreshTokenRequest(oldOfflineToken, "b2c07929-69e3-44c6-8d7f-76939000b3e4"); + AccessToken accessToken = oauth.verifyToken(response.getAccessToken()); + assertEquals("migration-test-user", accessToken.getPreferredUsername()); + } + } + + private String getMigrationMode() { + return System.getProperty("migration.mode"); + } + + private boolean isImportMigrationMode() { + String mode = getMigrationMode(); + return "import".equals(mode); } } diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java index f4cf19ca2a..150ff6b3cb 100755 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java @@ -465,6 +465,9 @@ public class OfflineTokenTest extends AbstractKeycloakTest { // Set the time offset, so that "normal" userSession expires setTimeOffset(86400); + // Remove expired sessions. This will remove "normal" userSession + testingClient.testing().removeUserSessions(appRealm.toRepresentation().getId()); + // Refresh with the offline token tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret1"); diff --git a/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore index 2df5170f9b..da0f709f5a 100644 Binary files a/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore and b/testsuite/integration-arquillian/tests/base/src/test/resources/keystore/keycloak.truststore differ diff --git a/testsuite/integration-arquillian/tests/other/server-config-migration/pom.xml b/testsuite/integration-arquillian/tests/other/server-config-migration/pom.xml index 7dd8cea1b3..559c60f80b 100644 --- a/testsuite/integration-arquillian/tests/other/server-config-migration/pom.xml +++ b/testsuite/integration-arquillian/tests/other/server-config-migration/pom.xml @@ -24,7 +24,7 @@ org.keycloak.testsuite integration-arquillian-tests-other - 2.5.1.Final-SNAPSHOT + 3.0.0.CR1-SNAPSHOT ../pom.xml @@ -323,7 +323,8 @@ - + + diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPNoMSADTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPNoMSADTest.java new file mode 100644 index 0000000000..917327ae6e --- /dev/null +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/storage/ldap/LDAPNoMSADTest.java @@ -0,0 +1,163 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * 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.federation.storage.ldap; + +import java.util.List; +import java.util.Map; + +import org.jboss.logging.Logger; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.FixMethodOrder; +import org.junit.Test; +import org.junit.rules.RuleChain; +import org.junit.rules.TestRule; +import org.junit.runners.MethodSorters; +import org.keycloak.common.util.MultivaluedHashMap; +import org.keycloak.component.ComponentModel; +import org.keycloak.models.KeycloakSession; +import org.keycloak.models.LDAPConstants; +import org.keycloak.models.RealmModel; +import org.keycloak.models.UserModel; +import org.keycloak.services.managers.RealmManager; +import org.keycloak.storage.UserStorageProvider; +import org.keycloak.storage.UserStorageProviderModel; +import org.keycloak.storage.ldap.LDAPStorageProvider; +import org.keycloak.storage.ldap.LDAPStorageProviderFactory; +import org.keycloak.storage.ldap.idm.model.LDAPObject; +import org.keycloak.storage.ldap.mappers.LDAPStorageMapper; +import org.keycloak.testsuite.rule.KeycloakRule; +import org.keycloak.testsuite.rule.LDAPRule; + +/** + * Test for special scenarios, which don't work on MSAD (eg. renaming user RDN to "sn=john2" ) + * + * @author Marek Posolda + */ +@FixMethodOrder(MethodSorters.NAME_ASCENDING) +public class LDAPNoMSADTest { + + private static final Logger log = Logger.getLogger(LDAPProvidersIntegrationTest.class); + + + // Skip this test on MSAD + private static LDAPRule ldapRule = new LDAPRule((Map ldapConfig) -> { + + String vendor = ldapConfig.get(LDAPConstants.VENDOR); + return (vendor.equals(LDAPConstants.VENDOR_ACTIVE_DIRECTORY)); + + }); + + private static ComponentModel ldapModel = null; + + + private static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { + + @Override + public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { + LDAPTestUtils.addLocalUser(manager.getSession(), appRealm, "marykeycloak", "mary@test.com", "password-app"); + + MultivaluedHashMap ldapConfig = LDAPTestUtils.getLdapRuleConfig(ldapRule); + ldapConfig.putSingle(LDAPConstants.SYNC_REGISTRATIONS, "true"); + ldapConfig.putSingle(LDAPConstants.EDIT_MODE, UserStorageProvider.EditMode.WRITABLE.toString()); + UserStorageProviderModel model = new UserStorageProviderModel(); + model.setLastSync(0); + model.setChangedSyncPeriod(-1); + model.setFullSyncPeriod(-1); + model.setName("test-ldap"); + model.setPriority(0); + model.setProviderId(LDAPStorageProviderFactory.PROVIDER_NAME); + model.setConfig(ldapConfig); + + ldapModel = appRealm.addComponentModel(model); + LDAPTestUtils.addZipCodeLDAPMapper(appRealm, ldapModel); + + // Delete all LDAP users and add some new for testing + LDAPStorageProvider ldapFedProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); + LDAPTestUtils.removeAllLDAPUsers(ldapFedProvider, appRealm); + + LDAPObject john = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "johnkeycloak", "John", "Doe", "john@email.org", null, "1234"); + LDAPTestUtils.updateLDAPPassword(ldapFedProvider, john, "Password1"); + + LDAPObject existing = LDAPTestUtils.addLDAPUser(ldapFedProvider, appRealm, "existing", "Existing", "Foo", "existing@email.org", null, "5678"); + + appRealm.getClientByClientId("test-app").setDirectAccessGrantsEnabled(true); + } + }); + + @ClassRule + public static TestRule chain = RuleChain + .outerRule(ldapRule) + .around(keycloakRule); + + + // KEYCLOAK-4364 + @Test + public void testUpdateWithUnmappedRdnAttribute() { + KeycloakSession session = keycloakRule.startSession(); + ComponentModel snMapper = null; + try { + // Create LDAP user with "sn" attribute in RDN like "sn=johnkeycloak2,ou=People,dc=domain,dc=com" + RealmModel appRealm = session.realms().getRealmByName("test"); + LDAPStorageProvider ldapProvider = LDAPTestUtils.getLdapProvider(session, ldapModel); + LDAPObject john2 = LDAPTestUtils.addLDAPUser(ldapProvider, appRealm, "johnkeycloak2", "john2", "Doe2", "john2@email.org", null, "4321"); + + john2.setRdnAttributeName("sn"); + ldapProvider.getLdapIdentityStore().update(john2); + + // Remove "sn" mapper + List components = appRealm.getComponents(ldapModel.getId(), LDAPStorageMapper.class.getName()); + for (ComponentModel mapper : components) { + if (mapper.getName().equals("last name")) { + snMapper = mapper; + break; + } + } + + Assert.assertNotNull(snMapper); + appRealm.removeComponent(snMapper); + } finally { + keycloakRule.stopSession(session, true); + } + + + // Try to update johnkeycloak2 user. It shouldn't try to update DN + session = keycloakRule.startSession(); + try { + RealmModel appRealm = session.realms().getRealmByName("test"); + UserModel johnkeycloak2 = session.users().getUserByUsername("johnkeycloak2", appRealm); + Assert.assertNotNull(johnkeycloak2); + + johnkeycloak2.setFirstName("foo2"); + johnkeycloak2.setLastName("foo"); + } finally { + keycloakRule.stopSession(session, true); + } + + // Re-create "sn" mapper back + session = keycloakRule.startSession(); + try { + RealmModel appRealm = session.realms().getRealmByName("test"); + snMapper.setId(null); + appRealm.addComponentModel(snMapper); + } finally { + keycloakRule.stopSession(session, true); + } + + } +}