clientsession.action to String
This commit is contained in:
parent
dcc40b0a63
commit
95349e6e2e
44 changed files with 989 additions and 561 deletions
|
@ -7,6 +7,13 @@
|
|||
<delete tableName="CLIENT_SESSION"/>
|
||||
<delete tableName="USER_SESSION_NOTE"/>
|
||||
<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">
|
||||
<column name="ID" type="VARCHAR(36)">
|
||||
<constraints nullable="false"/>
|
||||
|
@ -145,6 +152,63 @@
|
|||
<column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
|
||||
<where>ACTION = 3</where>
|
||||
</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">
|
||||
<column name="NAME" type="VARCHAR(255)">
|
||||
<constraints nullable="false"/>
|
||||
|
@ -162,10 +226,12 @@
|
|||
<addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTHENTICATOR_CONFIG_PK" tableName="AUTHENTICATOR_CONFIG"/>
|
||||
<dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_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="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="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="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"/>
|
||||
|
|
|
@ -22,9 +22,9 @@ public interface ClientSessionModel {
|
|||
|
||||
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 void setRoles(Set<String> roles);
|
||||
|
|
|
@ -156,6 +156,13 @@ public interface RealmModel extends RoleContainerModel {
|
|||
|
||||
void updateDefaultRoles(String[] defaultRoles);
|
||||
|
||||
Set<String> getDefaultRequiredActions();
|
||||
|
||||
void addDefaultRequiredAction(String action);
|
||||
void removeDefaultRequiredAction(String action);
|
||||
|
||||
void setDefaultRequiredActions(Set<String> action);
|
||||
|
||||
// Key is clientId
|
||||
Map<String, ClientModel> getClientNameMap();
|
||||
|
||||
|
|
|
@ -2,8 +2,10 @@ package org.keycloak.models.entities;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @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<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
|
||||
private List<AuthenticatorEntity> authenticators = new ArrayList<>();
|
||||
private List<String> defaultRequiredActions = new ArrayList<>();
|
||||
|
||||
|
||||
public String getName() {
|
||||
|
@ -500,6 +503,14 @@ public class RealmEntity extends AbstractIdentifiableEntity {
|
|||
public void setAuthenticators(List<AuthenticatorEntity> authenticators) {
|
||||
this.authenticators = authenticators;
|
||||
}
|
||||
|
||||
public List<String> getDefaultRequiredActions() {
|
||||
return defaultRequiredActions;
|
||||
}
|
||||
|
||||
public void setDefaultRequiredActions(List<String> defaultRequiredActions) {
|
||||
this.defaultRequiredActions = defaultRequiredActions;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -104,5 +104,7 @@ public class DefaultAuthenticationFlows {
|
|||
execution.setAutheticatorFlow(false);
|
||||
realm.addAuthenticatorExecution(execution);
|
||||
|
||||
//
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1548,4 +1548,37 @@ public class RealmAdapter implements RealmModel {
|
|||
mapper.setConfig(config);
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1116,4 +1116,30 @@ public class RealmAdapter implements RealmModel {
|
|||
if (updated != null) return updated.getAuthenticatorById(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);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -98,6 +98,7 @@ public class CachedRealm {
|
|||
private Set<String> supportedLocales = new HashSet<String>();
|
||||
private String defaultLocale;
|
||||
private MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>();
|
||||
private Set<String> defaultRequiredActions = new HashSet<>();
|
||||
|
||||
public CachedRealm() {
|
||||
}
|
||||
|
@ -200,6 +201,7 @@ public class CachedRealm {
|
|||
for (AuthenticatorModel authenticator : model.getAuthenticators()) {
|
||||
authenticators.put(authenticator.getId(), authenticator);
|
||||
}
|
||||
this.defaultRequiredActions.addAll(model.getDefaultRequiredActions());
|
||||
|
||||
}
|
||||
|
||||
|
@ -438,4 +440,8 @@ public class CachedRealm {
|
|||
public Map<String, AuthenticationExecutionModel> getExecutionsById() {
|
||||
return executionsById;
|
||||
}
|
||||
|
||||
public Set<String> getDefaultRequiredActions() {
|
||||
return defaultRequiredActions;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1707,5 +1707,30 @@ public class RealmAdapter implements RealmModel {
|
|||
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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -113,6 +113,12 @@ public class RealmEntity {
|
|||
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
|
||||
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
|
||||
@MapKeyColumn(name="NAME")
|
||||
@Column(name="VALUE")
|
||||
|
@ -568,5 +574,13 @@ public class RealmEntity {
|
|||
public void setAuthenticationFlows(Collection<AuthenticationFlowEntity> authenticationFlows) {
|
||||
this.authenticationFlows = authenticationFlows;
|
||||
}
|
||||
|
||||
public Set<String> getDefaultRequiredActions() {
|
||||
return defaultRequiredActions;
|
||||
}
|
||||
|
||||
public void setDefaultRequiredActions(Set<String> defaultRequiredActions) {
|
||||
this.defaultRequiredActions = defaultRequiredActions;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1590,4 +1590,32 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
|
|||
mapper.setConfig(config);
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -100,12 +100,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
public String getAction() {
|
||||
return entity.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(Action action) {
|
||||
public void setAction(String action) {
|
||||
entity.setAction(action);
|
||||
update();
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@ public class ClientSessionEntity extends SessionEntity {
|
|||
|
||||
private int timestamp;
|
||||
|
||||
private ClientSessionModel.Action action;
|
||||
private String action;
|
||||
|
||||
private Set<String> roles;
|
||||
private Set<String> protocolMappers;
|
||||
|
@ -81,11 +81,11 @@ public class ClientSessionEntity extends SessionEntity {
|
|||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public ClientSessionModel.Action getAction() {
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
|
|
@ -189,12 +189,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
public String getAction() {
|
||||
return entity.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(Action action) {
|
||||
public void setAction(String action) {
|
||||
entity.setAction(action);
|
||||
}
|
||||
|
||||
|
|
|
@ -54,8 +54,8 @@ public class ClientSessionEntity {
|
|||
@Column(name="AUTH_METHOD")
|
||||
protected String authMethod;
|
||||
|
||||
@Column(name="ACTION")
|
||||
protected ClientSessionModel.Action action;
|
||||
@Column(name="CURRENT_ACTION")
|
||||
protected String action;
|
||||
|
||||
@Column(name="AUTH_USER_ID")
|
||||
protected String userId;
|
||||
|
@ -123,11 +123,11 @@ public class ClientSessionEntity {
|
|||
this.redirectUri = redirectUri;
|
||||
}
|
||||
|
||||
public ClientSessionModel.Action getAction() {
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
|
|
@ -93,12 +93,12 @@ public class ClientSessionAdapter implements ClientSessionModel {
|
|||
}
|
||||
|
||||
@Override
|
||||
public ClientSessionModel.Action getAction() {
|
||||
public String getAction() {
|
||||
return entity.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
entity.setAction(action);
|
||||
}
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@ public class ClientSessionEntity {
|
|||
private String authMethod;
|
||||
|
||||
private int timestamp;
|
||||
private ClientSessionModel.Action action;
|
||||
private String action;
|
||||
private Set<String> roles;
|
||||
private Set<String> protocolMappers;
|
||||
private Map<String, String> notes = new HashMap<>();
|
||||
|
@ -78,11 +78,11 @@ public class ClientSessionEntity {
|
|||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public ClientSessionModel.Action getAction() {
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
|
|
@ -108,12 +108,12 @@ public class ClientSessionAdapter extends AbstractMongoAdapter<MongoClientSessio
|
|||
}
|
||||
|
||||
@Override
|
||||
public Action getAction() {
|
||||
public String getAction() {
|
||||
return entity.getAction();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAction(Action action) {
|
||||
public void setAction(String action) {
|
||||
entity.setAction(action);
|
||||
updateMongoEntity();
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
|
|||
private String authMethod;
|
||||
|
||||
private int timestamp;
|
||||
private ClientSessionModel.Action action;
|
||||
private String action;
|
||||
private List<String> roles;
|
||||
private List<String> protocolMappers;
|
||||
private Map<String, String> notes = new HashMap<String, String>();
|
||||
|
@ -82,11 +82,11 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
|
|||
this.timestamp = timestamp;
|
||||
}
|
||||
|
||||
public ClientSessionModel.Action getAction() {
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,6 +21,7 @@ import org.keycloak.login.LoginFormsProvider;
|
|||
import org.keycloak.models.AuthenticationFlowModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientSessionModel;
|
||||
import org.keycloak.models.IdentityProviderModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
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.processing.core.saml.v2.common.SAMLDocumentHolder;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.Urls;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.managers.HttpAuthenticationManager;
|
||||
|
@ -59,6 +61,7 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.PublicKey;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource class for the oauth/openid connect token service
|
||||
|
@ -264,7 +267,7 @@ public class SamlService {
|
|||
ClientSessionModel clientSession = session.sessions().createClientSession(realm, client);
|
||||
clientSession.setAuthMethod(SamlProtocol.LOGIN_PROTOCOL);
|
||||
clientSession.setRedirectUri(redirect);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
|
||||
clientSession.setNote(SamlProtocol.SAML_BINDING, bindingType);
|
||||
clientSession.setNote(GeneralConstants.RELAY_STATE, relayState);
|
||||
|
@ -317,7 +320,22 @@ public class SamlService {
|
|||
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) {
|
||||
List<IdentityProviderModel> identityProviders = realm.getIdentityProviders();
|
||||
for (IdentityProviderModel identityProvider : identityProviders) {
|
||||
if (identityProvider.isAuthenticateByDefault()) {
|
||||
return buildRedirectToIdentityProvider(identityProvider.getAlias(), new ClientSessionCode(realm, clientSession).getCode() );
|
||||
}
|
||||
}
|
||||
|
||||
String flowId = null;
|
||||
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
|
||||
if (flow.getAlias().equals("browser")) {
|
||||
|
@ -336,7 +354,11 @@ public class SamlService {
|
|||
.setUriInfo(uriInfo)
|
||||
.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
|
||||
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
if (clientSession.getClient().getId().equals(client.getId())) {
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
}
|
||||
logger.debug("browser Logout");
|
||||
|
@ -405,7 +427,7 @@ public class SamlService {
|
|||
if (clientSession == null) continue;
|
||||
if (clientSession.getClient().getClientId().equals(client.getClientId())) {
|
||||
// remove requesting client from logout
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
}
|
||||
UserSessionModel userSession = clientSession.getUserSession();
|
||||
try {
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.jboss.logging.Logger;
|
|||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.ClientConnection;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventBuilder;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.AuthenticationExecutionModel;
|
||||
|
@ -15,8 +16,10 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.models.UserSessionModel;
|
||||
import org.keycloak.protocol.oidc.TokenManager;
|
||||
import org.keycloak.services.ErrorPage;
|
||||
import org.keycloak.services.managers.AuthenticationManager;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
@ -39,6 +42,7 @@ public class AuthenticationProcessor {
|
|||
protected EventBuilder event;
|
||||
protected HttpRequest request;
|
||||
protected String flowId;
|
||||
protected String action;
|
||||
protected boolean userSessionCreated;
|
||||
|
||||
|
||||
|
@ -134,6 +138,11 @@ public class AuthenticationProcessor {
|
|||
return this;
|
||||
}
|
||||
|
||||
public AuthenticationProcessor setAction(String action) {
|
||||
this.action = action;
|
||||
return this;
|
||||
}
|
||||
|
||||
private class Result implements AuthenticatorContext {
|
||||
AuthenticatorModel model;
|
||||
AuthenticationExecutionModel execution;
|
||||
|
@ -168,6 +177,11 @@ public class AuthenticationProcessor {
|
|||
this.model = model;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAction() {
|
||||
return AuthenticationProcessor.this.action;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Authenticator getAuthenticator() {
|
||||
return authenticator;
|
||||
|
@ -332,6 +346,34 @@ public class AuthenticationProcessor {
|
|||
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 {
|
||||
logger.debug("AUTHENTICATE");
|
||||
event.event(EventType.LOGIN);
|
||||
|
|
|
@ -31,6 +31,8 @@ public interface AuthenticatorContext {
|
|||
|
||||
void setAuthenticatorModel(AuthenticatorModel model);
|
||||
|
||||
String getAction();
|
||||
|
||||
Authenticator getAuthenticator();
|
||||
|
||||
void setAuthenticator(Authenticator authenticator);
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
}
|
|
@ -19,26 +19,27 @@ import java.net.URI;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
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) {
|
||||
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
URI action = getActionUrl(context, code);
|
||||
code.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
URI action = getActionUrl(context, code, LOGIN_FORM_ACTION);
|
||||
return context.getSession().getProvider(LoginFormsProvider.class)
|
||||
.setActionUri(action)
|
||||
.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())
|
||||
.queryParam(OAuth2Constants.CODE, code.getCode())
|
||||
.queryParam(OAuth2Constants.CODE, code.getCode())
|
||||
.queryParam(ACTION, action)
|
||||
.build(context.getRealm().getName());
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
|
|||
|
||||
@Override
|
||||
public void authenticate(AuthenticatorContext context) {
|
||||
if (!isActionUrl(context)) {
|
||||
if (!isAction(context, LOGIN_FORM_ACTION)) {
|
||||
context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -9,7 +9,6 @@ import org.keycloak.models.RealmModel;
|
|||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -28,7 +27,7 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
|
|||
|
||||
@Override
|
||||
public void authenticate(AuthenticatorContext context) {
|
||||
if (!isActionUrl(context)) {
|
||||
if (!isAction(context, LOGIN_FORM_ACTION)) {
|
||||
context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ public class LoginFormUsernameAuthenticator extends AbstractFormAuthenticator im
|
|||
|
||||
@Override
|
||||
public void authenticate(AuthenticatorContext context) {
|
||||
if (!isActionUrl(context)) {
|
||||
if (!isAction(context, LOGIN_FORM_ACTION)) {
|
||||
MultivaluedMap<String, String> formData = new MultivaluedMapImpl<>();
|
||||
String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.ClientSessionCode;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.services.resources.LoginActionsService;
|
||||
|
||||
import javax.ws.rs.core.MultivaluedMap;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -26,6 +25,7 @@ import java.util.List;
|
|||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class OTPFormAuthenticator extends AbstractFormAuthenticator implements Authenticator {
|
||||
public static final String TOTP_FORM_ACTION = "totp";
|
||||
protected AuthenticatorModel model;
|
||||
|
||||
public OTPFormAuthenticator(AuthenticatorModel model) {
|
||||
|
@ -34,7 +34,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
|||
|
||||
@Override
|
||||
public void authenticate(AuthenticatorContext context) {
|
||||
if (!isActionUrl(context)) {
|
||||
if (!isAction(context, TOTP_FORM_ACTION)) {
|
||||
Response challengeResponse = challenge(context, null);
|
||||
context.challenge(challengeResponse);
|
||||
return;
|
||||
|
@ -43,7 +43,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
|||
}
|
||||
|
||||
public void validateOTP(AuthenticatorContext context) {
|
||||
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
|
||||
MultivaluedMap<String, String> inputData = context.getHttpRequest().getDecodedFormParameters();
|
||||
List<UserCredentialModel> credentials = new LinkedList<>();
|
||||
String password = inputData.getFirst(CredentialRepresentation.TOTP);
|
||||
if (password == null) {
|
||||
|
@ -70,7 +70,7 @@ public class OTPFormAuthenticator extends AbstractFormAuthenticator implements A
|
|||
|
||||
protected Response challenge(AuthenticatorContext context, String error) {
|
||||
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)
|
||||
.setActionUri(action)
|
||||
.setClientSessionCode(clientSessionCode.getCode());
|
||||
|
|
|
@ -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() {
|
||||
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -130,7 +130,7 @@ public class OIDCLoginProtocol implements LoginProtocol {
|
|||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
String redirect = clientSession.getRedirectUri();
|
||||
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());
|
||||
log.debugv("redirectAccessCode: state: {0}", state);
|
||||
if (state != null)
|
||||
|
|
|
@ -220,7 +220,7 @@ public class AuthorizationEndpoint {
|
|||
clientSession = session.sessions().createClientSession(realm, client);
|
||||
clientSession.setAuthMethod(OIDCLoginProtocol.LOGIN_PROTOCOL);
|
||||
clientSession.setRedirectUri(redirectUri);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
clientSession.setNote(ClientSessionCode.ACTION_KEY, KeycloakModelUtils.generateCodeSecret());
|
||||
clientSession.setNote(OIDCLoginProtocol.RESPONSE_TYPE_PARAM, responseType);
|
||||
clientSession.setNote(OIDCLoginProtocol.REDIRECT_URI_PARAM, redirectUriParam);
|
||||
|
@ -277,7 +277,12 @@ public class AuthorizationEndpoint {
|
|||
.setUriInfo(uriInfo)
|
||||
.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 (processor.isUserSessionCreated()) {
|
||||
|
|
|
@ -191,7 +191,7 @@ public class TokenEndpoint {
|
|||
|
||||
ClientSessionModel clientSession = accessCode.getClientSession();
|
||||
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);
|
||||
throw new ErrorResponseException("invalid_grant", "Code is expired", Response.Status.BAD_REQUEST);
|
||||
}
|
||||
|
|
|
@ -148,7 +148,7 @@ public class AuthenticationManager {
|
|||
|
||||
public static void backchannelLogoutClientSession(KeycloakSession session, RealmModel realm, ClientSessionModel clientSession, UserSessionModel userSession, UriInfo uriInfo, HttpHeaders headers) {
|
||||
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();
|
||||
if (authMethod == null) return; // must be a keycloak service like account
|
||||
LoginProtocol protocol = session.getProvider(LoginProtocol.class, authMethod);
|
||||
|
@ -156,7 +156,7 @@ public class AuthenticationManager {
|
|||
.setHttpHeaders(headers)
|
||||
.setUriInfo(uriInfo);
|
||||
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>();
|
||||
for (ClientSessionModel clientSession : userSession.getClientSessions()) {
|
||||
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()) {
|
||||
String authMethod = clientSession.getAuthMethod();
|
||||
if (authMethod == null) continue; // must be a keycloak service like account
|
||||
|
@ -205,7 +205,7 @@ public class AuthenticationManager {
|
|||
try {
|
||||
logger.debugv("backchannel logout to: {0}", client.getClientId());
|
||||
protocol.backchannelLogout(userSession, clientSession);
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT);
|
||||
clientSession.setAction(ClientSessionModel.Action.LOGGED_OUT.name());
|
||||
} catch (Exception e) {
|
||||
logger.warn("Failed to logout client, continuing", e);
|
||||
}
|
||||
|
@ -219,7 +219,7 @@ public class AuthenticationManager {
|
|||
.setHttpHeaders(headers)
|
||||
.setUriInfo(uriInfo);
|
||||
// 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 {
|
||||
logger.debugv("frontchannel logout to: {0}", nextRedirectClient.getClient().getClientId());
|
||||
Response response = protocol.frontchannelLogout(userSession, nextRedirectClient);
|
||||
|
@ -476,7 +476,7 @@ public class AuthenticationManager {
|
|||
}
|
||||
|
||||
if (client.isConsentRequired()) {
|
||||
accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT);
|
||||
accessCode.setAction(ClientSessionModel.Action.OAUTH_GRANT.name());
|
||||
|
||||
UserConsentModel grantedConsent = user.getConsentByClient(client.getId());
|
||||
|
||||
|
|
|
@ -80,8 +80,8 @@ public class ClientSessionCode {
|
|||
return clientSession;
|
||||
}
|
||||
|
||||
public boolean isValid(ClientSessionModel.Action requestedAction) {
|
||||
ClientSessionModel.Action action = clientSession.getAction();
|
||||
public boolean isValid(String requestedAction) {
|
||||
String action = clientSession.getAction();
|
||||
if (action == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -93,18 +93,14 @@ public class ClientSessionCode {
|
|||
}
|
||||
|
||||
int lifespan;
|
||||
switch (action) {
|
||||
case CODE_TO_TOKEN:
|
||||
lifespan = realm.getAccessCodeLifespan();
|
||||
break;
|
||||
case AUTHENTICATE:
|
||||
lifespan = realm.getAccessCodeLifespanLogin() > 0 ? realm.getAccessCodeLifespanLogin() : realm.getAccessCodeLifespanUserAction();
|
||||
break;
|
||||
default:
|
||||
lifespan = realm.getAccessCodeLifespanUserAction();
|
||||
break;
|
||||
}
|
||||
if (action.equals(ClientSessionModel.Action.CODE_TO_TOKEN.name())) {
|
||||
lifespan = realm.getAccessCodeLifespan();
|
||||
|
||||
} 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();
|
||||
}
|
||||
|
||||
|
@ -132,7 +128,7 @@ public class ClientSessionCode {
|
|||
return requestedProtocolMappers;
|
||||
}
|
||||
|
||||
public void setAction(ClientSessionModel.Action action) {
|
||||
public void setAction(String action) {
|
||||
clientSession.setAction(action);
|
||||
clientSession.setNote(ACTION_KEY, UUID.randomUUID().toString());
|
||||
clientSession.setTimestamp(Time.currentTime());
|
||||
|
@ -142,16 +138,16 @@ public class ClientSessionCode {
|
|||
setAction(convertToAction(requiredAction));
|
||||
}
|
||||
|
||||
private ClientSessionModel.Action convertToAction(RequiredAction requiredAction) {
|
||||
private String convertToAction(RequiredAction requiredAction) {
|
||||
switch (requiredAction) {
|
||||
case CONFIGURE_TOTP:
|
||||
return ClientSessionModel.Action.CONFIGURE_TOTP;
|
||||
return ClientSessionModel.Action.CONFIGURE_TOTP.name();
|
||||
case UPDATE_PASSWORD:
|
||||
return ClientSessionModel.Action.UPDATE_PASSWORD;
|
||||
return ClientSessionModel.Action.UPDATE_PASSWORD.name();
|
||||
case UPDATE_PROFILE:
|
||||
return ClientSessionModel.Action.UPDATE_PROFILE;
|
||||
return ClientSessionModel.Action.UPDATE_PROFILE.name();
|
||||
case VERIFY_EMAIL:
|
||||
return ClientSessionModel.Action.VERIFY_EMAIL;
|
||||
return ClientSessionModel.Action.VERIFY_EMAIL.name();
|
||||
default:
|
||||
throw new IllegalArgumentException("Unknown required action " + requiredAction);
|
||||
}
|
||||
|
|
|
@ -738,7 +738,7 @@ public class AccountService {
|
|||
try {
|
||||
ClientSessionModel clientSession = auth.getClientSession();
|
||||
ClientSessionCode clientSessionCode = new ClientSessionCode(realm, clientSession);
|
||||
clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
clientSessionCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
clientSession.setRedirectUri(redirectUri);
|
||||
clientSession.setNote(OIDCLoginProtocol.STATE_PARAM, UUID.randomUUID().toString());
|
||||
|
||||
|
|
|
@ -373,7 +373,7 @@ public class IdentityBrokerService implements IdentityProvider.AuthenticationCal
|
|||
private ClientSessionCode parseClientSessionCode(String code) {
|
||||
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();
|
||||
|
||||
if (clientSession != null) {
|
||||
|
|
|
@ -166,7 +166,7 @@ public class LoginActionsService {
|
|||
ClientSessionCode clientCode;
|
||||
Response response;
|
||||
|
||||
boolean check(String code, ClientSessionModel.Action requiredAction) {
|
||||
boolean check(String code, String requiredAction) {
|
||||
if (!check(code)) {
|
||||
return false;
|
||||
} 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)) {
|
||||
return false;
|
||||
} else if (!(clientCode.isValid(requiredAction) || clientCode.isValid(alternativeRequiredAction))) {
|
||||
|
@ -231,9 +231,9 @@ public class LoginActionsService {
|
|||
ClientSessionCode clientSessionCode = checks.clientCode;
|
||||
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);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
clientSession.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
}
|
||||
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
|
@ -281,7 +281,8 @@ public class LoginActionsService {
|
|||
@Path("auth-form")
|
||||
@POST
|
||||
@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);
|
||||
if (!checkSsl()) {
|
||||
event.error(Errors.SSL_REQUIRED);
|
||||
|
@ -301,8 +302,8 @@ public class LoginActionsService {
|
|||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.EXPIRED_CODE)
|
||||
|
@ -338,39 +339,17 @@ public class LoginActionsService {
|
|||
.setRealm(realm)
|
||||
.setSession(session)
|
||||
.setUriInfo(uriInfo)
|
||||
.setAction(action)
|
||||
.setRequest(request);
|
||||
|
||||
try {
|
||||
return processor.authenticate();
|
||||
} catch (AuthenticationProcessor.AuthException e) {
|
||||
return handleError(e, code);
|
||||
} catch (Exception e) {
|
||||
event.error(Errors.INVALID_USER_CREDENTIALS);
|
||||
logger.error("failed authentication", e);
|
||||
return ErrorPage.error(session, Messages.UNEXPECTED_ERROR_HANDLING_RESPONSE);
|
||||
|
||||
return processor.handleBrowserException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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!
|
||||
*
|
||||
|
@ -402,8 +381,8 @@ public class LoginActionsService {
|
|||
ClientSessionModel clientSession = clientCode.getClientSession();
|
||||
event.detail(Details.CODE_ID, clientSession.getId());
|
||||
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE);
|
||||
if (!clientCode.isValid(ClientSessionModel.Action.AUTHENTICATE.name()) || clientSession.getUserSession() != null) {
|
||||
clientCode.setAction(ClientSessionModel.Action.AUTHENTICATE.name());
|
||||
event.client(clientSession.getClient()).error(Errors.EXPIRED_CODE);
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
.setError(Messages.EXPIRED_CODE)
|
||||
|
@ -538,7 +517,7 @@ public class LoginActionsService {
|
|||
event.error(Errors.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);
|
||||
return ErrorPage.error(session, Messages.INVALID_CODE);
|
||||
}
|
||||
|
@ -676,7 +655,7 @@ public class LoginActionsService {
|
|||
String code = formData.getFirst("code");
|
||||
|
||||
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);
|
||||
return ErrorPage.error(session, Messages.INVALID_ACCESS_CODE);
|
||||
}
|
||||
|
@ -742,7 +721,7 @@ public class LoginActionsService {
|
|||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.UPDATE_PROFILE);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PROFILE)) {
|
||||
if (!checks.check(code, ClientSessionModel.Action.UPDATE_PROFILE.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -804,7 +783,7 @@ public class LoginActionsService {
|
|||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.UPDATE_TOTP);
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, ClientSessionModel.Action.CONFIGURE_TOTP)) {
|
||||
if (!checks.check(code, ClientSessionModel.Action.CONFIGURE_TOTP.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -849,7 +828,7 @@ public class LoginActionsService {
|
|||
final MultivaluedMap<String, String> formData) {
|
||||
event.event(EventType.UPDATE_PASSWORD);
|
||||
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;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -890,7 +869,7 @@ public class LoginActionsService {
|
|||
|
||||
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();
|
||||
if (actionCookieValue == null || !actionCookieValue.equals(userSession.getId())) {
|
||||
return session.getProvider(LoginFormsProvider.class)
|
||||
|
@ -911,7 +890,7 @@ public class LoginActionsService {
|
|||
event.event(EventType.VERIFY_EMAIL);
|
||||
if (key != null) {
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(key, ClientSessionModel.Action.VERIFY_EMAIL)) {
|
||||
if (!checks.check(key, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -937,7 +916,7 @@ public class LoginActionsService {
|
|||
return redirectOauth(user, accessCode, clientSession, userSession);
|
||||
} else {
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL)) {
|
||||
if (!checks.check(code, ClientSessionModel.Action.VERIFY_EMAIL.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -960,7 +939,7 @@ public class LoginActionsService {
|
|||
event.event(EventType.RESET_PASSWORD);
|
||||
if (key != null) {
|
||||
Checks checks = new Checks();
|
||||
if (!checks.check(key, ClientSessionModel.Action.RECOVER_PASSWORD)) {
|
||||
if (!checks.check(key, ClientSessionModel.Action.RECOVER_PASSWORD.name())) {
|
||||
return checks.response;
|
||||
}
|
||||
ClientSessionCode accessCode = checks.clientCode;
|
||||
|
@ -1038,7 +1017,7 @@ public class LoginActionsService {
|
|||
event.session(userSession);
|
||||
TokenManager.attachClientSession(userSession, clientSession);
|
||||
|
||||
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD);
|
||||
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
|
||||
|
||||
try {
|
||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||
|
|
|
@ -788,7 +788,7 @@ public class UsersResource {
|
|||
|
||||
ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD);
|
||||
accessCode.setAction(ClientSessionModel.Action.RECOVER_PASSWORD.name());
|
||||
|
||||
try {
|
||||
UriBuilder builder = Urls.loginPasswordResetBuilder(uriInfo.getBaseUri());
|
||||
|
@ -838,7 +838,7 @@ public class UsersResource {
|
|||
ClientSessionModel clientSession = createClientSession(user, redirectUri, clientId);
|
||||
ClientSessionCode accessCode = new ClientSessionCode(realm, clientSession);
|
||||
|
||||
accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL);
|
||||
accessCode.setAction(ClientSessionModel.Action.VERIFY_EMAIL.name());
|
||||
|
||||
try {
|
||||
UriBuilder builder = Urls.loginActionEmailVerificationBuilder(uriInfo.getBaseUri());
|
||||
|
|
|
@ -3,4 +3,5 @@ org.keycloak.authentication.authenticators.LoginFormOTPAuthenticatorFactory
|
|||
org.keycloak.authentication.authenticators.LoginFormPasswordAuthenticatorFactory
|
||||
org.keycloak.authentication.authenticators.LoginFormUsernameAuthenticatorFactory
|
||||
org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory
|
||||
org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory
|
||||
org.keycloak.authentication.authenticators.SpnegoAuthenticatorFactory
|
||||
org.keycloak.authentication.authenticators.SetRequiredActionAuthenticatorFactory
|
|
@ -166,7 +166,7 @@ public class TwitterIdentityProvider extends AbstractIdentityProvider<OAuth2Iden
|
|||
private ClientSessionCode parseClientSessionCode(String code) {
|
||||
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();
|
||||
|
||||
if (clientSession != null) {
|
||||
|
|
|
@ -1,437 +1,437 @@
|
|||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.BrowserSecurityHeaders;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.util.Time;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LoginTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
|
||||
user.setEmail("login@test.com");
|
||||
user.setEnabled(true);
|
||||
|
||||
userId = user.getId();
|
||||
|
||||
UserCredentialModel creds = new UserCredentialModel();
|
||||
creds.setType(CredentialRepresentation.PASSWORD);
|
||||
creds.setValue("password");
|
||||
|
||||
user.updateCredential(creds);
|
||||
}
|
||||
});
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(keycloakRule);
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected AppPage appPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordUpdatePage updatePasswordPage;
|
||||
|
||||
private static String userId;
|
||||
|
||||
@Test
|
||||
public void testBrowserSecurityHeaders() {
|
||||
Client client = ClientBuilder.newClient();
|
||||
Response response = client.target(oauth.getLoginFormUrl()).request().get();
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
for (Map.Entry<String, String> entry : BrowserSecurityHeaders.defaultHeaders.entrySet()) {
|
||||
String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey());
|
||||
String headerValue = response.getHeaderString(headerName);
|
||||
Assert.assertNotNull(headerValue);
|
||||
Assert.assertEquals(headerValue, entry.getValue());
|
||||
}
|
||||
response.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidPassword() {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "invalid");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidPasswordDisabledUser() {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "invalid");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginDisabledUser() {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidUsername() {
|
||||
loginPage.open();
|
||||
loginPage.login("invalid", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccess() {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginPromptNone() {
|
||||
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
|
||||
|
||||
assertFalse(loginPage.isCurrent());
|
||||
assertTrue(appPage.isCurrent());
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).removeDetail(Details.USERNAME).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to more than one day to force password update
|
||||
// elapsedTime > timeToExpire
|
||||
Time.setOffset(86405);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
updatePasswordPage.changePassword("updatedPassword", "updatedPassword");
|
||||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
|
||||
UserModel user = manager.getSession().users().getUserByUsername("login-test", appRealm);
|
||||
UserCredentialModel cred = new UserCredentialModel();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
cred.setValue("password");
|
||||
user.updateCredential(cred);
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithoutForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to less than one day to avoid forced password update
|
||||
// elapsedTime < timeToExpire
|
||||
Time.setOffset(86205);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginNoTimeoutWithLongWait() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1700);
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginTimeout() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1850);
|
||||
|
||||
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();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginLoginHint() {
|
||||
String loginFormUrl = oauth.getLoginFormUrl() + "&login_hint=login-test";
|
||||
driver.navigate().to(loginFormUrl);
|
||||
|
||||
Assert.assertEquals("login-test", loginPage.getUsername());
|
||||
loginPage.login("password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithEmailSuccess() {
|
||||
loginPage.open();
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithRememberMe() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRememberMe(true);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
assertFalse(loginPage.isRememberMeChecked());
|
||||
loginPage.setRememberMe(true);
|
||||
assertTrue(loginPage.isRememberMeChecked());
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
Event loginEvent = events.expectLogin().user(userId)
|
||||
.detail(Details.USERNAME, "login-test")
|
||||
.detail(Details.REMEMBER_ME, "true")
|
||||
.assertEvent();
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
|
||||
// Expire session
|
||||
keycloakRule.removeUserSession(sessionId);
|
||||
|
||||
// Assert rememberMe checked and username/email prefilled
|
||||
loginPage.open();
|
||||
assertTrue(loginPage.isRememberMeChecked());
|
||||
Assert.assertEquals("login-test", loginPage.getUsername());
|
||||
|
||||
loginPage.setRememberMe(false);
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRememberMe(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginCancel() {
|
||||
loginPage.open();
|
||||
loginPage.cancel();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
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();
|
||||
}
|
||||
|
||||
// KEYCLOAK-1037
|
||||
@Test
|
||||
public void loginExpiredCode() {
|
||||
try {
|
||||
loginPage.open();
|
||||
Time.setOffset(5000);
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
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();
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* JBoss, Home of Professional Open Source.
|
||||
* Copyright 2012, Red Hat, Inc., and individual contributors
|
||||
* as indicated by the @author tags. See the copyright.txt file in the
|
||||
* distribution for a full listing of individual contributors.
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU Lesser General Public License as
|
||||
* published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this software; if not, write to the Free
|
||||
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||
*/
|
||||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Event;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.BrowserSecurityHeaders;
|
||||
import org.keycloak.models.PasswordPolicy;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.OAuthClient;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
import org.keycloak.testsuite.pages.LoginPasswordUpdatePage;
|
||||
import org.keycloak.testsuite.rule.KeycloakRule;
|
||||
import org.keycloak.testsuite.rule.WebResource;
|
||||
import org.keycloak.testsuite.rule.WebRule;
|
||||
import org.keycloak.util.Time;
|
||||
import org.openqa.selenium.WebDriver;
|
||||
|
||||
import javax.ws.rs.client.Client;
|
||||
import javax.ws.rs.client.ClientBuilder;
|
||||
import javax.ws.rs.core.Response;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class LoginTest {
|
||||
|
||||
@ClassRule
|
||||
public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
|
||||
user.setEmail("login@test.com");
|
||||
user.setEnabled(true);
|
||||
|
||||
userId = user.getId();
|
||||
|
||||
UserCredentialModel creds = new UserCredentialModel();
|
||||
creds.setType(CredentialRepresentation.PASSWORD);
|
||||
creds.setValue("password");
|
||||
|
||||
user.updateCredential(creds);
|
||||
}
|
||||
});
|
||||
|
||||
@Rule
|
||||
public AssertEvents events = new AssertEvents(keycloakRule);
|
||||
|
||||
@Rule
|
||||
public WebRule webRule = new WebRule(this);
|
||||
|
||||
@WebResource
|
||||
protected OAuthClient oauth;
|
||||
|
||||
@WebResource
|
||||
protected WebDriver driver;
|
||||
|
||||
@WebResource
|
||||
protected AppPage appPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPage loginPage;
|
||||
|
||||
@WebResource
|
||||
protected LoginPasswordUpdatePage updatePasswordPage;
|
||||
|
||||
private static String userId;
|
||||
|
||||
@Test
|
||||
public void testBrowserSecurityHeaders() {
|
||||
Client client = ClientBuilder.newClient();
|
||||
Response response = client.target(oauth.getLoginFormUrl()).request().get();
|
||||
Assert.assertEquals(200, response.getStatus());
|
||||
for (Map.Entry<String, String> entry : BrowserSecurityHeaders.defaultHeaders.entrySet()) {
|
||||
String headerName = BrowserSecurityHeaders.headerAttributeMap.get(entry.getKey());
|
||||
String headerValue = response.getHeaderString(headerName);
|
||||
Assert.assertNotNull(headerValue);
|
||||
Assert.assertEquals(headerValue, entry.getValue());
|
||||
}
|
||||
response.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidPassword() {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "invalid");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidPasswordDisabledUser() {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "invalid");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginDisabledUser() {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(false);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
} finally {
|
||||
keycloakRule.configure(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
session.users().getUserByUsername("login-test", appRealm).setEnabled(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginInvalidUsername() {
|
||||
loginPage.open();
|
||||
loginPage.login("invalid", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginSuccess() {
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginPromptNone() {
|
||||
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
|
||||
|
||||
assertFalse(loginPage.isCurrent());
|
||||
assertTrue(appPage.isCurrent());
|
||||
|
||||
loginPage.open();
|
||||
loginPage.login("login-test", "password");
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
driver.navigate().to(oauth.getLoginFormUrl().toString() + "&prompt=none");
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).removeDetail(Details.USERNAME).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to more than one day to force password update
|
||||
// elapsedTime > timeToExpire
|
||||
Time.setOffset(86405);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
updatePasswordPage.assertCurrent();
|
||||
|
||||
updatePasswordPage.changePassword("updatedPassword", "updatedPassword");
|
||||
|
||||
events.expectRequiredAction(EventType.UPDATE_PASSWORD).user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
|
||||
UserModel user = manager.getSession().users().getUserByUsername("login-test", appRealm);
|
||||
UserCredentialModel cred = new UserCredentialModel();
|
||||
cred.setType(CredentialRepresentation.PASSWORD);
|
||||
cred.setValue("password");
|
||||
user.updateCredential(cred);
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithoutForcePasswordChangePolicy() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy("forceExpiredPasswordChange(1)"));
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
// Setting offset to less than one day to avoid forced password update
|
||||
// elapsedTime < timeToExpire
|
||||
Time.setOffset(86205);
|
||||
|
||||
loginPage.open();
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setPasswordPolicy(new PasswordPolicy(null));
|
||||
}
|
||||
});
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginNoTimeoutWithLongWait() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1700);
|
||||
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent().getSessionId();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginTimeout() {
|
||||
try {
|
||||
loginPage.open();
|
||||
|
||||
Time.setOffset(1850);
|
||||
|
||||
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();
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginLoginHint() {
|
||||
String loginFormUrl = oauth.getLoginFormUrl() + "&login_hint=login-test";
|
||||
driver.navigate().to(loginFormUrl);
|
||||
|
||||
Assert.assertEquals("login-test", loginPage.getUsername());
|
||||
loginPage.login("password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).detail(Details.USERNAME, "login-test").assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithEmailSuccess() {
|
||||
loginPage.open();
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
|
||||
events.expectLogin().user(userId).assertEvent();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginWithRememberMe() {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRememberMe(true);
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
loginPage.open();
|
||||
assertFalse(loginPage.isRememberMeChecked());
|
||||
loginPage.setRememberMe(true);
|
||||
assertTrue(loginPage.isRememberMeChecked());
|
||||
loginPage.login("login-test", "password");
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
Assert.assertNotNull(oauth.getCurrentQuery().get(OAuth2Constants.CODE));
|
||||
Event loginEvent = events.expectLogin().user(userId)
|
||||
.detail(Details.USERNAME, "login-test")
|
||||
.detail(Details.REMEMBER_ME, "true")
|
||||
.assertEvent();
|
||||
String sessionId = loginEvent.getSessionId();
|
||||
|
||||
// Expire session
|
||||
keycloakRule.removeUserSession(sessionId);
|
||||
|
||||
// Assert rememberMe checked and username/email prefilled
|
||||
loginPage.open();
|
||||
assertTrue(loginPage.isRememberMeChecked());
|
||||
Assert.assertEquals("login-test", loginPage.getUsername());
|
||||
|
||||
loginPage.setRememberMe(false);
|
||||
} finally {
|
||||
keycloakRule.update(new KeycloakRule.KeycloakSetup() {
|
||||
@Override
|
||||
public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
|
||||
appRealm.setRememberMe(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loginCancel() {
|
||||
loginPage.open();
|
||||
loginPage.cancel();
|
||||
|
||||
Assert.assertEquals(RequestType.AUTH_RESPONSE, appPage.getRequestType());
|
||||
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();
|
||||
}
|
||||
|
||||
// KEYCLOAK-1037
|
||||
@Test
|
||||
public void loginExpiredCode() {
|
||||
try {
|
||||
loginPage.open();
|
||||
Time.setOffset(5000);
|
||||
loginPage.login("login@test.com", "password");
|
||||
|
||||
loginPage.assertCurrent();
|
||||
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();
|
||||
|
||||
} finally {
|
||||
Time.setOffset(0);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -117,14 +117,14 @@ public class UserSessionProviderTest {
|
|||
int time = clientSession.getTimestamp();
|
||||
assertEquals(null, clientSession.getAction());
|
||||
|
||||
clientSession.setAction(ClientSessionModel.Action.CODE_TO_TOKEN);
|
||||
clientSession.setAction(ClientSessionModel.Action.CODE_TO_TOKEN.name());
|
||||
clientSession.setTimestamp(time + 10);
|
||||
|
||||
kc.stopSession(session, true);
|
||||
session = kc.startSession();
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue