diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
old mode 100644
new mode 100755
index 9325a40964..27869eb1a5
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.3.0.Beta1.xml
@@ -1,6 +1,11 @@
-
+
+
+
+
+
+
@@ -24,5 +29,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ACTION = 0
+
+
+
+ ACTION = 1
+
+
+
+ ACTION = 2
+
+
+
+ ACTION = 3
+
+
+
+
+
+
-
\ No newline at end of file
+
diff --git a/connections/jpa/src/main/resources/META-INF/persistence.xml b/connections/jpa/src/main/resources/META-INF/persistence.xml
index 79b730a727..5bcb77ee99 100755
--- a/connections/jpa/src/main/resources/META-INF/persistence.xml
+++ b/connections/jpa/src/main/resources/META-INF/persistence.xml
@@ -28,6 +28,7 @@
org.keycloak.models.sessions.jpa.entities.ClientSessionEntity
org.keycloak.models.sessions.jpa.entities.ClientSessionRoleEntity
+ org.keycloak.models.sessions.jpa.entities.ClientSessionAuthStatusEntity
org.keycloak.models.sessions.jpa.entities.ClientSessionProtocolMapperEntity
org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity
org.keycloak.models.sessions.jpa.entities.UserSessionNoteEntity
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java
new file mode 100755
index 0000000000..0ca9322076
--- /dev/null
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticatorModel.java
@@ -0,0 +1,127 @@
+package org.keycloak.models;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+* @author Bill Burke
+* @version $Revision: 1 $
+*/
+public class AuthenticatorModel {
+
+ public enum Requirement {
+ REQUIRED,
+ OPTIONAL,
+ ALTERNATIVE
+ }
+
+ private String id;
+ private String alias;
+ private String providerId;
+ private boolean masterAuthenticator;
+ private boolean formBased;
+ private String inputPage;
+ private String actionUrl;
+ private String setupUrl;
+ private Requirement requirement;
+ private boolean userSetupAllowed;
+ private int priority;
+ private Map config = new HashMap();
+
+
+ public String getId() {
+ return id;
+ }
+
+ public void setId(String id) {
+ this.id = id;
+ }
+
+ public String getAlias() {
+ return alias;
+ }
+
+ public void setAlias(String alias) {
+ this.alias = alias;
+ }
+
+ public String getProviderId() {
+ return providerId;
+ }
+
+ public void setProviderId(String providerId) {
+ this.providerId = providerId;
+ }
+
+ public boolean isFormBased() {
+ return formBased;
+ }
+
+ public void setFormBased(boolean formBased) {
+ this.formBased = formBased;
+ }
+
+ public String getInputPage() {
+ return inputPage;
+ }
+
+ public void setInputPage(String inputPage) {
+ this.inputPage = inputPage;
+ }
+
+ public String getActionUrl() {
+ return actionUrl;
+ }
+
+ public void setActionUrl(String actionUrl) {
+ this.actionUrl = actionUrl;
+ }
+
+ public String getSetupUrl() {
+ return setupUrl;
+ }
+
+ public void setSetupUrl(String setupUrl) {
+ this.setupUrl = setupUrl;
+ }
+
+ public Requirement getRequirement() {
+ return requirement;
+ }
+
+ public void setRequirement(Requirement requirement) {
+ this.requirement = requirement;
+ }
+
+ public int getPriority() {
+ return priority;
+ }
+
+ public void setPriority(int priority) {
+ this.priority = priority;
+ }
+
+ public boolean isUserSetupAllowed() {
+ return userSetupAllowed;
+ }
+
+ public void setUserSetupAllowed(boolean userSetupAllowed) {
+ this.userSetupAllowed = userSetupAllowed;
+ }
+
+ public boolean isMasterAuthenticator() {
+ return masterAuthenticator;
+ }
+
+ public void setMasterAuthenticator(boolean masterAuthenticator) {
+ this.masterAuthenticator = masterAuthenticator;
+ }
+
+ public Map getConfig() {
+ return config;
+ }
+
+ public void setConfig(Map config) {
+ this.config = config;
+ }
+}
diff --git a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
index 7c197a34a1..2c66df3f4e 100755
--- a/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/ClientSessionModel.java
@@ -1,5 +1,6 @@
package org.keycloak.models;
+import java.util.Map;
import java.util.Set;
/**
@@ -31,6 +32,14 @@ public interface ClientSessionModel {
public Set getProtocolMappers();
public void setProtocolMappers(Set protocolMappers);
+ public Map getAuthenticators();
+ public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status);
+ public void setAuthenticatorStatus(Map status);
+ public UserModel getAuthenticatedUser();
+ public void setAuthenticatedUser(UserModel user);
+
+
+
/**
* Authentication request type, i.e. OAUTH, SAML 2.0, SAML 1.1, etc.
*
diff --git a/model/api/src/main/java/org/keycloak/models/UserModel.java b/model/api/src/main/java/org/keycloak/models/UserModel.java
index 6761920add..2088abce2d 100755
--- a/model/api/src/main/java/org/keycloak/models/UserModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserModel.java
@@ -35,8 +35,12 @@ public interface UserModel {
Map getAttributes();
- Set getRequiredActions();
-
+ Set getRequiredActions();
+
+ void addRequiredAction(String action);
+
+ void removeRequiredAction(String action);
+
void addRequiredAction(RequiredAction action);
void removeRequiredAction(RequiredAction action);
@@ -65,6 +69,14 @@ public interface UserModel {
void updateCredentialDirectly(UserCredentialValueModel cred);
+ /**
+ * Is the use configured to use this credential type
+ *
+ * @param type
+ * @return
+ */
+ boolean configuredForCredentialType(String type);
+
Set getRealmRoleMappings();
Set getClientRoleMappings(ClientModel app);
boolean hasRole(RoleModel role);
diff --git a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
index 1db82b6ff8..769fbcaef3 100755
--- a/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/UserSessionModel.java
@@ -1,6 +1,7 @@
package org.keycloak.models;
import java.util.List;
+import java.util.Map;
/**
* @author Stian Thorgersen
@@ -36,6 +37,13 @@ public interface UserSessionModel {
List getClientSessions();
+ public static enum AuthenticatorStatus {
+ SUCCESS,
+ SETUP_REQUIRED,
+ ATTEMPTED,
+ SKIPPED
+ }
+
public String getNote(String name);
public void setNote(String name, String value);
public void removeNote(String name);
diff --git a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
index d2a20988f8..064697b00a 100755
--- a/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
+++ b/model/api/src/main/java/org/keycloak/models/entities/UserEntity.java
@@ -24,7 +24,7 @@ public class UserEntity extends AbstractIdentifiableEntity {
private List roleIds;
private Map attributes;
- private List requiredActions;
+ private List requiredActions;
private List credentials = new ArrayList();
private List federatedIdentities;
private String federationLink;
@@ -109,11 +109,11 @@ public class UserEntity extends AbstractIdentifiableEntity {
this.attributes = attributes;
}
- public List getRequiredActions() {
+ public List getRequiredActions() {
return requiredActions;
}
- public void setRequiredActions(List requiredActions) {
+ public void setRequiredActions(List requiredActions) {
this.requiredActions = requiredActions;
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 6148f8ebed..84248fdada 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -56,8 +56,8 @@ public class ModelToRepresentation {
rep.setFederationLink(user.getFederationLink());
List reqActions = new ArrayList();
- for (UserModel.RequiredAction ra : user.getRequiredActions()){
- reqActions.add(ra.name());
+ for (String ra : user.getRequiredActions()){
+ reqActions.add(ra);
}
rep.setRequiredActions(reqActions);
diff --git a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
index 8ac8f5b1b6..f0256473f8 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/UserModelDelegate.java
@@ -73,10 +73,25 @@ public class UserModelDelegate implements UserModel {
}
@Override
- public Set getRequiredActions() {
+ public Set getRequiredActions() {
return delegate.getRequiredActions();
}
+ @Override
+ public void addRequiredAction(String action) {
+ delegate.addRequiredAction(action);
+ }
+
+ @Override
+ public void removeRequiredAction(String action) {
+ delegate.removeRequiredAction(action);
+ }
+
+ @Override
+ public boolean configuredForCredentialType(String type) {
+ return delegate.configuredForCredentialType(type);
+ }
+
@Override
public void addRequiredAction(RequiredAction action) {
delegate.addRequiredAction(action);
@@ -211,4 +226,5 @@ public class UserModelDelegate implements UserModel {
public boolean revokeConsentForClient(String clientId) {
return delegate.revokeConsentForClient(clientId);
}
+
}
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
index 6e622cca11..eb4277f621 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/UserAdapter.java
@@ -183,28 +183,53 @@ public class UserAdapter implements UserModel, Comparable {
}
@Override
- public Set getRequiredActions() {
- List requiredActions = user.getRequiredActions();
- if (requiredActions == null) requiredActions = new ArrayList();
+ public Set getRequiredActions() {
+ List requiredActions = user.getRequiredActions();
+ if (requiredActions == null) requiredActions = new ArrayList();
return new HashSet(requiredActions);
}
@Override
public void addRequiredAction(RequiredAction action) {
- List requiredActions = user.getRequiredActions();
- if (requiredActions == null) requiredActions = new ArrayList();
- if (!requiredActions.contains(action)) requiredActions.add(action);
+ String actionName = action.name();
+ addRequiredAction(actionName);
+ }
+
+ @Override
+ public void addRequiredAction(String actionName) {
+ List requiredActions = user.getRequiredActions();
+ if (requiredActions == null) requiredActions = new ArrayList<>();
+ if (!requiredActions.contains(actionName)) {
+ requiredActions.add(actionName);
+ }
user.setRequiredActions(requiredActions);
}
@Override
public void removeRequiredAction(RequiredAction action) {
- List requiredActions = user.getRequiredActions();
+ String actionName = action.name();
+ removeRequiredAction(actionName);
+ }
+
+ @Override
+ public void removeRequiredAction(String actionName) {
+ List requiredActions = user.getRequiredActions();
if (requiredActions == null) return;
- requiredActions.remove(action);
+ requiredActions.remove(actionName);
user.setRequiredActions(requiredActions);
}
+ @Override
+ public boolean configuredForCredentialType(String type) {
+ List creds = getCredentialsDirectly();
+ for (UserCredentialValueModel cred : creds) {
+ if (cred.getType().equals(type)) return true;
+ }
+ return false;
+ }
+
+
+
@Override
public boolean isTotp() {
return user.isTotp();
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
index 10336fd592..dc159ceb67 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/UserAdapter.java
@@ -102,7 +102,7 @@ public class UserAdapter implements UserModel {
}
@Override
- public Set getRequiredActions() {
+ public Set getRequiredActions() {
if (updated != null) return updated.getRequiredActions();
return cached.getRequiredActions();
}
@@ -119,6 +119,27 @@ public class UserAdapter implements UserModel {
updated.removeRequiredAction(action);
}
+ @Override
+ public void addRequiredAction(String action) {
+ getDelegateForUpdate();
+ updated.addRequiredAction(action);
+ }
+
+ @Override
+ public void removeRequiredAction(String action) {
+ getDelegateForUpdate();
+ updated.removeRequiredAction(action);
+ }
+
+ @Override
+ public boolean configuredForCredentialType(String type) {
+ List creds = getCredentialsDirectly();
+ for (UserCredentialValueModel cred : creds) {
+ if (cred.getType().equals(type)) return true;
+ }
+ return false;
+ }
+
@Override
public String getFirstName() {
if (updated != null) return updated.getFirstName();
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
index ca9a24c0ff..eb9d418cb7 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedUser.java
@@ -28,8 +28,8 @@ public class CachedUser {
private boolean enabled;
private boolean totp;
private String federationLink;
- private Map attributes = new HashMap();
- private Set requiredActions = new HashSet();
+ private Map attributes = new HashMap<>();
+ private Set requiredActions = new HashSet<>();
private Set roleMappings = new HashSet();
@@ -96,7 +96,7 @@ public class CachedUser {
return attributes;
}
- public Set getRequiredActions() {
+ public Set getRequiredActions() {
return requiredActions;
}
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
index 5f92104b26..7c9086bf3b 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/UserAdapter.java
@@ -139,8 +139,8 @@ public class UserAdapter implements UserModel {
}
@Override
- public Set getRequiredActions() {
- Set result = new HashSet();
+ public Set getRequiredActions() {
+ Set result = new HashSet<>();
for (UserRequiredActionEntity attr : user.getRequiredActions()) {
result.add(attr.getAction());
}
@@ -149,13 +149,19 @@ public class UserAdapter implements UserModel {
@Override
public void addRequiredAction(RequiredAction action) {
+ String actionName = action.name();
+ addRequiredAction(actionName);
+ }
+
+ @Override
+ public void addRequiredAction(String actionName) {
for (UserRequiredActionEntity attr : user.getRequiredActions()) {
- if (attr.getAction().equals(action)) {
+ if (attr.getAction().equals(actionName)) {
return;
}
}
UserRequiredActionEntity attr = new UserRequiredActionEntity();
- attr.setAction(action);
+ attr.setAction(actionName);
attr.setUser(user);
em.persist(attr);
user.getRequiredActions().add(attr);
@@ -163,16 +169,31 @@ public class UserAdapter implements UserModel {
@Override
public void removeRequiredAction(RequiredAction action) {
+ String actionName = action.name();
+ removeRequiredAction(actionName);
+ }
+
+ @Override
+ public void removeRequiredAction(String actionName) {
Iterator it = user.getRequiredActions().iterator();
while (it.hasNext()) {
UserRequiredActionEntity attr = it.next();
- if (attr.getAction().equals(action)) {
+ if (attr.getAction().equals(actionName)) {
it.remove();
em.remove(attr);
}
}
}
+ @Override
+ public boolean configuredForCredentialType(String type) {
+ List creds = getCredentialsDirectly();
+ for (UserCredentialValueModel cred : creds) {
+ if (cred.getType().equals(type)) return true;
+ }
+ return false;
+ }
+
@Override
public String getFirstName() {
return user.getFirstName();
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java
index 1818b75644..a583815d3d 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserRequiredActionEntity.java
@@ -33,14 +33,14 @@ public class UserRequiredActionEntity {
protected UserEntity user;
@Id
- @Column(name="ACTION")
- protected UserModel.RequiredAction action;
+ @Column(name="REQUIRED_ACTION")
+ protected String action;
- public UserModel.RequiredAction getAction() {
+ public String getAction() {
return action;
}
- public void setAction(UserModel.RequiredAction action) {
+ public void setAction(String action) {
this.action = action;
}
@@ -56,12 +56,12 @@ public class UserRequiredActionEntity {
protected UserEntity user;
- protected UserModel.RequiredAction action;
+ protected String action;
public Key() {
}
- public Key(UserEntity user, UserModel.RequiredAction action) {
+ public Key(UserEntity user, String action) {
this.user = user;
this.action = action;
}
@@ -70,7 +70,7 @@ public class UserRequiredActionEntity {
return user;
}
- public UserModel.RequiredAction getAction() {
+ public String getAction() {
return action;
}
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
index 1a4a9b5196..f7895f2992 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/UserAdapter.java
@@ -159,8 +159,8 @@ public class UserAdapter extends AbstractMongoAdapter implement
@Override
- public Set getRequiredActions() {
- Set result = new HashSet();
+ public Set getRequiredActions() {
+ Set result = new HashSet();
if (user.getRequiredActions() != null) {
result.addAll(user.getRequiredActions());
}
@@ -169,12 +169,24 @@ public class UserAdapter extends AbstractMongoAdapter implement
@Override
public void addRequiredAction(RequiredAction action) {
- getMongoStore().pushItemToList(user, "requiredActions", action, true, invocationContext);
+ String actionName = action.name();
+ addRequiredAction(actionName);
+ }
+
+ @Override
+ public void addRequiredAction(String actionName) {
+ getMongoStore().pushItemToList(user, "requiredActions", actionName, true, invocationContext);
}
@Override
public void removeRequiredAction(RequiredAction action) {
- getMongoStore().pullItemFromList(user, "requiredActions", action, invocationContext);
+ String actionName = action.name();
+ removeRequiredAction(actionName);
+ }
+
+ @Override
+ public void removeRequiredAction(String actionName) {
+ getMongoStore().pullItemFromList(user, "requiredActions", actionName, invocationContext);
}
@Override
@@ -320,6 +332,16 @@ public class UserAdapter extends AbstractMongoAdapter implement
return result;
}
+ @Override
+ public boolean configuredForCredentialType(String type) {
+ List creds = getCredentialsDirectly();
+ for (UserCredentialValueModel cred : creds) {
+ if (cred.getType().equals(type)) return true;
+ }
+ return false;
+ }
+
+
@Override
public void updateCredentialDirectly(UserCredentialValueModel credModel) {
CredentialEntity credentialEntity = getCredentialEntity(user, credModel.getType());
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
old mode 100644
new mode 100755
index cc2c8ccb75..ddf42d58f4
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/ClientSessionAdapter.java
@@ -5,11 +5,13 @@ import org.keycloak.models.ClientModel;
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.models.sessions.infinispan.entities.ClientSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.SessionEntity;
import java.util.HashMap;
+import java.util.Map;
import java.util.Set;
/**
@@ -165,5 +167,30 @@ public class ClientSessionAdapter implements ClientSessionModel {
void update() {
provider.getTx().replace(cache, entity.getId(), entity);
}
+ @Override
+ public Map getAuthenticators() {
+ return entity.getAuthenticatorStatus();
+ }
+
+ @Override
+ public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ entity.getAuthenticatorStatus().put(authenticator, status);
+
+ }
+
+ @Override
+ public void setAuthenticatorStatus(Map status) {
+ entity.setAuthenticatorStatus(status);
+ }
+
+ @Override
+ public UserModel getAuthenticatedUser() {
+ return session.users().getUserById(entity.getAuthUserId(), realm); }
+
+ @Override
+ public void setAuthenticatedUser(UserModel user) {
+ entity.setAuthUserId(user.getId());
+
+ }
}
diff --git a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
index 38d2ea56ac..cc8ce200ba 100755
--- a/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
+++ b/model/sessions-infinispan/src/main/java/org/keycloak/models/sessions/infinispan/entities/ClientSessionEntity.java
@@ -1,7 +1,9 @@
package org.keycloak.models.sessions.infinispan.entities;
import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.UserSessionModel;
+import java.util.HashMap;
import java.util.Map;
import java.util.Set;
@@ -27,6 +29,8 @@ public class ClientSessionEntity extends SessionEntity {
private Set roles;
private Set protocolMappers;
private Map notes;
+ private Map authenticatorStatus = new HashMap<>();
+ private String authUserId;
public String getClient() {
return client;
@@ -107,4 +111,20 @@ public class ClientSessionEntity extends SessionEntity {
public void setNotes(Map notes) {
this.notes = notes;
}
+
+ public Map getAuthenticatorStatus() {
+ return authenticatorStatus;
+ }
+
+ public void setAuthenticatorStatus(Map authenticatorStatus) {
+ this.authenticatorStatus = authenticatorStatus;
+ }
+
+ public String getAuthUserId() {
+ return authUserId;
+ }
+
+ public void setAuthUserId(String authUserId) {
+ this.authUserId = authUserId;
+ }
}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
index 74e795f830..68e3b53ee5 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/ClientSessionAdapter.java
@@ -4,6 +4,7 @@ import org.keycloak.models.ClientModel;
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.models.sessions.jpa.entities.ClientSessionEntity;
import org.keycloak.models.sessions.jpa.entities.ClientSessionNoteEntity;
@@ -14,6 +15,7 @@ import org.keycloak.models.sessions.jpa.entities.UserSessionEntity;
import javax.persistence.EntityManager;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Map;
import java.util.Set;
/**
@@ -210,4 +212,29 @@ public class ClientSessionAdapter implements ClientSessionModel {
}
}
}
+
+ @Override
+ public Map getAuthenticators() {
+ return null;
+ }
+
+ @Override
+ public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+
+ }
+
+ @Override
+ public void setAuthenticatorStatus(Map status) {
+
+ }
+
+ @Override
+ public UserModel getAuthenticatedUser() {
+ return null;
+ }
+
+ @Override
+ public void setAuthenticatedUser(UserModel user) {
+
+ }
}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java
new file mode 100755
index 0000000000..49afbab2f7
--- /dev/null
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionAuthStatusEntity.java
@@ -0,0 +1,111 @@
+package org.keycloak.models.sessions.jpa.entities;
+
+import org.keycloak.models.UserSessionModel;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.Id;
+import javax.persistence.IdClass;
+import javax.persistence.JoinColumn;
+import javax.persistence.ManyToOne;
+import javax.persistence.NamedQueries;
+import javax.persistence.NamedQuery;
+import javax.persistence.Table;
+import java.io.Serializable;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+@NamedQueries({
+ @NamedQuery(name = "removeClientSessionAuthStatusByUser", query="delete from ClientSessionAuthStatusEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and s.userId = :userId))"),
+ @NamedQuery(name = "removeClientSessionAuthStatusByClient", query="delete from ClientSessionAuthStatusEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.clientId = :clientId and c.realmId = :realmId)"),
+ @NamedQuery(name = "removeClientSessionAuthStatusByRealm", query="delete from ClientSessionAuthStatusEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.realmId = :realmId)"),
+ @NamedQuery(name = "removeClientSessionAuthStatusByExpired", query = "delete from ClientSessionAuthStatusEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IN (select s from UserSessionEntity s where s.realmId = :realmId and (s.started < :maxTime or s.lastSessionRefresh < :idleTime)))"),
+ @NamedQuery(name = "removeDetachedClientSessionAuthStatusByExpired", query = "delete from ClientSessionAuthStatusEntity r where r.clientSession IN (select c from ClientSessionEntity c where c.session IS NULL and c.realmId = :realmId and c.timestamp < :maxTime )")
+})
+@Table(name="CLIENT_SESSION_AUTH_STATUS")
+@Entity
+@IdClass(ClientSessionAuthStatusEntity.Key.class)
+public class ClientSessionAuthStatusEntity {
+
+ @Id
+ @ManyToOne(fetch= FetchType.LAZY)
+ @JoinColumn(name = "CLIENT_SESSION")
+ protected ClientSessionEntity clientSession;
+
+ @Id
+ @Column(name = "AUTHENTICATOR")
+ protected String authenticator;
+ @Column(name = "STATUS")
+ protected UserSessionModel.AuthenticatorStatus status;
+
+ public String getAuthenticator() {
+ return authenticator;
+ }
+
+ public void setAuthenticator(String authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ public UserSessionModel.AuthenticatorStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(UserSessionModel.AuthenticatorStatus status) {
+ this.status = status;
+ }
+
+ public ClientSessionEntity getClientSession() {
+ return clientSession;
+ }
+
+ public void setClientSession(ClientSessionEntity clientSession) {
+ this.clientSession = clientSession;
+ }
+
+ public static class Key implements Serializable {
+
+ protected ClientSessionEntity clientSession;
+
+ protected String authenticator;
+
+ public Key() {
+ }
+
+ public Key(ClientSessionEntity clientSession, String authenticator) {
+ this.clientSession = clientSession;
+ this.authenticator = authenticator;
+ }
+
+ public ClientSessionEntity getClientSession() {
+ return clientSession;
+ }
+
+ public String getAuthenticator() {
+ return authenticator;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Key key = (Key) o;
+
+ if (authenticator != null ? !authenticator.equals(key.authenticator) : key.authenticator != null) return false;
+ if (clientSession != null ? !clientSession.getId().equals(key.clientSession != null ? key.clientSession.getId() : null) : key.clientSession != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = clientSession != null ? clientSession.getId().hashCode() : 0;
+ result = 31 * result + (authenticator != null ? authenticator.hashCode() : 0);
+ return result;
+ }
+ }
+
+}
diff --git a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
index 6f0c535539..7cc9062b99 100755
--- a/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
+++ b/model/sessions-jpa/src/main/java/org/keycloak/models/sessions/jpa/entities/ClientSessionEntity.java
@@ -57,6 +57,9 @@ public class ClientSessionEntity {
@Column(name="ACTION")
protected ClientSessionModel.Action action;
+ @Column(name="AUTH_USER_ID")
+ protected String userId;
+
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="clientSession")
protected Collection roles = new ArrayList();
@@ -66,6 +69,9 @@ public class ClientSessionEntity {
@OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="clientSession")
protected Collection notes = new ArrayList();
+ @OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, mappedBy="clientSession")
+ protected Collection authanticatorStatus = new ArrayList<>();
+
public String getId() {
return id;
}
@@ -153,4 +159,20 @@ public class ClientSessionEntity {
public void setAuthMethod(String authMethod) {
this.authMethod = authMethod;
}
+
+ public Collection getAuthanticatorStatus() {
+ return authanticatorStatus;
+ }
+
+ public void setAuthanticatorStatus(Collection authanticatorStatus) {
+ this.authanticatorStatus = authanticatorStatus;
+ }
+
+ public String getUserId() {
+ return userId;
+ }
+
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
index daa6e3f572..cff4ca373d 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/ClientSessionAdapter.java
@@ -4,10 +4,12 @@ import org.keycloak.models.ClientModel;
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.models.sessions.mem.entities.ClientSessionEntity;
import org.keycloak.models.sessions.mem.entities.UserSessionEntity;
+import java.util.Map;
import java.util.Set;
/**
@@ -141,4 +143,30 @@ public class ClientSessionAdapter implements ClientSessionModel {
public void setAuthMethod(String method) {
entity.setAuthMethod(method);
}
+
+ @Override
+ public Map getAuthenticators() {
+ return entity.getAuthenticatorStatus();
+ }
+
+ @Override
+ public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ entity.getAuthenticatorStatus().put(authenticator, status);
+
+ }
+
+ @Override
+ public void setAuthenticatorStatus(Map status) {
+ entity.setAuthenticatorStatus(status);
+ }
+
+ @Override
+ public UserModel getAuthenticatedUser() {
+ return session.users().getUserById(entity.getAuthUserId(), realm); }
+
+ @Override
+ public void setAuthenticatedUser(UserModel user) {
+ entity.setAuthUserId(user.getId());
+
+ }
}
diff --git a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
index ca304966ec..9823570c4c 100755
--- a/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
+++ b/model/sessions-mem/src/main/java/org/keycloak/models/sessions/mem/entities/ClientSessionEntity.java
@@ -1,6 +1,7 @@
package org.keycloak.models.sessions.mem.entities;
import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.UserSessionModel;
import java.util.HashMap;
import java.util.Map;
@@ -14,6 +15,8 @@ public class ClientSessionEntity {
private String id;
private String clientId;
private String realmId;
+ private Map authenticatorStatus = new HashMap<>();
+ private String authUserId;
private UserSessionEntity session;
@@ -24,7 +27,7 @@ public class ClientSessionEntity {
private ClientSessionModel.Action action;
private Set roles;
private Set protocolMappers;
- private Map notes = new HashMap();
+ private Map notes = new HashMap<>();
public String getId() {
return id;
@@ -109,4 +112,20 @@ public class ClientSessionEntity {
public void setAuthMethod(String authMethod) {
this.authMethod = authMethod;
}
+
+ public String getAuthUserId() {
+ return authUserId;
+ }
+
+ public void setAuthUserId(String authUserId) {
+ this.authUserId = authUserId;
+ }
+
+ public Map getAuthenticatorStatus() {
+ return authenticatorStatus;
+ }
+
+ public void setAuthenticatorStatus(Map authenticatorStatus) {
+ this.authenticatorStatus = authenticatorStatus;
+ }
}
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
index 649ffcf400..e5fd346d55 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/ClientSessionAdapter.java
@@ -5,6 +5,7 @@ import org.keycloak.models.ClientModel;
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.models.sessions.mongo.entities.MongoClientSessionEntity;
import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
@@ -12,6 +13,7 @@ import org.keycloak.models.sessions.mongo.entities.MongoUserSessionEntity;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -154,6 +156,37 @@ public class ClientSessionAdapter extends AbstractMongoAdapter getAuthenticators() {
+ return entity.getAuthenticatorStatus();
+ }
+
+ @Override
+ public void setAuthenticatorStatus(String authenticator, UserSessionModel.AuthenticatorStatus status) {
+ entity.getAuthenticatorStatus().put(authenticator, status);
+ updateMongoEntity();
+
+ }
+
+ @Override
+ public void setAuthenticatorStatus(Map status) {
+ entity.setAuthenticatorStatus(status);
+ updateMongoEntity();
+
+ }
+
+ @Override
+ public UserModel getAuthenticatedUser() {
+ return session.users().getUserById(entity.getAuthUserId(), realm);
+ }
+
+ @Override
+ public void setAuthenticatedUser(UserModel user) {
+ entity.setAuthUserId(user.getId());
+ updateMongoEntity();
+
+ }
+
@Override
public String getAuthMethod() {
return entity.getAuthMethod();
diff --git a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
index b5abede7d9..ed8099dd90 100755
--- a/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
+++ b/model/sessions-mongo/src/main/java/org/keycloak/models/sessions/mongo/entities/MongoClientSessionEntity.java
@@ -4,6 +4,7 @@ import org.keycloak.connections.mongo.api.MongoCollection;
import org.keycloak.connections.mongo.api.MongoIdentifiableEntity;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.UserSessionModel;
import org.keycloak.models.entities.AbstractIdentifiableEntity;
import java.util.HashMap;
@@ -29,6 +30,8 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
private List roles;
private List protocolMappers;
private Map notes = new HashMap();
+ private Map authenticatorStatus = new HashMap<>();
+ private String authUserId;
public String getId() {
return id;
@@ -118,6 +121,22 @@ public class MongoClientSessionEntity extends AbstractIdentifiableEntity impleme
this.sessionId = sessionId;
}
+ public Map getAuthenticatorStatus() {
+ return authenticatorStatus;
+ }
+
+ public void setAuthenticatorStatus(Map authenticatorStatus) {
+ this.authenticatorStatus = authenticatorStatus;
+ }
+
+ public String getAuthUserId() {
+ return authUserId;
+ }
+
+ public void setAuthUserId(String authUserId) {
+ this.authUserId = authUserId;
+ }
+
@Override
public void afterRemove(MongoStoreInvocationContext context) {
}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
new file mode 100755
index 0000000000..6f630049b6
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticationProcessor.java
@@ -0,0 +1,358 @@
+package org.keycloak.authentication;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+import org.keycloak.events.EventBuilder;
+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.protocol.oidc.TokenManager;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.BruteForceProtector;
+
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriInfo;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+
+//
+// setup
+// cookie: master, alternative
+// CERT_AUTH: alternative
+// UserPassword: alternative
+// OTP: optional
+// CAPTHA: required
+//
+// scenario: username password
+// * cookie, attempted
+// * cert, attempated
+// * usernamepassord, doesn't see form, sets challenge to form
+//
+//
+//
+//
+//
+//
+//
+
+
+
+public class AuthenticationProcessor {
+ protected RealmModel realm;
+ protected UserSessionModel userSession;
+ protected ClientSessionModel clientSession;
+ protected ClientConnection connection;
+ protected UriInfo uriInfo;
+ protected KeycloakSession session;
+ protected List authenticators;
+ protected BruteForceProtector protector;
+ protected EventBuilder eventBuilder;
+ protected HttpRequest request;
+
+
+ public static enum Status {
+ SUCCESS,
+ CHALLENGE,
+ FAILURE_CHALLENGE,
+ FAILED,
+ ATTEMPTED
+
+ }
+ public static enum Error {
+ INVALID_USER,
+ INVALID_CREDENTIALS,
+ CREDENTIAL_SETUP_REQUIRED,
+ USER_DISABLED,
+ USER_CONFLICT,
+ USER_TEMPORARILY_DISABLED,
+ INTERNAL_ERROR,
+ UNKNOWN_USER
+ }
+
+ public RealmModel getRealm() {
+ return realm;
+ }
+
+ public ClientSessionModel getClientSession() {
+ return clientSession;
+ }
+
+ public ClientConnection getConnection() {
+ return connection;
+ }
+
+ public UriInfo getUriInfo() {
+ return uriInfo;
+ }
+
+ public KeycloakSession getSession() {
+ return session;
+ }
+
+
+ private class Result implements AuthenticatorContext {
+ AuthenticatorModel model;
+ Authenticator authenticator;
+ Status status;
+ Response challenge;
+ Error error;
+
+ private Result(AuthenticatorModel model, Authenticator authenticator) {
+ this.model = model;
+ this.authenticator = authenticator;
+ }
+
+ @Override
+ public AuthenticatorModel getModel() {
+ return model;
+ }
+
+ @Override
+ public void setModel(AuthenticatorModel model) {
+ this.model = model;
+ }
+
+ @Override
+ public Authenticator getAuthenticator() {
+ return authenticator;
+ }
+
+ @Override
+ public void setAuthenticator(Authenticator authenticator) {
+ this.authenticator = authenticator;
+ }
+
+ @Override
+ public Status getStatus() {
+ return status;
+ }
+
+ @Override
+ public void success() {
+ this.status = Status.SUCCESS;
+ }
+
+ @Override
+ public void failure(Error error) {
+ status = Status.FAILED;
+ this.error = error;
+
+ }
+
+ @Override
+ public void challenge(Response challenge) {
+ this.status = Status.CHALLENGE;
+ this.challenge = challenge;
+
+ }
+ @Override
+ public void failureChallenge(Error error, Response challenge) {
+ this.error = error;
+ this.status = Status.FAILURE_CHALLENGE;
+ this.challenge = challenge;
+
+ }
+
+ @Override
+ public void attempted() {
+ this.status = Status.ATTEMPTED;
+
+ }
+
+ @Override
+ public UserModel getUser() {
+ return getClientSession().getAuthenticatedUser();
+ }
+
+ @Override
+ public void setUser(UserModel user) {
+ UserModel previousUser = getUser();
+ if (previousUser != null && !user.getId().equals(previousUser.getId())) throw new AuthException(Error.USER_CONFLICT);
+ validateUser(user);
+ getClientSession().setAuthenticatedUser(user);
+ }
+
+ @Override
+ public RealmModel getRealm() {
+ return AuthenticationProcessor.this.getRealm();
+ }
+
+ @Override
+ public ClientSessionModel getClientSession() {
+ return AuthenticationProcessor.this.getClientSession();
+ }
+
+ @Override
+ public ClientConnection getConnection() {
+ return AuthenticationProcessor.this.getConnection();
+ }
+
+ @Override
+ public UriInfo getUriInfo() {
+ return AuthenticationProcessor.this.getUriInfo();
+ }
+
+ @Override
+ public KeycloakSession getSession() {
+ return AuthenticationProcessor.this.getSession();
+ }
+
+ @Override
+ public HttpRequest getHttpRequest() {
+ return AuthenticationProcessor.this.request;
+ }
+
+ @Override
+ public void attachUserSession(UserSessionModel userSession) {
+ AuthenticationProcessor.this.userSession = userSession;
+ }
+
+ @Override
+ public BruteForceProtector getProtector() {
+ return AuthenticationProcessor.this.protector;
+ }
+ }
+
+ public static class AuthException extends RuntimeException {
+ private Error error;
+
+ public AuthException(Error error) {
+ this.error = error;
+ }
+
+ public AuthException(String message, Error error) {
+ super(message);
+ this.error = error;
+ }
+
+ public AuthException(String message, Throwable cause, Error error) {
+ super(message, cause);
+ this.error = error;
+ }
+
+ public AuthException(Throwable cause, Error error) {
+ super(cause);
+ this.error = error;
+ }
+
+ public AuthException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace, Error error) {
+ super(message, cause, enableSuppression, writableStackTrace);
+ this.error = error;
+ }
+ }
+
+ public void logUserFailure() {
+
+ }
+
+ protected boolean isProcessed(UserSessionModel.AuthenticatorStatus status) {
+ return status == UserSessionModel.AuthenticatorStatus.SUCCESS || status == UserSessionModel.AuthenticatorStatus.SKIPPED
+ || status == UserSessionModel.AuthenticatorStatus.ATTEMPTED
+ || status == UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED;
+ }
+
+ public Response authenticate() {
+ UserModel authUser = clientSession.getAuthenticatedUser();
+ validateUser(authUser);
+ Response challenge = null;
+ Map previousAttempts = clientSession.getAuthenticators();
+ for (AuthenticatorModel model : authenticators) {
+ UserSessionModel.AuthenticatorStatus oldStatus = previousAttempts.get(model.getAlias());
+ if (isProcessed(oldStatus)) continue;
+
+ AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getProviderId());
+ Authenticator authenticator = factory.create(model);
+ if (authenticator.requiresUser() && authUser == null){
+ if ( authenticator.requiresUser()) {
+ if (challenge != null) return challenge;
+ throw new AuthException(Error.UNKNOWN_USER);
+ }
+ }
+ if (authUser != null && model.getRequirement() == AuthenticatorModel.Requirement.ALTERNATIVE) {
+ clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SKIPPED);
+ continue;
+ }
+ authUser = clientSession.getAuthenticatedUser();
+
+ if (authenticator.requiresUser() && authUser != null && !authenticator.configuredFor(authUser)) {
+ if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) {
+ if (model.isUserSetupAllowed()) {
+ clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
+ authUser.addRequiredAction(authenticator.getRequiredAction());
+
+ } else {
+ throw new AuthException(Error.CREDENTIAL_SETUP_REQUIRED);
+ }
+ }
+ continue;
+ }
+ Result context = new Result(model, authenticator);
+ authenticator.authenticate(context);
+ Status result = context.getStatus();
+ if (result == Status.SUCCESS){
+ clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SUCCESS);
+ if (model.isMasterAuthenticator()) return authenticationComplete();
+ continue;
+ } else if (result == Status.FAILED) {
+ throw new AuthException(context.error);
+ } else if (result == Status.CHALLENGE) {
+ if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) return context.challenge;
+ if (challenge != null) challenge = context.challenge;
+ continue;
+ } else if (result == Status.FAILURE_CHALLENGE) {
+ logUserFailure();
+ return context.challenge;
+ } else if (result == Status.ATTEMPTED) {
+ if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
+ clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
+ continue;
+ }
+ }
+
+ if (authUser == null) {
+ if (challenge != null) return challenge;
+ throw new AuthException(Error.UNKNOWN_USER);
+ }
+
+
+ return authenticationComplete();
+ }
+
+
+
+ public void validateUser(UserModel authenticatedUser) {
+ if (authenticatedUser != null) {
+ if (!clientSession.getAuthenticatedUser().isEnabled()) throw new AuthException(Error.USER_DISABLED);
+ }
+ if (realm.isBruteForceProtected()) {
+ if (protector.isTemporarilyDisabled(session, realm, authenticatedUser.getUsername())) {
+ throw new AuthException(Error.USER_TEMPORARILY_DISABLED);
+ }
+ }
+ }
+
+ protected Response authenticationComplete() {
+ if (userSession == null) { // if no authenticator attached a usersession
+ userSession = session.sessions().createUserSession(realm, clientSession.getAuthenticatedUser(), clientSession.getAuthenticatedUser().getUsername(), connection.getRemoteAddr(), "form", false, null, null);
+ userSession.setState(UserSessionModel.State.LOGGING_IN);
+ }
+ TokenManager.attachClientSession(userSession, clientSession);
+ return processRequiredActions();
+
+ }
+
+ public Response processRequiredActions() {
+ return AuthenticationManager.nextActionAfterAuthentication(session, userSession, clientSession, connection, request, uriInfo, eventBuilder);
+
+ }
+
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/Authenticator.java b/services/src/main/java/org/keycloak/authentication/Authenticator.java
new file mode 100755
index 0000000000..3d5c64eeba
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/Authenticator.java
@@ -0,0 +1,17 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.UserModel;
+import org.keycloak.provider.Provider;
+
+/**
+* @author Bill Burke
+* @version $Revision: 1 $
+*/
+public interface Authenticator extends Provider {
+ boolean requiresUser();
+ void authenticate(AuthenticatorContext context);
+ boolean configuredFor(UserModel user);
+ String getRequiredAction();
+
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
new file mode 100755
index 0000000000..162c9b649f
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorContext.java
@@ -0,0 +1,54 @@
+package org.keycloak.authentication;
+
+import org.jboss.resteasy.spi.HttpRequest;
+import org.keycloak.ClientConnection;
+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 Bill Burke
+ * @version $Revision: 1 $
+ */
+public interface AuthenticatorContext {
+ AuthenticatorModel getModel();
+
+ void setModel(AuthenticatorModel model);
+
+ Authenticator getAuthenticator();
+
+ void setAuthenticator(Authenticator authenticator);
+
+ AuthenticationProcessor.Status getStatus();
+
+ UserModel getUser();
+
+ void setUser(UserModel user);
+
+ RealmModel getRealm();
+
+ ClientSessionModel getClientSession();
+ void attachUserSession(UserSessionModel userSession);
+
+ ClientConnection getConnection();
+
+ UriInfo getUriInfo();
+
+ KeycloakSession getSession();
+
+ HttpRequest getHttpRequest();
+ BruteForceProtector getProtector();
+
+ void success();
+ void failure(AuthenticationProcessor.Error error);
+ void challenge(Response challenge);
+ void failureChallenge(AuthenticationProcessor.Error error, Response challenge);
+ void attempted();
+}
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
new file mode 100755
index 0000000000..28290fc22e
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorFactory.java
@@ -0,0 +1,13 @@
+package org.keycloak.authentication;
+
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.provider.ProviderFactory;
+
+/**
+* @author Bill Burke
+* @version $Revision: 1 $
+*/
+public interface AuthenticatorFactory extends ProviderFactory {
+ Authenticator create(AuthenticatorModel model);
+
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
new file mode 100755
index 0000000000..b508024828
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticator.java
@@ -0,0 +1,47 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.models.UserModel;
+import org.keycloak.services.managers.AuthenticationManager;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class CookieAuthenticator implements Authenticator {
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ AuthenticationManager.AuthResult authResult = AuthenticationManager.authenticateIdentityCookie(context.getSession(),
+ context.getRealm(), context.getUriInfo(), context.getConnection(), context.getHttpRequest().getHttpHeaders(), true);
+ if (authResult == null) {
+ context.attempted();
+ } else {
+ context.setUser(authResult.getUser());
+ context.attachUserSession(authResult.getSession());
+ context.success();
+ }
+
+ }
+
+ @Override
+ public boolean configuredFor(UserModel user) {
+ return true;
+ }
+
+ @Override
+ public String getRequiredAction() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
new file mode 100755
index 0000000000..ab50b3dc51
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/CookieAuthenticatorFactory.java
@@ -0,0 +1,45 @@
+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;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class CookieAuthenticatorFactory implements AuthenticatorFactory {
+ static CookieAuthenticator SINGLETON = new CookieAuthenticator();
+ @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 "auth-cookie";
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
new file mode 100755
index 0000000000..7cf5b71705
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormOTPAuthenticator.java
@@ -0,0 +1,71 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
+
+ public LoginFormOTPAuthenticator(AuthenticatorModel model) {
+ super(model);
+ }
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ if (!isActionUrl(context)) {
+ context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
+ return;
+ }
+ validateOTP(context);
+ }
+
+ public void validateOTP(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getFormParameters();
+ List credentials = new LinkedList<>();
+ String password = inputData.getFirst(CredentialRepresentation.TOTP);
+ if (password == null) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ credentials.add(UserCredentialModel.totp(password));
+ boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+ if (!valid) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return true;
+ }
+
+ @Override
+ public boolean configuredFor(UserModel user) {
+ return user.configuredForCredentialType(UserCredentialModel.TOTP);
+ }
+
+ @Override
+ public String getRequiredAction() {
+ return UserModel.RequiredAction.CONFIGURE_TOTP.name();
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
new file mode 100755
index 0000000000..1aae3b2445
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormPasswordAuthenticator.java
@@ -0,0 +1,71 @@
+package org.keycloak.authentication.authenticators;
+
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticator {
+
+ public LoginFormPasswordAuthenticator(AuthenticatorModel model) {
+ super(model);
+ }
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ if (!isActionUrl(context)) {
+ context.failure(AuthenticationProcessor.Error.INTERNAL_ERROR);
+ return;
+ }
+ validatePassword(context);
+ }
+
+ public void validatePassword(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getFormParameters();
+ List credentials = new LinkedList<>();
+ String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
+ if (password == null) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ credentials.add(UserCredentialModel.password(password));
+ boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+ if (!valid) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return true;
+ }
+
+ @Override
+ public boolean configuredFor(UserModel user) {
+ return false;
+ }
+
+ @Override
+ public String getRequiredAction() {
+ return UserModel.RequiredAction.UPDATE_PASSWORD.name();
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
new file mode 100755
index 0000000000..1d1f73f896
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/LoginFormUsernameAuthenticator.java
@@ -0,0 +1,121 @@
+package org.keycloak.authentication.authenticators;
+
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.LoginActionsService;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class LoginFormUsernameAuthenticator implements Authenticator {
+ protected AuthenticatorModel model;
+
+ public LoginFormUsernameAuthenticator(AuthenticatorModel model) {
+ this.model = model;
+ }
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ if (!isActionUrl(context)) {
+ MultivaluedMap formData = new MultivaluedMapImpl<>();
+ String loginHint = context.getClientSession().getNote(OIDCLoginProtocol.LOGIN_HINT_PARAM);
+ if (loginHint == null) {
+ loginHint = AuthenticationManager.getRememberMeUsername(context.getRealm(), context.getHttpRequest().getHttpHeaders());
+ if (loginHint != null) {
+ formData.add("rememberMe", "on");
+ }
+ }
+ if (loginHint != null) formData.add(AuthenticationManager.FORM_USERNAME, loginHint);
+ Response challengeResponse = challenge(context, formData);
+ context.challenge(challengeResponse);
+ return;
+ }
+ validateUser(context);
+ }
+
+ protected boolean isActionUrl(AuthenticatorContext context) {
+ URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
+ return expected.getPath().equals(context.getUriInfo().getPath());
+
+ }
+
+ @Override
+ public boolean requiresUser() {
+ return false;
+ }
+
+ protected Response challenge(AuthenticatorContext context, MultivaluedMap formData) {
+ LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode());
+
+ if (formData.size() > 0) forms.setFormData(formData);
+
+ return forms.createLogin();
+ }
+
+ public void validateUser(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getFormParameters();
+ String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
+ if (username == null) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return;
+ }
+ UserModel user = KeycloakModelUtils.findUserByNameOrEmail(context.getSession(), context.getRealm(), username);
+ if (invalidUser(context, user)) return;
+ context.setUser(user);
+ }
+
+ public boolean invalidUser(AuthenticatorContext context, UserModel user) {
+ if (user == null) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
+ return true;
+ }
+ if (!user.isEnabled()) {
+ context.failure(AuthenticationProcessor.Error.USER_DISABLED);
+ return true;
+ }
+ if (context.getRealm().isBruteForceProtected()) {
+ if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
+ context.failure(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public Response challenge(AuthenticatorContext context) {
+ MultivaluedMap formData = new MultivaluedMapImpl<>();
+ return challenge(context, formData);
+ }
+
+ @Override
+ public boolean configuredFor(UserModel user) {
+ return true;
+ }
+
+ @Override
+ public String getRequiredAction() {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
new file mode 100755
index 0000000000..7659c92a66
--- /dev/null
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/OTPFormAuthenticator.java
@@ -0,0 +1,97 @@
+package org.keycloak.authentication.authenticators;
+
+import org.jboss.resteasy.specimpl.MultivaluedMapImpl;
+import org.keycloak.authentication.AuthenticationProcessor;
+import org.keycloak.authentication.Authenticator;
+import org.keycloak.authentication.AuthenticatorContext;
+import org.keycloak.login.LoginFormsProvider;
+import org.keycloak.models.AuthenticatorModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.protocol.oidc.OIDCLoginProtocol;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.AuthenticationManager;
+import org.keycloak.services.managers.ClientSessionCode;
+import org.keycloak.services.resources.LoginActionsService;
+
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class OTPFormAuthenticator implements Authenticator {
+ protected AuthenticatorModel model;
+
+ public OTPFormAuthenticator(AuthenticatorModel model) {
+ this.model = model;
+ }
+
+ @Override
+ public void authenticate(AuthenticatorContext context) {
+ URI expected = LoginActionsService.authenticationFormProcessor(context.getUriInfo()).build(context.getRealm().getName());
+ if (!expected.getPath().equals(context.getUriInfo().getPath())) {
+ Response challengeResponse = challenge(context);
+ context.challenge(challengeResponse);
+ return;
+ }
+ validateOTP(context);
+ }
+
+ public void validateOTP(AuthenticatorContext context) {
+ MultivaluedMap inputData = context.getHttpRequest().getFormParameters();
+ List credentials = new LinkedList<>();
+ String password = inputData.getFirst(CredentialRepresentation.TOTP);
+ if (password == null) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ credentials.add(UserCredentialModel.totp(password));
+ boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
+ if (!valid) {
+ Response challengeResponse = challenge(context);
+ context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
+ return;
+ }
+ }
+
+
+ @Override
+ public boolean requiresUser() {
+ return true;
+ }
+
+ protected Response challenge(AuthenticatorContext context, MultivaluedMap formData) {
+ LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class)
+ .setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode());
+
+ if (formData.size() > 0) forms.setFormData(formData);
+
+ return forms.createLoginTotp();
+ }
+
+ public Response challenge(AuthenticatorContext context) {
+ MultivaluedMap formData = new MultivaluedMapImpl<>();
+ return challenge(context, formData);
+ }
+
+ @Override
+ public boolean configuredFor(UserModel user) {
+ return user.configuredForCredentialType(UserCredentialModel.TOTP);
+ }
+
+ @Override
+ public String getRequiredAction() {
+ return UserModel.RequiredAction.CONFIGURE_TOTP.name();
+ }
+
+ @Override
+ public void close() {
+
+ }
+}
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index be7291c8e4..60a49d5c8a 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -406,7 +406,7 @@ public class AuthenticationManager {
}
}
}
-
+ if (userSession.getState() != UserSessionModel.State.LOGGED_IN) userSession.setState(UserSessionModel.State.LOGGED_IN);
// refresh the cookies!
createLoginCookie(realm, userSession.getUser(), userSession, uriInfo, clientConnection);
if (userSession.isRememberMe()) createRememberMeCookie(realm, userSession.getUser().getUsername(), uriInfo, clientConnection);
@@ -434,12 +434,12 @@ public class AuthenticationManager {
event.detail(Details.CODE_ID, clientSession.getId());
- Set requiredActions = user.getRequiredActions();
+ Set requiredActions = user.getRequiredActions();
if (!requiredActions.isEmpty()) {
- Iterator i = user.getRequiredActions().iterator();
- UserModel.RequiredAction action = i.next();
+ Iterator i = user.getRequiredActions().iterator();
+ String action = i.next();
- if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL) && Validation.isEmpty(user.getEmail())) {
+ if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL.name()) && Validation.isEmpty(user.getEmail())) {
if (i.hasNext())
action = i.next();
else
@@ -447,16 +447,16 @@ public class AuthenticationManager {
}
if (action != null) {
- accessCode.setRequiredAction(action);
+ accessCode.setRequiredAction(RequiredAction.valueOf(action));
LoginFormsProvider loginFormsProvider = session.getProvider(LoginFormsProvider.class).setClientSessionCode(accessCode.getCode())
.setUser(user);
- if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL)) {
+ if (action.equals(UserModel.RequiredAction.VERIFY_EMAIL.name())) {
event.clone().event(EventType.SEND_VERIFY_EMAIL).detail(Details.EMAIL, user.getEmail()).success();
LoginActionsService.createActionCookie(realm, uriInfo, clientConnection, userSession.getId());
}
- return loginFormsProvider.createResponse(action);
+ return loginFormsProvider.createResponse(RequiredAction.valueOf(action));
}
}
diff --git a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
index 470494300f..6daf649a4c 100755
--- a/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
+++ b/services/src/main/java/org/keycloak/services/resources/LoginActionsService.java
@@ -117,6 +117,10 @@ public class LoginActionsService {
return loginActionsBaseUrl(baseUriBuilder);
}
+ public static UriBuilder authenticationFormProcessor(UriInfo uriInfo) {
+ return loginActionsBaseUrl(uriInfo).path("auth-form");
+ }
+
public static UriBuilder loginActionsBaseUrl(UriBuilder baseUriBuilder) {
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getLoginActionsService");
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
index b9b45a9244..1ad0207225 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/broker/AbstractIdentityProviderTest.java
@@ -151,7 +151,7 @@ public abstract class AbstractIdentityProviderTest {
UserModel federatedUser = assertSuccessfulAuthentication(identityProviderModel, "test-user-noemail", null);
- assertTrue(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL));
+ assertTrue(federatedUser.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
} finally {
getRealm().setVerifyEmail(false);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
index 25802429e8..5e1930e3ee 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/federation/AbstractKerberosTest.java
@@ -298,7 +298,7 @@ public abstract class AbstractKerberosTest {
Assert.assertEquals(user.getLastName(), expectedLastname);
if (updateProfileActionExpected) {
- Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(), user.getRequiredActions().iterator().next().name());
+ Assert.assertEquals(UserModel.RequiredAction.UPDATE_PROFILE.toString(), user.getRequiredActions().iterator().next());
} else {
Assert.assertTrue(user.getRequiredActions().isEmpty());
}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
index b70e3fff59..d9e1aa5d0e 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserModelTest.java
@@ -110,28 +110,28 @@ public class UserModelTest extends AbstractModelTest {
user = session.users().getUserByUsername("user", realm);
Assert.assertEquals(1, user.getRequiredActions().size());
- Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
user = session.users().getUserByUsername("user", realm);
Assert.assertEquals(1, user.getRequiredActions().size());
- Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
- user.addRequiredAction(RequiredAction.VERIFY_EMAIL);
+ user.addRequiredAction(RequiredAction.VERIFY_EMAIL.name());
user = session.users().getUserByUsername("user", realm);
Assert.assertEquals(2, user.getRequiredActions().size());
- Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP));
- Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.CONFIGURE_TOTP.name()));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
- user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP);
+ user.removeRequiredAction(RequiredAction.CONFIGURE_TOTP.name());
user = session.users().getUserByUsername("user", realm);
Assert.assertEquals(1, user.getRequiredActions().size());
- Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL));
+ Assert.assertTrue(user.getRequiredActions().contains(RequiredAction.VERIFY_EMAIL.name()));
- user.removeRequiredAction(RequiredAction.VERIFY_EMAIL);
+ user.removeRequiredAction(RequiredAction.VERIFY_EMAIL.name());
user = session.users().getUserByUsername("user", realm);
Assert.assertTrue(user.getRequiredActions().isEmpty());
@@ -142,9 +142,9 @@ public class UserModelTest extends AbstractModelTest {
Assert.assertEquals(expected.getFirstName(), actual.getFirstName());
Assert.assertEquals(expected.getLastName(), actual.getLastName());
- RequiredAction[] expectedRequiredActions = expected.getRequiredActions().toArray(new RequiredAction[expected.getRequiredActions().size()]);
+ String[] expectedRequiredActions = expected.getRequiredActions().toArray(new String[expected.getRequiredActions().size()]);
Arrays.sort(expectedRequiredActions);
- RequiredAction[] actualRequiredActions = actual.getRequiredActions().toArray(new RequiredAction[actual.getRequiredActions().size()]);
+ String[] actualRequiredActions = actual.getRequiredActions().toArray(new String[actual.getRequiredActions().size()]);
Arrays.sort(actualRequiredActions);
Assert.assertArrayEquals(expectedRequiredActions, actualRequiredActions);