Merge remote-tracking branch 'upstream/master'

This commit is contained in:
mhajas 2015-10-26 12:26:47 +01:00
commit df24d8a01f
35 changed files with 752 additions and 52 deletions

View file

@ -31,7 +31,7 @@
<constraints nullable="false"/>
</column>
<column name="LAST_SESSION_REFRESH" type="INT"/>
<column name="OFFLINE_FLAG" type="VARCHAR(4)">
<column name="OFFLINE" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="DATA" type="CLOB"/>
@ -47,14 +47,14 @@
<column name="CLIENT_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="OFFLINE_FLAG" type="VARCHAR(4)">
<column name="OFFLINE" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="TIMESTAMP" type="INT"/>
<column name="DATA" type="CLOB"/>
</createTable>
<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>
<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION"/>
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE" constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION"/>
</changeSet>
</databaseChangeLog>

View file

@ -0,0 +1,105 @@
<?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.1_from15">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.6.0 version. Skipped 1.6.1_from15 changeSet and marked as ran">
<not>
<changeSetExecuted id="1.6.0" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-1.6.0.xml" />
</not>
</preConditions>
<addColumn tableName="REALM">
<column name="OFFLINE_SESSION_IDLE_TIMEOUT" type="INT" defaultValueNumeric="0"/>
<column name="REVOKE_REFRESH_TOKEN" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="KEYCLOAK_ROLE">
<column name="SCOPE_PARAM_REQUIRED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
</addColumn>
<addColumn tableName="CLIENT">
<column name="ROOT_URL" type="VARCHAR(255)"/>
<column name="DESCRIPTION" type="VARCHAR(255)"/>
</addColumn>
<createTable tableName="OFFLINE_USER_SESSION">
<column name="USER_SESSION_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="USER_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="LAST_SESSION_REFRESH" type="INT"/>
<column name="OFFLINE_FLAG" type="VARCHAR(4)">
<constraints nullable="false"/>
</column>
<column name="DATA" type="CLOB"/>
</createTable>
<createTable tableName="OFFLINE_CLIENT_SESSION">
<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="OFFLINE_FLAG" type="VARCHAR(4)">
<constraints nullable="false"/>
</column>
<column name="TIMESTAMP" type="INT"/>
<column name="DATA" type="CLOB"/>
</createTable>
<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_US_SES_PK2" tableName="OFFLINE_USER_SESSION"/>
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK2" tableName="OFFLINE_CLIENT_SESSION"/>
</changeSet>
<!-- Just for the update from 1.6.0 -->
<changeSet author="mposolda@redhat.com" id="1.6.1_from16">
<preConditions onFail="MARK_RAN" onFailMessage="Upgrading from 1.5.0 or older version. Skipped 1.6.1_from16 changeSet and marked as ran">
<changeSetExecuted id="1.6.0" author="mposolda@redhat.com" changeLogFile="META-INF/jpa-changelog-1.6.0.xml" />
</preConditions>
<dropPrimaryKey constraintName="CONSTRAINT_OFFLINE_US_SES_PK" tableName="OFFLINE_USER_SESSION" />
<dropPrimaryKey constraintName="CONSTRAINT_OFFLINE_CL_SES_PK" tableName="OFFLINE_CLIENT_SESSION" />
<addColumn tableName="OFFLINE_USER_SESSION">
<column name="OFFLINE_FLAG" type="VARCHAR(4)">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="OFFLINE_USER_SESSION">
<column name="OFFLINE_FLAG" value="1"/>
</update>
<dropColumn tableName="OFFLINE_USER_SESSION" columnName="OFFLINE" />
<addColumn tableName="OFFLINE_CLIENT_SESSION">
<column name="OFFLINE_FLAG" type="VARCHAR(4)">
<constraints nullable="false"/>
</column>
</addColumn>
<update tableName="OFFLINE_CLIENT_SESSION">
<column name="OFFLINE_FLAG" value="1"/>
</update>
<dropColumn tableName="OFFLINE_CLIENT_SESSION" columnName="OFFLINE" />
<addPrimaryKey columnNames="USER_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_US_SES_PK2" tableName="OFFLINE_USER_SESSION"/>
<addPrimaryKey columnNames="CLIENT_SESSION_ID, OFFLINE_FLAG" constraintName="CONSTRAINT_OFFL_CL_SES_PK2" tableName="OFFLINE_CLIENT_SESSION"/>
</changeSet>
<changeSet author="mposolda@redhat.com" id="1.6.1">
</changeSet>
</databaseChangeLog>

View file

@ -9,5 +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"/>
<include file="META-INF/jpa-changelog-1.6.1.xml"/>
</databaseChangeLog>

View file

@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
public String FIRST_VERSION = "1.0.0.Final";
public String LAST_VERSION = "1.6.0";
public String LAST_VERSION = "1.6.1";
public String getCurrentVersionSql(String defaultSchema);

View file

@ -4,7 +4,7 @@
<parent>
<artifactId>keycloak-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.6.0.Final-SNAPSHOT</version>
<version></version>
<relativePath>../../../pom.xml</relativePath>
</parent>

View file

@ -210,6 +210,7 @@ new Keycloak({ url: 'http://localhost/auth', realm: 'myrealm', clientId: 'myApp'
<listitem>prompt - can be set to 'none' to check if the user is logged in already (if not logged in, a login form is not displayed)</listitem>
<listitem>loginHint - used to pre-fill the username/email field on the login form</listitem>
<listitem>action - if value is 'register' then user is redirected to registration page, otherwise to login page</listitem>
<listitem>locale - specifies the desired locale for the UI</listitem>
</itemizedlist>
</para>
</simplesect>

View file

@ -14,14 +14,18 @@
<packaging>pom</packaging>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-deploy-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<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-war-plugin</artifactId>

View file

@ -2,9 +2,13 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.examples</groupId>
<parent>
<artifactId>keycloak-examples-saml-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.7.0.Final-SNAPSHOT</version>
</parent>
<artifactId>saml-post-encryption</artifactId>
<version>1.6.0.Final-SNAPSHOT</version>
<packaging>war</packaging>

View file

@ -2,9 +2,13 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.examples</groupId>
<parent>
<artifactId>keycloak-examples-saml-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.7.0.Final-SNAPSHOT</version>
</parent>
<artifactId>saml-post-signatures</artifactId>
<version>1.6.0.Final-SNAPSHOT</version>
<packaging>war</packaging>

View file

@ -2,9 +2,13 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.keycloak.examples</groupId>
<parent>
<artifactId>keycloak-examples-saml-parent</artifactId>
<groupId>org.keycloak</groupId>
<version>1.7.0.Final-SNAPSHOT</version>
</parent>
<artifactId>saml-redirect-signatures</artifactId>
<version>1.6.0.Final-SNAPSHOT</version>
<packaging>war</packaging>

View file

@ -11,7 +11,7 @@
<label for="username" class="${properties.kcLabelClass!}">${msg("usernameOrEmail")}</label>
</div>
<div class="${properties.kcInputWrapperClass!}">
<input type="text" id="username" name="username" class="${properties.kcInputClass!}" />
<input type="text" id="username" name="username" class="${properties.kcInputClass!}" autofocus/>
</div>
</div>

View file

@ -12,7 +12,7 @@
</div>
<div class="${properties.kcInputWrapperClass!}">
<input id="totp" name="totp" type="text" class="${properties.kcInputClass!}" />
<input id="totp" name="totp" type="text" class="${properties.kcInputClass!}" autofocus />
</div>
</div>

View file

@ -52,6 +52,8 @@ public interface LoginFormsProvider extends Provider {
public LoginFormsProvider setClientSessionCode(String accessCode);
public LoginFormsProvider setClientSession(ClientSessionModel clientSession);
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String,RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappers);
public LoginFormsProvider setAccessRequest(String message);

View file

@ -47,6 +47,7 @@ import org.keycloak.login.freemarker.model.TotpBean;
import org.keycloak.login.freemarker.model.UrlBean;
import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
@ -138,7 +139,8 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
case VERIFY_EMAIL:
try {
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
builder.queryParam("key", accessCode);
builder.queryParam(OAuth2Constants.CODE, accessCode);
builder.queryParam("key", clientSession.getNote(Constants.VERIFY_EMAIL_KEY));
String link = builder.build(realm.getName()).toString();
long expiration = TimeUnit.SECONDS.toMinutes(realm.getAccessCodeLifespanUserAction());
@ -531,6 +533,12 @@ public class FreeMarkerLoginFormsProvider implements LoginFormsProvider {
return this;
}
@Override
public LoginFormsProvider setClientSession(ClientSessionModel clientSession) {
this.clientSession = clientSession;
return this;
}
@Override
public LoginFormsProvider setAccessRequest(List<RoleModel> realmRolesRequested, MultivaluedMap<String, RoleModel> resourceRolesRequested, List<ProtocolMapperModel> protocolMappersRequested) {
this.realmRolesRequested = realmRolesRequested;

View file

@ -4,6 +4,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.services.util.ResolveRelative;
import java.net.URI;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -32,4 +33,11 @@ public class ClientBean {
return ResolveRelative.resolveRelativeUri(requestUri, client.getRootUrl(), client.getBaseUrl());
}
public Map<String,String> getAttributes(){
return client.getAttributes();
}
public String getAttribute(String key){
return client.getAttribute(key);
}
}

View file

@ -168,6 +168,10 @@
url += '&scope=' + options.scope;
}
if (options && options.locale) {
url += '&ui_locales=' + options.locale;
}
return url;
}

View file

@ -22,4 +22,6 @@ public interface Constants {
// 30 days
int DEFAULT_OFFLINE_SESSION_IDLE_TIMEOUT = 2592000;
public static final String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
}

View file

@ -0,0 +1,11 @@
package org.keycloak.models.utils;
import org.keycloak.provider.ProviderEvent;
/**
* Executed at startup after model migration is finished
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class PostMigrationEvent implements ProviderEvent {
}

View file

@ -188,32 +188,32 @@ public class JpaUserSessionPersisterProvider implements UserSessionPersisterProv
userSessionIds.add(entity.getUserSessionId());
}
TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class);
query2.setParameter("userSessionIds", userSessionIds);
query2.setParameter("offline", offlineStr);
List<PersistentClientSessionEntity> clientSessions = query2.getResultList();
if (!userSessionIds.isEmpty()) {
TypedQuery<PersistentClientSessionEntity> query2 = em.createNamedQuery("findClientSessionsByUserSessions", PersistentClientSessionEntity.class);
query2.setParameter("userSessionIds", userSessionIds);
query2.setParameter("offline", offlineStr);
List<PersistentClientSessionEntity> clientSessions = query2.getResultList();
// Assume both userSessions and clientSessions ordered by userSessionId
int j=0;
for (UserSessionModel ss : result) {
PersistentUserSessionAdapter userSession = (PersistentUserSessionAdapter) ss;
List<ClientSessionModel> currentClientSessions = userSession.getClientSessions(); // This is empty now and we want to fill it
// Assume both userSessions and clientSessions ordered by userSessionId
int j = 0;
for (UserSessionModel ss : result) {
PersistentUserSessionAdapter userSession = (PersistentUserSessionAdapter) ss;
List<ClientSessionModel> currentClientSessions = userSession.getClientSessions(); // This is empty now and we want to fill it
boolean next = true;
while (next && j<clientSessions.size()) {
PersistentClientSessionEntity clientSession = clientSessions.get(j);
if (clientSession.getUserSessionId().equals(userSession.getId())) {
PersistentClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
currentClientSessions.add(clientSessAdapter);
j++;
} else {
next = false;
boolean next = true;
while (next && j < clientSessions.size()) {
PersistentClientSessionEntity clientSession = clientSessions.get(j);
if (clientSession.getUserSessionId().equals(userSession.getId())) {
PersistentClientSessionAdapter clientSessAdapter = toAdapter(userSession.getRealm(), userSession, clientSession);
currentClientSessions.add(clientSessAdapter);
j++;
} else {
next = false;
}
}
}
}
return result;
}

View file

@ -5,9 +5,11 @@ import org.infinispan.Version;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.connections.infinispan.InfinispanConnectionProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakSessionTask;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.UserSessionProviderFactory;
import org.keycloak.models.session.UserSessionPersisterProvider;
@ -19,6 +21,9 @@ import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import org.keycloak.models.sessions.infinispan.initializer.InfinispanUserSessionInitializer;
import org.keycloak.models.sessions.infinispan.initializer.OfflineUserSessionLoader;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.provider.ProviderEvent;
import org.keycloak.provider.ProviderEventListener;
/**
* Uses Infinispan to store user sessions. On EAP 6.4 (Infinispan 5.2) map reduce is not supported for local caches as a work around
@ -68,13 +73,20 @@ public class InfinispanUserSessionProviderFactory implements UserSessionProvider
});
// Max count of worker errors. Initialization will end with exception when this number is reached
int maxErrors = config.getInt("maxErrors", 20);
final int maxErrors = config.getInt("maxErrors", 20);
// Count of sessions to be computed in each segment
int sessionsPerSegment = config.getInt("sessionsPerSegment", 100);
final int sessionsPerSegment = config.getInt("sessionsPerSegment", 100);
// TODO: Possibility to run this asynchronously to not block start time
loadPersistentSessions(factory, maxErrors, sessionsPerSegment);
factory.register(new ProviderEventListener() {
@Override
public void onEvent(ProviderEvent event) {
if (event instanceof PostMigrationEvent) {
loadPersistentSessions(factory, maxErrors, sessionsPerSegment);
}
}
});
}

View file

@ -39,7 +39,7 @@ public class SimpleUserSessionInitializer {
public void run(KeycloakSession session) {
int count = sessionLoader.getSessionsCount(session);
for (int i=0 ; i<=count ; i+=sessionsPerSegment) {
for (int i=0 ; i<count ; i+=sessionsPerSegment) {
sessionLoader.loadSessions(session, i, sessionsPerSegment);
}
}

View file

@ -8,9 +8,12 @@ import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.events.Details;
import org.keycloak.events.EventType;
import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.HmacOTP;
import org.keycloak.services.resources.LoginActionsService;
import org.keycloak.services.validation.Validation;
@ -44,8 +47,11 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
context.getEvent().clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, context.getUser().getEmail()).success();
LoginActionsService.createActionCookie(context.getRealm(), context.getUriInfo(), context.getConnection(), context.getUserSession().getId());
setupKey(context.getClientSession());
LoginFormsProvider loginFormsProvider = context.getSession().getProvider(LoginFormsProvider.class)
.setClientSessionCode(context.generateCode())
.setClientSession(context.getClientSession())
.setUser(context.getUser());
Response challenge = loginFormsProvider.createResponse(UserModel.RequiredAction.VERIFY_EMAIL);
context.challenge(challenge);
@ -87,4 +93,9 @@ public class VerifyEmail implements RequiredActionProvider, RequiredActionFactor
public String getId() {
return UserModel.RequiredAction.VERIFY_EMAIL.name();
}
public static void setupKey(ClientSessionModel clientSession) {
String secret = HmacOTP.generateSecret(10);
clientSession.setNote(Constants.VERIFY_EMAIL_KEY, secret);
}
}

View file

@ -11,6 +11,7 @@ import org.keycloak.migration.MigrationModelManager;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.PostMigrationEvent;
import org.keycloak.offlineconfig.AdminRecovery;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.DefaultKeycloakSessionFactory;
@ -83,6 +84,8 @@ public class KeycloakApplication extends Application {
setupDefaultRealm(context.getContextPath());
migrateModel();
sessionFactory.publish(new PostMigrationEvent());
new ExportImportManager().checkExportImport(this.sessionFactory, context.getContextPath());
importRealms(context);

View file

@ -23,6 +23,8 @@ package org.keycloak.services.resources;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.authentication.AuthenticationFlowError;
import org.keycloak.authentication.requiredactions.VerifyEmail;
import org.keycloak.common.ClientConnection;
import org.keycloak.OAuth2Constants;
import org.keycloak.authentication.AuthenticationProcessor;
@ -49,6 +51,7 @@ import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.RequiredAction;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.models.utils.HmacOTP;
import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.LoginProtocol;
import org.keycloak.protocol.RestartLoginCookie;
@ -533,7 +536,7 @@ public class LoginActionsService {
event.event(EventType.VERIFY_EMAIL);
if (key != null) {
Checks checks = new Checks();
if (!checks.verifyCode(key, ClientSessionModel.Action.REQUIRED_ACTIONS.name())) {
if (!checks.verifyCode(code, ClientSessionModel.Action.REQUIRED_ACTIONS.name())) {
return checks.response;
}
ClientSessionCode accessCode = checks.clientCode;
@ -547,11 +550,21 @@ public class LoginActionsService {
UserSessionModel userSession = clientSession.getUserSession();
UserModel user = userSession.getUser();
initEvent(clientSession);
event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail());
String keyFromSession = clientSession.getNote(Constants.VERIFY_EMAIL_KEY);
clientSession.removeNote(Constants.VERIFY_EMAIL_KEY);
if (!key.equals(keyFromSession)) {
logger.error("Invalid key for email verification");
event.error(Errors.INVALID_USER_CREDENTIALS);
throw new WebApplicationException(ErrorPage.error(session, Messages.INVALID_CODE));
}
user.setEmailVerified(true);
user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
event.event(EventType.VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
event.success();
String actionCookieValue = getActionCookie();
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
@ -576,8 +589,11 @@ public class LoginActionsService {
createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
VerifyEmail.setupKey(clientSession);
return session.getProvider(LoginFormsProvider.class)
.setClientSessionCode(accessCode.getCode())
.setClientSession(clientSession)
.setUser(userSession.getUser())
.createResponse(RequiredAction.VERIFY_EMAIL);
}

View file

@ -26,6 +26,12 @@
<module>eap6</module>
</modules>
</profile>
<profile>
<id>migration-kc16</id>
<modules>
<module>wildfly_kc16</module>
</modules>
</profile>
<profile>
<id>migration-kc15</id>
<modules>

View file

@ -0,0 +1,29 @@
<assembly>
<id>auth-server-wildfly-kc16</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>${keycloak.server.home}</directory>
<outputDirectory>keycloak-1.6.0.Final</outputDirectory>
<excludes>
<exclude>**/*.sh</exclude>
</excludes>
</fileSet>
<fileSet>
<directory>${keycloak.server.home}</directory>
<outputDirectory>keycloak-1.6.0.Final</outputDirectory>
<includes>
<include>**/*.sh</include>
</includes>
<fileMode>0755</fileMode>
</fileSet>
</fileSets>
</assembly>

View file

@ -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.7.0.Final-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>integration-arquillian-server-wildfly-kc16</artifactId>
<packaging>pom</packaging>
<name>Keycloak 1.6.0.Final on Wildfly</name>
<properties>
<keycloak.server.home>${project.build.directory}/unpacked/keycloak-1.6.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.6.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>

View file

@ -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>

View file

@ -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>

View file

@ -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>

View file

@ -21,6 +21,17 @@
<!-- PREVIOUS VERSIONS KEYCLOAK FOR MIGRATION TESTS -->
<!-- IT HAS TO BE LISTED ABOWE KEYCLOAK AUTH SERVERS -->
<container qualifier="keycloak-1.6.0.Final" mode="suite" >
<configuration>
<property name="enabled">${migration.kc16}</property>
<property name="adapterImplClass">org.jboss.as.arquillian.container.managed.ManagedDeployableContainer</property>
<property name="jbossHome">${keycloak-1.6.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.5.1.Final" mode="suite" >
<configuration>
<property name="enabled">${migration.kc15}</property>

View file

@ -451,6 +451,57 @@
<!-- Profiles for migration tests-->
<profile>
<id>migration-kc16</id>
<properties>
<keycloak-1.6.0.Final.home>${containers.home}/keycloak-1.6.0.Final</keycloak-1.6.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-kc16</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.kc16>true</migration.kc16>
<keycloak-1.6.0.Final.home>${keycloak-1.6.0.Final.home}</keycloak-1.6.0.Final.home>
<keycloak.migration.file>src/test/resources/migration-test/migration-realm-16.json</keycloak.migration.file>
</systemPropertyVariables>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
</profile>
<profile>
<id>migration-kc15</id>
<properties>

View file

@ -26,7 +26,9 @@ import org.junit.Before;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.common.util.KeycloakUriBuilder;
import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.Event;
import org.keycloak.events.EventType;
import org.keycloak.models.RealmModel;
@ -130,7 +132,7 @@ public class RequiredActionEmailVerificationTest {
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1].split("\\.")[1]);
Assert.assertEquals(mailCodeId, verificationUrl.split("code=")[1].split("\\&")[0].split("\\.")[1]);
driver.navigate().to(verificationUrl.trim());
@ -223,7 +225,7 @@ public class RequiredActionEmailVerificationTest {
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
Assert.assertEquals(mailCodeId, verificationUrl.split("key=")[1].split("\\.")[1]);
Assert.assertEquals(mailCodeId, verificationUrl.split("code=")[1].split("\\&")[0].split("\\.")[1]);
driver.manage().deleteAllCookies();
@ -238,6 +240,42 @@ public class RequiredActionEmailVerificationTest {
assertTrue(loginPage.isCurrent());
}
@Test
public void verifyInvalidKeyOrCode() throws IOException, MessagingException {
loginPage.open();
loginPage.login("test-user@localhost", "password");
Assert.assertTrue(verifyEmailPage.isCurrent());
String resendEmailLink = verifyEmailPage.getResendEmailLink();
String keyInsteadCodeURL = resendEmailLink.replace("code=", "key=");
AssertEvents.ExpectedEvent emailEvent = events.expectRequiredAction(EventType.SEND_VERIFY_EMAIL).detail("email", "test-user@localhost");
Event sendEvent = emailEvent.assertEvent();
String sessionId = sendEvent.getSessionId();
String mailCodeId = sendEvent.getDetails().get(Details.CODE_ID);
driver.navigate().to(keyInsteadCodeURL);
events.expectRequiredAction(EventType.VERIFY_EMAIL_ERROR)
.error(Errors.INVALID_CODE)
.client((String)null)
.user((String)null)
.session((String)null)
.clearDetails()
.assertEvent();
String badKeyURL = KeycloakUriBuilder.fromUri(resendEmailLink).queryParam("key", "foo").build().toString();
driver.navigate().to(badKeyURL);
events.expectRequiredAction(EventType.VERIFY_EMAIL_ERROR)
.error(Errors.INVALID_USER_CREDENTIALS)
.session(sessionId)
.detail("email", "test-user@localhost")
.detail(Details.CODE_ID, mailCodeId)
.assertEvent();
}
private String getPasswordResetEmailLink(MimeMessage message) throws IOException, MessagingException {
Multipart multipart = (Multipart) message.getContent();

View file

@ -290,6 +290,14 @@ public class UserSessionPersisterProviderTest {
realmMgr.removeRealm(realmMgr.getRealm("foo"));
}
// KEYCLOAK-1999
@Test
public void testNoSessions() {
UserSessionPersisterProvider persister = session.getProvider(UserSessionPersisterProvider.class);
List<UserSessionModel> sessions = persister.loadUserSessions(0, 1, true);
Assert.assertEquals(0, sessions.size());
}
private ClientSessionModel createClientSession(ClientModel client, UserSessionModel userSession, String redirect, String state, Set<String> roles, Set<String> protocolMappers) {
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);

View file

@ -50,4 +50,8 @@ public class VerifyEmailPage extends AbstractPage {
resendEmailLink.click();
}
public String getResendEmailLink() {
return resendEmailLink.getAttribute("href");
}
}