clientsession.action to String

This commit is contained in:
Bill Burke 2015-06-10 09:21:23 -04:00
parent dcc40b0a63
commit 95349e6e2e
44 changed files with 989 additions and 561 deletions

View file

@ -7,6 +7,13 @@
<delete tableName="CLIENT_SESSION"/> <delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/> <delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/> <delete tableName="USER_SESSION"/>
<createTable tableName="DEFAULT_REQUIRED_ACTIONS">
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="ADMIN_EVENT_ENTITY"> <createTable tableName="ADMIN_EVENT_ENTITY">
<column name="ID" type="VARCHAR(36)"> <column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
@ -145,6 +152,63 @@
<column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/> <column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
<where>ACTION = 3</where> <where>ACTION = 3</where>
</update> </update>
<addColumn tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
</addColumn>
<!-- OAUTH_GRANT,
CODE_TO_TOKEN,
VERIFY_EMAIL,
UPDATE_PROFILE,
CONFIGURE_TOTP,
UPDATE_PASSWORD,
RECOVER_PASSWORD,
AUTHENTICATE,
SOCIAL_CALLBACK,
LOGGED_OUT -->
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="OAUTH_GRANT"/>
<where>ACTION = 0</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="CODE_TO_TOKEN"/>
<where>ACTION = 1</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="VERIFY_EMAIL"/>
<where>ACTION = 2</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="UPDATE_PROFILE"/>
<where>ACTION = 3</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="CONFIGURE_TOTP"/>
<where>ACTION = 4</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="UPDATE_PASSWORD"/>
<where>ACTION = 5</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="RECOVER_PASSWORD"/>
<where>ACTION = 6</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="AUTHENTICATE"/>
<where>ACTION = 7</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="SOCIAL_CALLBACK"/>
<where>ACTION = 8</where>
</update>
<update tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" value="LOGGED_OUT"/>
<where>ACTION = 9</where>
</update>
<createTable tableName="CLIENT_USER_SESSION_NOTE"> <createTable tableName="CLIENT_USER_SESSION_NOTE">
<column name="NAME" type="VARCHAR(255)"> <column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/> <constraints nullable="false"/>
@ -162,10 +226,12 @@
<addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTHENTICATOR_CONFIG_PK" tableName="AUTHENTICATOR_CONFIG"/> <addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTHENTICATOR_CONFIG_PK" tableName="AUTHENTICATOR_CONFIG"/>
<dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/> <dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
<dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/> <dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/>
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
<addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/> <addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/>
<addPrimaryKey columnNames="CLIENT_SESSION, AUTHENTICATOR" constraintName="CONSTRAINT_AUTH_STATUS_PK" tableName="CLIENT_SESSION_AUTH_STATUS"/> <addPrimaryKey columnNames="CLIENT_SESSION, AUTHENTICATOR" constraintName="CONSTRAINT_AUTH_STATUS_PK" tableName="CLIENT_SESSION_AUTH_STATUS"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/> <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/>
<addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/> <addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="DEFAULT_REQUIRED_ACTIONS" constraintName="FK_DEFAULT_REQUIRED_ACTIONS_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/> <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTHENTICATOR_REALM" referencedColumnNames="ID" referencedTableName="REALM"/> <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTHENTICATOR_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTHENTICATION_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/> <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTHENTICATION_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>

View file

@ -22,9 +22,9 @@ public interface ClientSessionModel {
public void setTimestamp(int timestamp); public void setTimestamp(int timestamp);
public Action getAction(); public String getAction();
public void setAction(Action action); public void setAction(String action);
public Set<String> getRoles(); public Set<String> getRoles();
public void setRoles(Set<String> roles); public void setRoles(Set<String> roles);

View file

@ -156,6 +156,13 @@ public interface RealmModel extends RoleContainerModel {
void updateDefaultRoles(String[] defaultRoles); void updateDefaultRoles(String[] defaultRoles);
Set<String> getDefaultRequiredActions();
void addDefaultRequiredAction(String action);
void removeDefaultRequiredAction(String action);
void setDefaultRequiredActions(Set<String> action);
// Key is clientId // Key is clientId
Map<String, ClientModel> getClientNameMap(); Map<String, ClientModel> getClientNameMap();

View file

@ -2,8 +2,10 @@ package org.keycloak.models.entities;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -76,6 +78,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>(); private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>(); private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
private List<AuthenticatorEntity> authenticators = new ArrayList<>(); private List<AuthenticatorEntity> authenticators = new ArrayList<>();
private List<String> defaultRequiredActions = new ArrayList<>();
public String getName() { public String getName() {
@ -500,6 +503,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
public void setAuthenticators(List<AuthenticatorEntity> authenticators) { public void setAuthenticators(List<AuthenticatorEntity> authenticators) {
this.authenticators = authenticators; this.authenticators = authenticators;
} }
public List<String> getDefaultRequiredActions() {
return defaultRequiredActions;
}
public void setDefaultRequiredActions(List<String> defaultRequiredActions) {
this.defaultRequiredActions = defaultRequiredActions;
}
} }

View file

@ -104,5 +104,7 @@ public class DefaultAuthenticationFlows {
execution.setAutheticatorFlow(false); execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
//
} }
} }

View file

@ -1548,4 +1548,37 @@ public class RealmAdapter implements RealmModel {
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }
@Override
public Set<String> getDefaultRequiredActions() {
Set<String> result = new HashSet<String>();
if (realm.getDefaultRequiredActions() != null) {
result.addAll(realm.getDefaultRequiredActions());
}
return result;
}
@Override
public void addDefaultRequiredAction(String action) {
Set<String> actions = getDefaultRequiredActions();
actions.add(action);
setDefaultRequiredActions(actions);
}
@Override
public void removeDefaultRequiredAction(String action) {
Set<String> actions = getDefaultRequiredActions();
actions.remove(action);
setDefaultRequiredActions(actions);
}
@Override
public void setDefaultRequiredActions(Set<String> action) {
List<String> result = new ArrayList<String>();
result.addAll(action);
realm.setDefaultRequiredActions(result);
}
} }

View file

@ -1116,4 +1116,30 @@ public class RealmAdapter implements RealmModel {
if (updated != null) return updated.getAuthenticatorById(id); if (updated != null) return updated.getAuthenticatorById(id);
return cached.getAuthenticators().get(id); return cached.getAuthenticators().get(id);
} }
@Override
public Set<String> getDefaultRequiredActions() {
return cached.getDefaultRequiredActions();
}
@Override
public void addDefaultRequiredAction(String action) {
getDelegateForUpdate();
updated.addDefaultRequiredAction(action);
}
@Override
public void removeDefaultRequiredAction(String action) {
getDelegateForUpdate();
updated.removeDefaultRequiredAction(action);
}
@Override
public void setDefaultRequiredActions(Set<String> action) {
getDelegateForUpdate();
updated.setDefaultRequiredActions(action);
}
} }

View file

@ -98,6 +98,7 @@ public class CachedRealm {
private Set<String> supportedLocales = new HashSet<String>(); private Set<String> supportedLocales = new HashSet<String>();
private String defaultLocale; private String defaultLocale;
private MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>(); private MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>();
private Set<String> defaultRequiredActions = new HashSet<>();
public CachedRealm() { public CachedRealm() {
} }
@ -200,6 +201,7 @@ public class CachedRealm {
for (AuthenticatorModel authenticator : model.getAuthenticators()) { for (AuthenticatorModel authenticator : model.getAuthenticators()) {
authenticators.put(authenticator.getId(), authenticator); authenticators.put(authenticator.getId(), authenticator);
} }
this.defaultRequiredActions.addAll(model.getDefaultRequiredActions());
} }
@ -438,4 +440,8 @@ public class CachedRealm {
public Map<String, AuthenticationExecutionModel> getExecutionsById() { public Map<String, AuthenticationExecutionModel> getExecutionsById() {
return executionsById; return executionsById;
} }
public Set<String> getDefaultRequiredActions() {
return defaultRequiredActions;
}
} }

View file

@ -1707,5 +1707,30 @@ public class RealmAdapter implements RealmModel {
return authenticators; return authenticators;
} }
@Override
public Set<String> getDefaultRequiredActions() {
Set<String> result = new HashSet<String>();
result.addAll(realm.getDefaultRequiredActions());
return result;
}
@Override
public void setDefaultRequiredActions(Set<String> actions) {
realm.setDefaultRequiredActions(actions);
}
@Override
public void addDefaultRequiredAction(String action) {
realm.getDefaultRequiredActions().add(action);
}
@Override
public void removeDefaultRequiredAction(String action) {
realm.getDefaultRequiredActions().remove(action);
}
} }

View file

@ -113,6 +113,12 @@ public class RealmEntity {
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>(); Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@ElementCollection
@Column(name="VALUE")
@CollectionTable(name = "DEFAULT_REQUIRED_ACTIONS", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Set<String> defaultRequiredActions = new HashSet<String>();
@ElementCollection @ElementCollection
@MapKeyColumn(name="NAME") @MapKeyColumn(name="NAME")
@Column(name="VALUE") @Column(name="VALUE")
@ -568,5 +574,13 @@ public class RealmEntity {
public void setAuthenticationFlows(Collection<AuthenticationFlowEntity> authenticationFlows) { public void setAuthenticationFlows(Collection<AuthenticationFlowEntity> authenticationFlows) {
this.authenticationFlows = authenticationFlows; this.authenticationFlows = authenticationFlows;
} }
public Set<String> getDefaultRequiredActions() {
return defaultRequiredActions;
}
public void setDefaultRequiredActions(Set<String> defaultRequiredActions) {
this.defaultRequiredActions = defaultRequiredActions;
}
} }

View file

@ -1590,4 +1590,32 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }
@Override
public Set<String> getDefaultRequiredActions() {
Set<String> result = new HashSet<String>();
result.addAll(realm.getDefaultRequiredActions());
return result;
}
@Override
public void setDefaultRequiredActions(Set<String> actions) {
List<String> result = new ArrayList<String>();
result.addAll(actions);
getMongoEntity().setDefaultRequiredActions(result);
updateMongoEntity();
}
@Override
public void addDefaultRequiredAction(String action) {
getMongoStore().pushItemToList(getMongoEntity(), "defaultRequiredActions", action, true, invocationContext);
}
@Override
public void removeDefaultRequiredAction(String action) {
getMongoStore().pullItemFromList(getMongoEntity(), "defaultRequiredActions", action, invocationContext);
}
} }

View file

@ -100,12 +100,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
} }
@Override @Override
public Action getAction() { public String getAction() {
return entity.getAction(); return entity.getAction();
} }
@Override @Override
public void setAction(Action action) { public void setAction(String action) {
entity.setAction(action); entity.setAction(action);
update(); update();
} }

View file

@ -24,7 +24,7 @@ public class ClientSessionEntity extends SessionEntity {
private int timestamp; private int timestamp;
private ClientSessionModel.Action action; private String action;
private Set<String> roles; private Set<String> roles;
private Set<String> protocolMappers; private Set<String> protocolMappers;
@ -81,11 +81,11 @@ public class ClientSessionEntity extends SessionEntity {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public ClientSessionModel.Action getAction() { public String getAction() {
return action; return action;
} }
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
this.action = action; this.action = action;
} }

View file

@ -189,12 +189,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
} }
@Override @Override
public Action getAction() { public String getAction() {
return entity.getAction(); return entity.getAction();
} }
@Override @Override
public void setAction(Action action) { public void setAction(String action) {
entity.setAction(action); entity.setAction(action);
} }

View file

@ -54,8 +54,8 @@ public class ClientSessionEntity {
@Column(name="AUTH_METHOD") @Column(name="AUTH_METHOD")
protected String authMethod; protected String authMethod;
@Column(name="ACTION") @Column(name="CURRENT_ACTION")
protected ClientSessionModel.Action action; protected String action;
@Column(name="AUTH_USER_ID") @Column(name="AUTH_USER_ID")
protected String userId; protected String userId;
@ -123,11 +123,11 @@ public class ClientSessionEntity {
this.redirectUri = redirectUri; this.redirectUri = redirectUri;
} }
public ClientSessionModel.Action getAction() { public String getAction() {
return action; return action;
} }
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
this.action = action; this.action = action;
} }

View file

@ -93,12 +93,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
} }
@Override @Override
public ClientSessionModel.Action getAction() { public String getAction() {
return entity.getAction(); return entity.getAction();
} }
@Override @Override
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
entity.setAction(action); entity.setAction(action);
} }

View file

@ -24,7 +24,7 @@ public class ClientSessionEntity {
private String authMethod; private String authMethod;
private int timestamp; private int timestamp;
private ClientSessionModel.Action action; private String action;
private Set<String> roles; private Set<String> roles;
private Set<String> protocolMappers; private Set<String> protocolMappers;
private Map<String, String> notes = new HashMap<>(); private Map<String, String> notes = new HashMap<>();
@ -78,11 +78,11 @@ public class ClientSessionEntity {
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public ClientSessionModel.Action getAction() { public String getAction() {
return action; return action;
} }
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
this.action = action; this.action = action;
} }

View file

@ -108,12 +108,12 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
} }
@Override @Override
public Action getAction() { public String getAction() {
return entity.getAction(); return entity.getAction();
} }
@Override @Override
public void setAction(Action action) { public void setAction(String action) {
entity.setAction(action); entity.setAction(action);
updateMongoEntity(); updateMongoEntity();
} }

View file

@ -26,7 +26,7 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
private String authMethod; private String authMethod;
private int timestamp; private int timestamp;
private ClientSessionModel.Action action; private String action;
private List<String> roles; private List<String> roles;
private List<String> protocolMappers; private List<String> protocolMappers;
private Map<String, String> notes = new HashMap<String, String>(); private Map<String, String> notes = new HashMap<String, String>();
@ -82,11 +82,11 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
this.timestamp = timestamp; this.timestamp = timestamp;
} }
public ClientSessionModel.Action getAction() { public String getAction() {
return action; return action;
} }
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
this.action = action; this.action = action;
} }

View file

@ -21,6 +21,7 @@ import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
@ -32,6 +33,7 @@ import org.keycloak.saml.common.exceptions.ConfigurationException;
import org.keycloak.saml.common.exceptions.ProcessingException; import org.keycloak.saml.common.exceptions.ProcessingException;
import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder; import org.keycloak.saml.processing.core.saml.v2.common.SAMLDocumentHolder;
import org.keycloak.services.ErrorPage; import org.keycloak.services.ErrorPage;
import org.keycloak.services.Urls;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.managers.HttpAuthenticationManager; import org.keycloak.services.managers.HttpAuthenticationManager;
@ -59,6 +61,7 @@ import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.security.PublicKey; import java.security.PublicKey;
import java.util.List;
/** /**
* Resource class for the oauth/openid connect token service * Resource class for the oauth/openid connect token service
@ -264,7 +267,7 @@ public class SamlService {
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client); ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL); clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirect); clientSession.setRedirectUri(redirect);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret()); clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
clientSession.setNote(SamlProtocol.SAML_BINDING, bindingType); clientSession.setNote(SamlProtocol.SAML_BINDING, bindingType);
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState); clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
@ -317,7 +320,22 @@ public class SamlService {
return forms.createLogin(); return forms.createLogin();
} }
private Response buildRedirectToIdentityProvider(String providerId, String accessCode) {
logger.debug("Automatically redirect to identity provider: " + providerId);
return Response.temporaryRedirect(
Urls.identityProviderAuthnRequest(uriInfo.getBaseUri(), providerId, realm.getName(), accessCode))
.build();
}
protected Response newBrowserAuthentication(ClientSessionModel clientSession) { protected Response newBrowserAuthentication(ClientSessionModel clientSession) {
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
for (IdentityProviderModel identityProvider : identityProviders) {
if (identityProvider.isAuthenticateByDefault()) {
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
}
}
String flowId = null; String flowId = null;
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) { for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
if (flow.getAlias().equals("browser")) { if (flow.getAlias().equals("browser")) {
@ -336,7 +354,11 @@ public class SamlService {
.setUriInfo(uriInfo) .setUriInfo(uriInfo)
.setRequest(request); .setRequest(request);
return processor.authenticate(); try {
return processor.authenticate();
} catch (Exception e) {
return processor.handleBrowserException(e);
}
} }
@ -394,7 +416,7 @@ public class SamlService {
// remove client from logout requests // remove client from logout requests
for (ClientSessionModel clientSession : userSession.getClientSessions()) { for (ClientSessionModel clientSession : userSession.getClientSessions()) {
if (clientSession.getClient().getId().equals(client.getId())) { if (clientSession.getClient().getId().equals(client.getId())) {
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
} }
} }
logger.debug("browser Logout"); logger.debug("browser Logout");
@ -405,7 +427,7 @@ public class SamlService {
if (clientSession == null) continue; if (clientSession == null) continue;
if (clientSession.getClient().getClientId().equals(client.getClientId())) { if (clientSession.getClient().getClientId().equals(client.getClientId())) {
// remove requesting client from logout // remove requesting client from logout
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
} }
UserSessionModel userSession = clientSession.getUserSession(); UserSessionModel userSession = clientSession.getUserSession();
try { try {

View file

@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Errors;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
@ -15,8 +16,10 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.protocol.oidc.TokenManager; import org.keycloak.protocol.oidc.TokenManager;
import org.keycloak.services.ErrorPage;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@ -39,6 +42,7 @@ public class AuthenticationProcessor {
protected EventBuilder event; protected EventBuilder event;
protected HttpRequest request; protected HttpRequest request;
protected String flowId; protected String flowId;
protected String action;
protected boolean userSessionCreated; protected boolean userSessionCreated;
@ -134,6 +138,11 @@ public class AuthenticationProcessor {
return this; return this;
} }
public AuthenticationProcessor setAction(String action) {
this.action = action;
return this;
}
private class Result implements AuthenticatorContext { private class Result implements AuthenticatorContext {
AuthenticatorModel model; AuthenticatorModel model;
AuthenticationExecutionModel execution; AuthenticationExecutionModel execution;
@ -168,6 +177,11 @@ public class AuthenticationProcessor {
this.model = model; this.model = model;
} }
@Override
public String getAction() {
return AuthenticationProcessor.this.action;
}
@Override @Override
public Authenticator getAuthenticator() { public Authenticator getAuthenticator() {
return authenticator; return authenticator;
@ -332,6 +346,34 @@ public class AuthenticationProcessor {
return status == UserSessionModel.AuthenticatorStatus.SUCCESS; return status == UserSessionModel.AuthenticatorStatus.SUCCESS;
} }
public Response handleBrowserException(Exception failure) {
if (failure instanceof AuthException) {
AuthException e = (AuthException)failure;
logger.error("failed authentication: " + e.getError().toString(), e);
if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
event.error(Errors.USER_NOT_FOUND);
return ErrorPage.error(session, Messages.INVALID_USER);
} else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
event.error(Errors.USER_DISABLED);
return ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
} else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
event.error(Errors.USER_TEMPORARILY_DISABLED);
return ErrorPage.error(session, Messages.ACCOUNT_TEMPORARILY_DISABLED);
} else {
event.error(Errors.INVALID_USER_CREDENTIALS);
return ErrorPage.error(session, Messages.INVALID_USER);
}
} else {
logger.error("failed authentication", failure);
event.error(Errors.INVALID_USER_CREDENTIALS);
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_REQUEST);
}
}
public Response authenticate() throws AuthException { public Response authenticate() throws AuthException {
logger.debug("AUTHENTICATE"); logger.debug("AUTHENTICATE");
event.event(EventType.LOGIN); event.event(EventType.LOGIN);

View file

@ -31,6 +31,8 @@ public interface AuthenticatorContext {
void setAuthenticatorModel(AuthenticatorModel model); void setAuthenticatorModel(AuthenticatorModel model);
String getAction();
Authenticator getAuthenticator(); Authenticator getAuthenticator();
void setAuthenticator(Authenticator authenticator); void setAuthenticator(Authenticator authenticator);

View file

@ -0,0 +1,32 @@
package org.keycloak.authentication;
import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection;
import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.services.managers.BruteForceProtector;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RequiredActionContext {
EventBuilder getEvent();
UserModel getUser();
RealmModel getRealm();
ClientSessionModel getClientSession();
UserSessionModel getUserSession();
ClientConnection getConnection();
UriInfo getUriInfo();
KeycloakSession getSession();
HttpRequest getHttpRequest();
}

View file

@ -0,0 +1,14 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import javax.ws.rs.core.Response;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface RequiredActionProvider extends Provider {
Response invokeRequiredAction(RequiredActionContext context);
Object jaxrsService();
}

View file

@ -19,26 +19,27 @@ import java.net.URI;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class AbstractFormAuthenticator { public class AbstractFormAuthenticator {
protected boolean isActionUrl(AuthenticatorContext context) {
URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
String current = context.getUriInfo().getAbsolutePath().getPath();
String expectedPath = expected.getPath();
return expectedPath.equals(current);
public static final String LOGIN_FORM_ACTION = "login_form";
public static final String ACTION = "action";
protected boolean isAction(AuthenticatorContext context, String action) {
return action.equals(context.getAction());
} }
protected LoginFormsProvider loginForm(AuthenticatorContext context) { protected LoginFormsProvider loginForm(AuthenticatorContext context) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession()); ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
code.setAction(ClientSessionModel.Action.AUTHENTICATE); code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
URI action = getActionUrl(context, code); URI action = getActionUrl(context, code, LOGIN_FORM_ACTION);
return context.getSession().getProvider(LoginFormsProvider.class) return context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action) .setActionUri(action)
.setClientSessionCode(code.getCode()); .setClientSessionCode(code.getCode());
} }
public static URI getActionUrl(AuthenticatorContext context, ClientSessionCode code) { public static URI getActionUrl(AuthenticatorContext context, ClientSessionCode code, String action) {
return LoginActionsService.authenticationFormProcessor(context.getUriInfo()) return LoginActionsService.authenticationFormProcessor(context.getUriInfo())
.queryParam(OAuth2Constants.CODE, code.getCode()) .queryParam(OAuth2Constants.CODE, code.getCode())
.queryParam(ACTION, action)
.build(context.getRealm().getName()); .build(context.getRealm().getName());
} }

View file

@ -27,7 +27,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
@Override @Override
public void authenticate(AuthenticatorContext context) { public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) { if (!isAction(context, LOGIN_FORM_ACTION)) {
context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR); context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
return; return;
} }

View file

@ -9,7 +9,6 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -28,7 +27,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
@Override @Override
public void authenticate(AuthenticatorContext context) { public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) { if (!isAction(context, LOGIN_FORM_ACTION)) {
context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR); context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
return; return;
} }

View file

@ -33,7 +33,7 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
@Override @Override
public void authenticate(AuthenticatorContext context) { public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) { if (!isAction(context, LOGIN_FORM_ACTION)) {
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>(); MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM); String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);

View file

@ -13,7 +13,6 @@ import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages; import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.LoginActionsService;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -26,6 +25,7 @@ import java.util.List;
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class OTPFormAuthenticator extends AbstractFormAuthenticator implements Authenticator { public class OTPFormAuthenticator extends AbstractFormAuthenticator implements Authenticator {
public static final String TOTP_FORM_ACTION = "totp";
protected AuthenticatorModel model; protected AuthenticatorModel model;
public OTPFormAuthenticator(AuthenticatorModel model) { public OTPFormAuthenticator(AuthenticatorModel model) {
@ -34,7 +34,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
@Override @Override
public void authenticate(AuthenticatorContext context) { public void authenticate(AuthenticatorContext context) {
if (!isActionUrl(context)) { if (!isAction(context, TOTP_FORM_ACTION)) {
Response challengeResponse = challenge(context, null); Response challengeResponse = challenge(context, null);
context.challenge(challengeResponse); context.challenge(challengeResponse);
return; return;
@ -43,7 +43,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
} }
public void validateOTP(AuthenticatorContext context) { public void validateOTP(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters(); MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>(); List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.TOTP); String password = inputData.getFirst(CredentialRepresentation.TOTP);
if (password == null) { if (password == null) {
@ -70,7 +70,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
protected Response challenge(AuthenticatorContext context, String error) { protected Response challenge(AuthenticatorContext context, String error) {
ClientSessionCode clientSessionCode = new ClientSessionCode(context.getRealm(), context.getClientSession()); ClientSessionCode clientSessionCode = new ClientSessionCode(context.getRealm(), context.getClientSession());
URI action = AbstractFormAuthenticator.getActionUrl(context, clientSessionCode); URI action = AbstractFormAuthenticator.getActionUrl(context, clientSessionCode, TOTP_FORM_ACTION);
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class) LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
.setActionUri(action) .setActionUri(action)
.setClientSessionCode(clientSessionCode.getCode()); .setClientSessionCode(clientSessionCode.getCode());

View file

@ -0,0 +1,48 @@
package org.keycloak.authentication.authenticators;
import org.keycloak.authentication.AuthenticationProcessor;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.AuthenticationManager;
/**
* No auth, but it sets a required action.
*
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SetRequiredActionAuthenticator implements Authenticator {
@Override
public boolean requiresUser() {
return false;
}
@Override
public void authenticate(AuthenticatorContext context) {
UserModel user = context.getUser();
if (user == null) {
throw new AuthenticationProcessor.AuthException(AuthenticationProcessor.Error.UNKNOWN_USER);
}
user.addRequiredAction(context.getAuthenticatorModel().getConfig().get("required.action"));
context.success();
}
@Override
public boolean configuredFor(KeycloakSession session, RealmModel realm, UserModel user) {
return true;
}
@Override
public String getRequiredAction() {
return null;
}
@Override
public void close() {
}
}

View file

@ -0,0 +1,69 @@
package org.keycloak.authentication.authenticators;
import org.keycloak.Config;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class SetRequiredActionAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-set-required-action";
static SetRequiredActionAuthenticator SINGLETON = new SetRequiredActionAuthenticator();
@Override
public Authenticator create(AuthenticatorModel model) {
return SINGLETON;
}
@Override
public Authenticator create(KeycloakSession session) {
throw new IllegalStateException("illegal call");
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return PROVIDER_ID;
}
@Override
public String getDisplayCategory() {
return "Action";
}
@Override
public String getDisplayType() {
return "Set Required Action";
}
@Override
public String getHelpText() {
return "Doesn't do any authentication. Instead it just sets a configured required action for the user.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
}

View file

@ -130,7 +130,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
ClientSessionModel clientSession = accessCode.getClientSession(); ClientSessionModel clientSession = accessCode.getClientSession();
String redirect = clientSession.getRedirectUri(); String redirect = clientSession.getRedirectUri();
String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM); String state = clientSession.getNote(OIDCLoginProtocol.STATE_PARAM);
accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN); accessCode.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, accessCode.getCode()); UriBuilder redirectUri = UriBuilder.fromUri(redirect).queryParam(OAuth2Constants.CODE, accessCode.getCode());
log.debugv("redirectAccessCode: state: {0}", state); log.debugv("redirectAccessCode: state: {0}", state);
if (state != null) if (state != null)

View file

@ -220,7 +220,7 @@ public class AuthorizationEndpoint {
clientSession = session.sessions().createClientSession(realm, client); clientSession = session.sessions().createClientSession(realm, client);
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL); clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
clientSession.setRedirectUri(redirectUri); clientSession.setRedirectUri(redirectUri);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret()); clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType); clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType);
clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam); clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam);
@ -277,7 +277,12 @@ public class AuthorizationEndpoint {
.setUriInfo(uriInfo) .setUriInfo(uriInfo)
.setRequest(request); .setRequest(request);
Response challenge = processor.authenticateOnly(); Response challenge = null;
try {
challenge = processor.authenticateOnly();
} catch (Exception e) {
return processor.handleBrowserException(e);
}
if (challenge != null && prompt != null && prompt.equals("none")) { if (challenge != null && prompt != null && prompt.equals("none")) {
if (processor.isUserSessionCreated()) { if (processor.isUserSessionCreated()) {

View file

@ -191,7 +191,7 @@ public class TokenEndpoint {
ClientSessionModel clientSession = accessCode.getClientSession(); ClientSessionModel clientSession = accessCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId()); event.detail(Details.CODE_ID, clientSession.getId());
if (!accessCode.isValid(ClientSessionModel.Action.CODE_TO_TOKEN)) { if (!accessCode.isValid(ClientSessionModel.Action.CODE_TO_TOKEN.name())) {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
throw new ErrorResponseException("invalid_grant", "Code is expired", Response.Status.BAD_REQUEST); throw new ErrorResponseException("invalid_grant", "Code is expired", Response.Status.BAD_REQUEST);
} }

View file

@ -148,7 +148,7 @@ public class AuthenticationManager {
public static void backchannelLogoutClientSession(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UserSessionModel userSession, UriInfo uriInfo, HttpHeaders headers) { public static void backchannelLogoutClientSession(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UserSessionModel userSession, UriInfo uriInfo, HttpHeaders headers) {
ClientModel client = clientSession.getClient(); ClientModel client = clientSession.getClient();
if (client instanceof ClientModel && !client.isFrontchannelLogout() && clientSession.getAction() != ClientSessionModel.Action.LOGGED_OUT) { if (client instanceof ClientModel && !client.isFrontchannelLogout() && !ClientSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) {
String authMethod = clientSession.getAuthMethod(); String authMethod = clientSession.getAuthMethod();
if (authMethod == null) return; // must be a keycloak service like account if (authMethod == null) return; // must be a keycloak service like account
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod); LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
@ -156,7 +156,7 @@ public class AuthenticationManager {
.setHttpHeaders(headers) .setHttpHeaders(headers)
.setUriInfo(uriInfo); .setUriInfo(uriInfo);
protocol.backchannelLogout(userSession, clientSession); protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
} }
} }
@ -188,7 +188,7 @@ public class AuthenticationManager {
List<ClientSessionModel> redirectClients = new LinkedList<ClientSessionModel>(); List<ClientSessionModel> redirectClients = new LinkedList<ClientSessionModel>();
for (ClientSessionModel clientSession : userSession.getClientSessions()) { for (ClientSessionModel clientSession : userSession.getClientSessions()) {
ClientModel client = clientSession.getClient(); ClientModel client = clientSession.getClient();
if (clientSession.getAction() == ClientSessionModel.Action.LOGGED_OUT) continue; if (ClientSessionModel.Action.LOGGED_OUT.name().equals(clientSession.getAction())) continue;
if (client.isFrontchannelLogout()) { if (client.isFrontchannelLogout()) {
String authMethod = clientSession.getAuthMethod(); String authMethod = clientSession.getAuthMethod();
if (authMethod == null) continue; // must be a keycloak service like account if (authMethod == null) continue; // must be a keycloak service like account
@ -205,7 +205,7 @@ public class AuthenticationManager {
try { try {
logger.debugv("backchannel logout to: {0}", client.getClientId()); logger.debugv("backchannel logout to: {0}", client.getClientId());
protocol.backchannelLogout(userSession, clientSession); protocol.backchannelLogout(userSession, clientSession);
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT); clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
} catch (Exception e) { } catch (Exception e) {
logger.warn("Failed to logout client, continuing", e); logger.warn("Failed to logout client, continuing", e);
} }
@ -219,7 +219,7 @@ public class AuthenticationManager {
.setHttpHeaders(headers) .setHttpHeaders(headers)
.setUriInfo(uriInfo); .setUriInfo(uriInfo);
// setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not // setting this to logged out cuz I"m not sure protocols can always verify that the client was logged out or not
nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT); nextRedirectClient.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
try { try {
logger.debugv("frontchannel logout to: {0}", nextRedirectClient.getClient().getClientId()); logger.debugv("frontchannel logout to: {0}", nextRedirectClient.getClient().getClientId());
Response response = protocol.frontchannelLogout(userSession, nextRedirectClient); Response response = protocol.frontchannelLogout(userSession, nextRedirectClient);
@ -476,7 +476,7 @@ public class AuthenticationManager {
} }
if (client.isConsentRequired()) { if (client.isConsentRequired()) {
accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT); accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT.name());
UserConsentModel grantedConsent = user.getConsentByClient(client.getId()); UserConsentModel grantedConsent = user.getConsentByClient(client.getId());

View file

@ -80,8 +80,8 @@ public class ClientSessionCode {
return clientSession; return clientSession;
} }
public boolean isValid(ClientSessionModel.Action requestedAction) { public boolean isValid(String requestedAction) {
ClientSessionModel.Action action = clientSession.getAction(); String action = clientSession.getAction();
if (action == null) { if (action == null) {
return false; return false;
} }
@ -93,18 +93,14 @@ public class ClientSessionCode {
} }
int lifespan; int lifespan;
switch (action) { if (action.equals(ClientSessionModel.Action.CODE_TO_TOKEN.name())) {
case CODE_TO_TOKEN: lifespan = realm.getAccessCodeLifespan();
lifespan = realm.getAccessCodeLifespan();
break;
case AUTHENTICATE:
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
break;
default:
lifespan = realm.getAccessCodeLifespanUserAction();
break;
}
} else if (action.equals(ClientSessionModel.Action.AUTHENTICATE.name())) {
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
} else {
lifespan = realm.getAccessCodeLifespanUserAction();
}
return timestamp + lifespan > Time.currentTime(); return timestamp + lifespan > Time.currentTime();
} }
@ -132,7 +128,7 @@ public class ClientSessionCode {
return requestedProtocolMappers; return requestedProtocolMappers;
} }
public void setAction(ClientSessionModel.Action action) { public void setAction(String action) {
clientSession.setAction(action); clientSession.setAction(action);
clientSession.setNote(ACTION_KEY, UUID.randomUUID().toString()); clientSession.setNote(ACTION_KEY, UUID.randomUUID().toString());
clientSession.setTimestamp(Time.currentTime()); clientSession.setTimestamp(Time.currentTime());
@ -142,16 +138,16 @@ public class ClientSessionCode {
setAction(convertToAction(requiredAction)); setAction(convertToAction(requiredAction));
} }
private ClientSessionModel.Action convertToAction(RequiredAction requiredAction) { private String convertToAction(RequiredAction requiredAction) {
switch (requiredAction) { switch (requiredAction) {
case CONFIGURE_TOTP: case CONFIGURE_TOTP:
return ClientSessionModel.Action.CONFIGURE_TOTP; return ClientSessionModel.Action.CONFIGURE_TOTP.name();
case UPDATE_PASSWORD: case UPDATE_PASSWORD:
return ClientSessionModel.Action.UPDATE_PASSWORD; return ClientSessionModel.Action.UPDATE_PASSWORD.name();
case UPDATE_PROFILE: case UPDATE_PROFILE:
return ClientSessionModel.Action.UPDATE_PROFILE; return ClientSessionModel.Action.UPDATE_PROFILE.name();
case VERIFY_EMAIL: case VERIFY_EMAIL:
return ClientSessionModel.Action.VERIFY_EMAIL; return ClientSessionModel.Action.VERIFY_EMAIL.name();
default: default:
throw new IllegalArgumentException("Unknown required action " + requiredAction); throw new IllegalArgumentException("Unknown required action " + requiredAction);
} }

View file

@ -738,7 +738,7 @@ public class AccountService {
try { try {
ClientSessionModel clientSession = auth.getClientSession(); ClientSessionModel clientSession = auth.getClientSession();
ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession); ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession);
clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE); clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
clientSession.setRedirectUri(redirectUri); clientSession.setRedirectUri(redirectUri);
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString()); clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString());

View file

@ -373,7 +373,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
private ClientSessionCode parseClientSessionCode(String code) { private ClientSessionCode parseClientSessionCode(String code) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realmModel);
if (clientCode != null && clientCode.isValid(AUTHENTICATE)) { if (clientCode != null && clientCode.isValid(AUTHENTICATE.name())) {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();
if (clientSession != null) { if (clientSession != null) {

View file

@ -166,7 +166,7 @@ public class LoginActionsService {
ClientSessionCode clientCode; ClientSessionCode clientCode;
Response response; Response response;
boolean check(String code, ClientSessionModel.Action requiredAction) { boolean check(String code, String requiredAction) {
if (!check(code)) { if (!check(code)) {
return false; return false;
} else if (!clientCode.isValid(requiredAction)) { } else if (!clientCode.isValid(requiredAction)) {
@ -178,7 +178,7 @@ public class LoginActionsService {
} }
} }
boolean check(String code, ClientSessionModel.Action requiredAction, ClientSessionModel.Action alternativeRequiredAction) { boolean check(String code, String requiredAction, String alternativeRequiredAction) {
if (!check(code)) { if (!check(code)) {
return false; return false;
} else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) { } else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
@ -231,9 +231,9 @@ public class LoginActionsService {
ClientSessionCode clientSessionCode = checks.clientCode; ClientSessionCode clientSessionCode = checks.clientCode;
ClientSessionModel clientSession = clientSessionCode.getClientSession(); ClientSessionModel clientSession = clientSessionCode.getClientSession();
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) { if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
TokenManager.dettachClientSession(session.sessions(), realm, clientSession); TokenManager.dettachClientSession(session.sessions(), realm, clientSession);
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE); clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
} }
return session.getProvider(LoginFormsProvider.class) return session.getProvider(LoginFormsProvider.class)
@ -281,7 +281,8 @@ public class LoginActionsService {
@Path("auth-form") @Path("auth-form")
@POST @POST
@Consumes(MediaType.APPLICATION_FORM_URLENCODED) @Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response authForm(@QueryParam("code") String code) { public Response authForm(@QueryParam("code") String code,
@QueryParam("action") String action) {
event.event(EventType.LOGIN); event.event(EventType.LOGIN);
if (!checkSsl()) { if (!checkSsl()) {
event.error(Errors.SSL_REQUIRED); event.error(Errors.SSL_REQUIRED);
@ -301,8 +302,8 @@ public class LoginActionsService {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId()); event.detail(Details.CODE_ID, clientSession.getId());
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) { if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE); clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE); event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
return session.getProvider(LoginFormsProvider.class) return session.getProvider(LoginFormsProvider.class)
.setError(Messages.EXPIRED_CODE) .setError(Messages.EXPIRED_CODE)
@ -338,39 +339,17 @@ public class LoginActionsService {
.setRealm(realm) .setRealm(realm)
.setSession(session) .setSession(session)
.setUriInfo(uriInfo) .setUriInfo(uriInfo)
.setAction(action)
.setRequest(request); .setRequest(request);
try { try {
return processor.authenticate(); return processor.authenticate();
} catch (AuthenticationProcessor.AuthException e) {
return handleError(e, code);
} catch (Exception e) { } catch (Exception e) {
event.error(Errors.INVALID_USER_CREDENTIALS); return processor.handleBrowserException(e);
logger.error("failed authentication", e);
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
} }
} }
protected Response handleError(AuthenticationProcessor.AuthException e, String code) {
logger.error("failed authentication: " + e.getError().toString(), e);
if (e.getError() == AuthenticationProcessor.Error.INVALID_USER) {
event.error(Errors.USER_NOT_FOUND);
return ErrorPage.error(session, Messages.INVALID_USER);
} else if (e.getError() == AuthenticationProcessor.Error.USER_DISABLED) {
event.error(Errors.USER_DISABLED);
return ErrorPage.error(session, Messages.ACCOUNT_DISABLED);
} else if (e.getError() == AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED) {
event.error(Errors.USER_TEMPORARILY_DISABLED);
return ErrorPage.error(session, Messages.ACCOUNT_TEMPORARILY_DISABLED);
} else {
event.error(Errors.INVALID_USER_CREDENTIALS);
return ErrorPage.error(session, Messages.INVALID_USER);
}
}
/** /**
* URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY! * URL called after login page. YOU SHOULD NEVER INVOKE THIS DIRECTLY!
* *
@ -402,8 +381,8 @@ public class LoginActionsService {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();
event.detail(Details.CODE_ID, clientSession.getId()); event.detail(Details.CODE_ID, clientSession.getId());
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) { if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE); clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE); event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
return session.getProvider(LoginFormsProvider.class) return session.getProvider(LoginFormsProvider.class)
.setError(Messages.EXPIRED_CODE) .setError(Messages.EXPIRED_CODE)
@ -538,7 +517,7 @@ public class LoginActionsService {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
return ErrorPage.error(session, Messages.INVALID_CODE); return ErrorPage.error(session, Messages.INVALID_CODE);
} }
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE)) { if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name())) {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
return ErrorPage.error(session, Messages.INVALID_CODE); return ErrorPage.error(session, Messages.INVALID_CODE);
} }
@ -676,7 +655,7 @@ public class LoginActionsService {
String code = formData.getFirst("code"); String code = formData.getFirst("code");
ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm); ClientSessionCode accessCode = ClientSessionCode.parse(code, session, realm);
if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT)) { if (accessCode == null || !accessCode.isValid(ClientSessionModel.Action.OAUTH_GRANT.name())) {
event.error(Errors.INVALID_CODE); event.error(Errors.INVALID_CODE);
return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE); return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE);
} }
@ -742,7 +721,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) { final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_PROFILE); event.event(EventType.UPDATE_PROFILE);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PROFILE)) { if (!checks.check(code, ClientSessionModel.Action.UPDATE_PROFILE.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -804,7 +783,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) { final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_TOTP); event.event(EventType.UPDATE_TOTP);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.CONFIGURE_TOTP)) { if (!checks.check(code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -849,7 +828,7 @@ public class LoginActionsService {
final MultivaluedMap<String, String> formData) { final MultivaluedMap<String, String> formData) {
event.event(EventType.UPDATE_PASSWORD); event.event(EventType.UPDATE_PASSWORD);
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD, ClientSessionModel.Action.RECOVER_PASSWORD)) { if (!checks.check(code, ClientSessionModel.Action.UPDATE_PASSWORD.name(), ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -890,7 +869,7 @@ public class LoginActionsService {
event.event(EventType.UPDATE_PASSWORD).success(); event.event(EventType.UPDATE_PASSWORD).success();
if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD)) { if (clientSession.getAction().equals(ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
String actionCookieValue = getActionCookie(); String actionCookieValue = getActionCookie();
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) { if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
return session.getProvider(LoginFormsProvider.class) return session.getProvider(LoginFormsProvider.class)
@ -911,7 +890,7 @@ public class LoginActionsService {
event.event(EventType.VERIFY_EMAIL); event.event(EventType.VERIFY_EMAIL);
if (key != null) { if (key != null) {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(key, ClientSessionModel.Action.VERIFY_EMAIL)) { if (!checks.check(key, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -937,7 +916,7 @@ public class LoginActionsService {
return redirectOauth(user, accessCode, clientSession, userSession); return redirectOauth(user, accessCode, clientSession, userSession);
} else { } else {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL)) { if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -960,7 +939,7 @@ public class LoginActionsService {
event.event(EventType.RESET_PASSWORD); event.event(EventType.RESET_PASSWORD);
if (key != null) { if (key != null) {
Checks checks = new Checks(); Checks checks = new Checks();
if (!checks.check(key, ClientSessionModel.Action.RECOVER_PASSWORD)) { if (!checks.check(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
return checks.response; return checks.response;
} }
ClientSessionCode accessCode = checks.clientCode; ClientSessionCode accessCode = checks.clientCode;
@ -1038,7 +1017,7 @@ public class LoginActionsService {
event.session(userSession); event.session(userSession);
TokenManager.attachClientSession(userSession, clientSession); TokenManager.attachClientSession(userSession, clientSession);
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD); accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
try { try {
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri()); UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());

View file

@ -788,7 +788,7 @@ public class UsersResource {
ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId); ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession); ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD); accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
try { try {
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri()); UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
@ -838,7 +838,7 @@ public class UsersResource {
ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId); ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession); ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL); accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL.name());
try { try {
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri()); UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());

View file

@ -3,4 +3,5 @@ org.keycloak.authentication.authenticators.LoginFormOTPAuthenticatorFactory
org.keycloak.authentication.authenticators.LoginFormPasswordAuthenticatorFactory org.keycloak.authentication.authenticators.LoginFormPasswordAuthenticatorFactory
org.keycloak.authentication.authenticators.LoginFormUsernameAuthenticatorFactory org.keycloak.authentication.authenticators.LoginFormUsernameAuthenticatorFactory
org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory
org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory
org.keycloak.authentication.authenticators.SetRequiredActionAuthenticatorFactory

View file

@ -166,7 +166,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
private ClientSessionCode parseClientSessionCode(String code) { private ClientSessionCode parseClientSessionCode(String code) {
ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm); ClientSessionCode clientCode = ClientSessionCode.parse(code, this.session, this.realm);
if (clientCode != null && clientCode.isValid(AUTHENTICATE)) { if (clientCode != null && clientCode.isValid(AUTHENTICATE.name())) {
ClientSessionModel clientSession = clientCode.getClientSession(); ClientSessionModel clientSession = clientCode.getClientSession();
if (clientSession != null) { if (clientSession != null) {

View file

@ -1,437 +1,437 @@
/* /*
* JBoss, Home of Professional Open Source. * JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors * Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the * as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors. * distribution for a full listing of individual contributors.
* *
* This is free software; you can redistribute it and/or modify it * This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as * under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of * published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version. * the License, or (at your option) any later version.
* *
* This software is distributed in the hope that it will be useful, * This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
* *
* You should have received a copy of the GNU Lesser General Public * You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free * License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/ */
package org.keycloak.testsuite.forms; package org.keycloak.testsuite.forms;
import org.junit.Assert; import org.junit.Assert;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.OAuth2Constants; import org.keycloak.OAuth2Constants;
import org.keycloak.events.Details; import org.keycloak.events.Details;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.events.EventType; import org.keycloak.events.EventType;
import org.keycloak.models.BrowserSecurityHeaders; import org.keycloak.models.BrowserSecurityHeaders;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.pages.AppPage; import org.keycloak.testsuite.pages.AppPage;
import org.keycloak.testsuite.pages.AppPage.RequestType; import org.keycloak.testsuite.pages.AppPage.RequestType;
import org.keycloak.testsuite.pages.LoginPage; import org.keycloak.testsuite.pages.LoginPage;
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage; import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
import org.keycloak.testsuite.rule.KeycloakRule; import org.keycloak.testsuite.rule.KeycloakRule;
import org.keycloak.testsuite.rule.WebResource; import org.keycloak.testsuite.rule.WebResource;
import org.keycloak.testsuite.rule.WebRule; import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.Time; import org.keycloak.util.Time;
import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebDriver;
import javax.ws.rs.client.Client; import javax.ws.rs.client.Client;
import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.ClientBuilder;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.Map; import java.util.Map;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class LoginTest { public class LoginTest {
@ClassRule @ClassRule
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() { public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
UserModel user = manager.getSession().users().addUser(appRealm, "login-test"); UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
user.setEmail("login@test.com"); user.setEmail("login@test.com");
user.setEnabled(true); user.setEnabled(true);
userId = user.getId(); userId = user.getId();
UserCredentialModel creds = new UserCredentialModel(); UserCredentialModel creds = new UserCredentialModel();
creds.setType(CredentialRepresentation.PASSWORD); creds.setType(CredentialRepresentation.PASSWORD);
creds.setValue("password"); creds.setValue("password");
user.updateCredential(creds); user.updateCredential(creds);
} }
}); });
@Rule @Rule
public AssertEvents events = new AssertEvents(keycloakRule); public AssertEvents events = new AssertEvents(keycloakRule);
@Rule @Rule
public WebRule webRule = new WebRule(this); public WebRule webRule = new WebRule(this);
@WebResource @WebResource
protected OAuthClient oauth; protected OAuthClient oauth;
@WebResource @WebResource
protected WebDriver driver; protected WebDriver driver;
@WebResource @WebResource
protected AppPage appPage; protected AppPage appPage;
@WebResource @WebResource
protected LoginPage loginPage; protected LoginPage loginPage;
@WebResource @WebResource
protected LoginPasswordUpdatePage updatePasswordPage; protected LoginPasswordUpdatePage updatePasswordPage;
private static String userId; private static String userId;
@Test @Test
public void testBrowserSecurityHeaders() { public void testBrowserSecurityHeaders() {
Client client = ClientBuilder.newClient(); Client client = ClientBuilder.newClient();
Response response = client.target(oauth.getLoginFormUrl()).request().get(); Response response = client.target(oauth.getLoginFormUrl()).request().get();
Assert.assertEquals(200, response.getStatus()); Assert.assertEquals(200, response.getStatus());
for (Map.Entry<String, String> entry : BrowserSecurityHeaders.defaultHeaders.entrySet()) { for (Map.Entry<String, String> entry : BrowserSecurityHeaders.defaultHeaders.entrySet()) {
String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey()); String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey());
String headerValue = response.getHeaderString(headerName); String headerValue = response.getHeaderString(headerName);
Assert.assertNotNull(headerValue); Assert.assertNotNull(headerValue);
Assert.assertEquals(headerValue, entry.getValue()); Assert.assertEquals(headerValue, entry.getValue());
} }
response.close(); response.close();
} }
@Test @Test
public void loginInvalidPassword() { public void loginInvalidPassword() {
loginPage.open(); loginPage.open();
loginPage.login("login-test", "invalid"); loginPage.login("login-test", "invalid");
loginPage.assertCurrent(); loginPage.assertCurrent();
Assert.assertEquals("Invalid username or password.", loginPage.getError()); Assert.assertEquals("Invalid username or password.", loginPage.getError());
events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).session((String) null).error("invalid_user_credentials").detail(Details.USERNAME, "login-test").assertEvent();
} }
@Test @Test
public void loginInvalidPasswordDisabledUser() { public void loginInvalidPasswordDisabledUser() {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() { keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
session.users().getUserByUsername("login-test", appRealm).setEnabled(false); session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
} }
}); });
try { try {
loginPage.open(); loginPage.open();
loginPage.login("login-test", "invalid"); loginPage.login("login-test", "invalid");
loginPage.assertCurrent(); loginPage.assertCurrent();
Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError()); Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError());
events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent();
} finally { } finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() { keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
session.users().getUserByUsername("login-test", appRealm).setEnabled(true); session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
} }
}); });
} }
} }
@Test @Test
public void loginDisabledUser() { public void loginDisabledUser() {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() { keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
session.users().getUserByUsername("login-test", appRealm).setEnabled(false); session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
} }
}); });
try { try {
loginPage.open(); loginPage.open();
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
loginPage.assertCurrent(); loginPage.assertCurrent();
Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError()); Assert.assertEquals("Account is disabled, contact admin.", loginPage.getError());
events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).session((String) null).error("user_disabled").detail(Details.USERNAME, "login-test").assertEvent();
} finally { } finally {
keycloakRule.configure(new KeycloakRule.KeycloakSetup() { keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
session.users().getUserByUsername("login-test", appRealm).setEnabled(true); session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
} }
}); });
} }
} }
@Test @Test
public void loginInvalidUsername() { public void loginInvalidUsername() {
loginPage.open(); loginPage.open();
loginPage.login("invalid", "password"); loginPage.login("invalid", "password");
loginPage.assertCurrent(); loginPage.assertCurrent();
Assert.assertEquals("Invalid username or password.", loginPage.getError()); Assert.assertEquals("Invalid username or password.", loginPage.getError());
events.expectLogin().user((String) null).session((String) null).error("user_not_found").detail(Details.USERNAME, "invalid").assertEvent(); events.expectLogin().user((String) null).session((String) null).error("user_not_found").detail(Details.USERNAME, "invalid").assertEvent();
} }
@Test @Test
public void loginSuccess() { public void loginSuccess() {
loginPage.open(); loginPage.open();
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
} }
@Test @Test
public void loginPromptNone() { public void loginPromptNone() {
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none"); driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
assertFalse(loginPage.isCurrent()); assertFalse(loginPage.isCurrent());
assertTrue(appPage.isCurrent()); assertTrue(appPage.isCurrent());
loginPage.open(); loginPage.open();
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none"); driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).removeDetail(Details.USERNAME).assertEvent(); events.expectLogin().user(userId).removeDetail(Details.USERNAME).assertEvent();
} }
@Test @Test
public void loginWithForcePasswordChangePolicy() { public void loginWithForcePasswordChangePolicy() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)")); appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
} }
}); });
try { try {
// Setting offset to more than one day to force password update // Setting offset to more than one day to force password update
// elapsedTime > timeToExpire // elapsedTime > timeToExpire
Time.setOffset(86405); Time.setOffset(86405);
loginPage.open(); loginPage.open();
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
updatePasswordPage.assertCurrent(); updatePasswordPage.assertCurrent();
updatePasswordPage.changePassword("updatedPassword", "updatedPassword"); updatePasswordPage.changePassword("updatedPassword", "updatedPassword");
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent();
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
} finally { } finally {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setPasswordPolicy(new PasswordPolicy(null)); appRealm.setPasswordPolicy(new PasswordPolicy(null));
UserModel user = manager.getSession().users().getUserByUsername("login-test", appRealm); UserModel user = manager.getSession().users().getUserByUsername("login-test", appRealm);
UserCredentialModel cred = new UserCredentialModel(); UserCredentialModel cred = new UserCredentialModel();
cred.setType(CredentialRepresentation.PASSWORD); cred.setType(CredentialRepresentation.PASSWORD);
cred.setValue("password"); cred.setValue("password");
user.updateCredential(cred); user.updateCredential(cred);
} }
}); });
Time.setOffset(0); Time.setOffset(0);
} }
} }
@Test @Test
public void loginWithoutForcePasswordChangePolicy() { public void loginWithoutForcePasswordChangePolicy() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)")); appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
} }
}); });
try { try {
// Setting offset to less than one day to avoid forced password update // Setting offset to less than one day to avoid forced password update
// elapsedTime < timeToExpire // elapsedTime < timeToExpire
Time.setOffset(86205); Time.setOffset(86205);
loginPage.open(); loginPage.open();
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
} finally { } finally {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setPasswordPolicy(new PasswordPolicy(null)); appRealm.setPasswordPolicy(new PasswordPolicy(null));
} }
}); });
Time.setOffset(0); Time.setOffset(0);
} }
} }
@Test @Test
public void loginNoTimeoutWithLongWait() { public void loginNoTimeoutWithLongWait() {
try { try {
loginPage.open(); loginPage.open();
Time.setOffset(1700); Time.setOffset(1700);
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId();
} finally { } finally {
Time.setOffset(0); Time.setOffset(0);
} }
} }
@Test @Test
public void loginTimeout() { public void loginTimeout() {
try { try {
loginPage.open(); loginPage.open();
Time.setOffset(1850); Time.setOffset(1850);
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
events.expectLogin().clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).user((String) null).session((String) null).error("expired_code").assertEvent().getSessionId(); events.expectLogin().clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).user((String) null).session((String) null).error("expired_code").assertEvent().getSessionId();
} finally { } finally {
Time.setOffset(0); Time.setOffset(0);
} }
} }
@Test @Test
public void loginLoginHint() { public void loginLoginHint() {
String loginFormUrl = oauth.getLoginFormUrl() + "&login_hint=login-test"; String loginFormUrl = oauth.getLoginFormUrl() + "&login_hint=login-test";
driver.navigate().to(loginFormUrl); driver.navigate().to(loginFormUrl);
Assert.assertEquals("login-test", loginPage.getUsername()); Assert.assertEquals("login-test", loginPage.getUsername());
loginPage.login("password"); loginPage.login("password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent(); events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
} }
@Test @Test
public void loginWithEmailSuccess() { public void loginWithEmailSuccess() {
loginPage.open(); loginPage.open();
loginPage.login("login@test.com", "password"); loginPage.login("login@test.com", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
events.expectLogin().user(userId).assertEvent(); events.expectLogin().user(userId).assertEvent();
} }
@Test @Test
public void loginWithRememberMe() { public void loginWithRememberMe() {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setRememberMe(true); appRealm.setRememberMe(true);
} }
}); });
try { try {
loginPage.open(); loginPage.open();
assertFalse(loginPage.isRememberMeChecked()); assertFalse(loginPage.isRememberMeChecked());
loginPage.setRememberMe(true); loginPage.setRememberMe(true);
assertTrue(loginPage.isRememberMeChecked()); assertTrue(loginPage.isRememberMeChecked());
loginPage.login("login-test", "password"); loginPage.login("login-test", "password");
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE)); Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
Event loginEvent = events.expectLogin().user(userId) Event loginEvent = events.expectLogin().user(userId)
.detail(Details.USERNAME, "login-test") .detail(Details.USERNAME, "login-test")
.detail(Details.REMEMBER_ME, "true") .detail(Details.REMEMBER_ME, "true")
.assertEvent(); .assertEvent();
String sessionId = loginEvent.getSessionId(); String sessionId = loginEvent.getSessionId();
// Expire session // Expire session
keycloakRule.removeUserSession(sessionId); keycloakRule.removeUserSession(sessionId);
// Assert rememberMe checked and username/email prefilled // Assert rememberMe checked and username/email prefilled
loginPage.open(); loginPage.open();
assertTrue(loginPage.isRememberMeChecked()); assertTrue(loginPage.isRememberMeChecked());
Assert.assertEquals("login-test", loginPage.getUsername()); Assert.assertEquals("login-test", loginPage.getUsername());
loginPage.setRememberMe(false); loginPage.setRememberMe(false);
} finally { } finally {
keycloakRule.update(new KeycloakRule.KeycloakSetup() { keycloakRule.update(new KeycloakRule.KeycloakSetup() {
@Override @Override
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) { public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
appRealm.setRememberMe(false); appRealm.setRememberMe(false);
} }
}); });
} }
} }
@Test @Test
public void loginCancel() { public void loginCancel() {
loginPage.open(); loginPage.open();
loginPage.cancel(); loginPage.cancel();
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType()); Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
Assert.assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR)); Assert.assertEquals("access_denied", oauth.getCurrentQuery().get(OAuth2Constants.ERROR));
events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).assertEvent(); events.expectLogin().error("rejected_by_user").user((String) null).session((String) null).removeDetail(Details.USERNAME).assertEvent();
} }
// KEYCLOAK-1037 // KEYCLOAK-1037
@Test @Test
public void loginExpiredCode() { public void loginExpiredCode() {
try { try {
loginPage.open(); loginPage.open();
Time.setOffset(5000); Time.setOffset(5000);
loginPage.login("login@test.com", "password"); loginPage.login("login@test.com", "password");
loginPage.assertCurrent(); loginPage.assertCurrent();
Assert.assertEquals("Login timeout. Please login again.", loginPage.getError()); Assert.assertEquals("Login timeout. Please login again.", loginPage.getError());
events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).assertEvent(); events.expectLogin().user((String) null).session((String) null).error("expired_code").clearDetails().detail(Details.CODE_ID, AssertEvents.isCodeId()).assertEvent();
} finally { } finally {
Time.setOffset(0); Time.setOffset(0);
} }
} }
} }

View file

@ -117,14 +117,14 @@ public class UserSessionProviderTest {
int time = clientSession.getTimestamp(); int time = clientSession.getTimestamp();
assertEquals(null, clientSession.getAction()); assertEquals(null, clientSession.getAction());
clientSession.setAction(ClientSessionModel.Action.CODE_TO_TOKEN); clientSession.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
clientSession.setTimestamp(time + 10); clientSession.setTimestamp(time + 10);
kc.stopSession(session, true); kc.stopSession(session, true);
session = kc.startSession(); session = kc.startSession();
ClientSessionModel updated = session.sessions().getClientSession(realm, id); ClientSessionModel updated = session.sessions().getClientSession(realm, id);
assertEquals(ClientSessionModel.Action.CODE_TO_TOKEN, updated.getAction()); assertEquals(ClientSessionModel.Action.CODE_TO_TOKEN.name(), updated.getAction());
assertEquals(time + 10, updated.getTimestamp()); assertEquals(time + 10, updated.getTimestamp());
} }