Merge pull request #1279 from patriot1burke/master

auth spi data model
This commit is contained in:
Bill Burke 2015-05-22 17:03:45 -04:00
commit 57780b6520
33 changed files with 1990 additions and 109 deletions

View file

@ -22,6 +22,44 @@
<column name="REPRESENTATION" type="VARCHAR(25500)"/> <column name="REPRESENTATION" type="VARCHAR(25500)"/>
<column name="ERROR" type="VARCHAR(255)"/> <column name="ERROR" type="VARCHAR(255)"/>
</createTable> </createTable>
<createTable tableName="AUTHENTICATOR">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="PROVIDER_ID" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="AUTHENTICATION_FLOW">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="DESCRIPTION" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
</createTable>
<createTable tableName="AUTHENTICATION_EXECUTION">
<column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="ALIAS" type="VARCHAR(255)"/>
<column name="AUTHENTICATOR" type="VARCHAR(36)"/>
<column name="REALM_ID" type="VARCHAR(36)"/>
<column name="FLOW_ID" type="VARCHAR(36)"/>
<column name="REQUIREMENT" type="INT"/>
<column name="PRIORITY" type="INT"/>
<column name="USER_SETUP_ALLOWED" type="BOOLEAN" defaultValueBoolean="false"/>
<column name="AUTHENTICATOR_FLOW" type="BOOLEAN" defaultValueBoolean="false"/>
</createTable>
<createTable tableName="AUTHENTICATOR_CONFIG">
<column name="AUTHENTICATOR_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="CLOB"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<createTable tableName="USER_FEDERATION_MAPPER"> <createTable tableName="USER_FEDERATION_MAPPER">
<column name="ID" type="VARCHAR(36)"> <column name="ID" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
@ -58,7 +96,7 @@
</column> </column>
</addColumn> </addColumn>
<createTable tableName="CLIENT_SESSION_AUTH_STATUS"> <createTable tableName="CLIENT_SESSION_AUTH_STATUS">
<column name="AUTHENTICATOR" type="VARCHAR(32)"> <column name="AUTHENTICATOR" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="STATUS" type="INT"/> <column name="STATUS" type="INT"/>
@ -67,10 +105,10 @@
</column> </column>
</createTable> </createTable>
<addColumn tableName="CLIENT_SESSION"> <addColumn tableName="CLIENT_SESSION">
<column name="AUTH_USER_ID" type="VARCHAR(32)"/> <column name="AUTH_USER_ID" type="VARCHAR(36)"/>
</addColumn> </addColumn>
<addColumn tableName="USER_REQUIRED_ACTION"> <addColumn tableName="USER_REQUIRED_ACTION">
<column name="REQUIRED_ACTION" type="VARCHAR(32)"> <column name="REQUIRED_ACTION" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
</addColumn> </addColumn>
@ -91,6 +129,10 @@
<column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/> <column name="REQUIRED_ACTION" value="UPDATE_PASSWORD"/>
<where>ACTION = 3</where> <where>ACTION = 3</where>
</update> </update>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATOR_PK" tableName="AUTHENTICATOR"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATION_FLOW_PK" tableName="AUTHENTICATION_FLOW"/>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_AUTHENTICATION_EXECUTION_PK" tableName="AUTHENTICATION_EXECUTION"/>
<addPrimaryKey columnNames="AUTHENTICATOR_ID, NAME" constraintName="CONSTRAINT_AUTHENTICATOR_CONFIG_PK" tableName="AUTHENTICATOR_CONFIG"/>
<dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/> <dropPrimaryKey constraintName="CONSTRAINT_2" tableName="USER_REQUIRED_ACTION"/>
<dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/> <dropColumn tableName="USER_REQUIRED_ACTION" columnName="ACTION"/>
<addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/> <addPrimaryKey columnNames="REQUIRED_ACTION, USER_ID" constraintName="CONSTRAINT_REQUIRED_ACTION" tableName="USER_REQUIRED_ACTION"/>
@ -98,6 +140,10 @@
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/> <addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_FEDMAPPERPM" tableName="USER_FEDERATION_MAPPER"/>
<addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/> <addPrimaryKey columnNames="USER_FEDERATION_MAPPER_ID, NAME" constraintName="CONSTRAINT_FEDMAPPER_CFG_PM" tableName="USER_FEDERATION_MAPPER_CONFIG"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/> <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_SESSION_AUTH_STATUS" constraintName="AUTH_STATUS_CONSTRAINT" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATOR" constraintName="FK_AUTHENTICATOR_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_FLOW" constraintName="FK_AUTHENTICATION_FLOW_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTHENTICATION_EXECUTION_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="FLOW_ID" baseTableName="AUTHENTICATION_EXECUTION" constraintName="FK_AUTHENTICATION_EXECUTION_FLOW" referencedColumnNames="ID" referencedTableName="AUTHENTICATION_FLOW"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/> <addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_FEDPRV" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/> <addForeignKeyConstraint baseColumnNames="FEDERATION_PROVIDER_ID" baseTableName="USER_FEDERATION_MAPPER" constraintName="FK_FEDMAPPERPM_FEDPRV" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_PROVIDER"/>
<addForeignKeyConstraint baseColumnNames="USER_FEDERATION_MAPPER_ID" baseTableName="USER_FEDERATION_MAPPER_CONFIG" constraintName="FK_FEDMAPPER_CFG" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_MAPPER"/> <addForeignKeyConstraint baseColumnNames="USER_FEDERATION_MAPPER_ID" baseTableName="USER_FEDERATION_MAPPER_CONFIG" constraintName="FK_FEDMAPPER_CFG" referencedColumnNames="ID" referencedTableName="USER_FEDERATION_MAPPER"/>

View file

@ -25,6 +25,9 @@
<class>org.keycloak.models.jpa.entities.UserConsentEntity</class> <class>org.keycloak.models.jpa.entities.UserConsentEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class> <class>org.keycloak.models.jpa.entities.UserConsentRoleEntity</class>
<class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class> <class>org.keycloak.models.jpa.entities.UserConsentProtocolMapperEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticatorEntity</class>
<!-- JpaUserSessionProvider --> <!-- JpaUserSessionProvider -->
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class> <class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>

View file

@ -0,0 +1,83 @@
package org.keycloak.models;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationExecutionModel {
private String id;
private String authenticator;
private boolean autheticatorFlow;
private Requirement requirement;
private boolean userSetupAllowed;
private int priority;
private String parentFlow;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAuthenticator() {
return authenticator;
}
public void setAuthenticator(String authenticator) {
this.authenticator = authenticator;
}
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 String getParentFlow() {
return parentFlow;
}
public void setParentFlow(String parentFlow) {
this.parentFlow = parentFlow;
}
/**
* Is the referenced authenticator a flow?
*
* @return
*/
public boolean isAutheticatorFlow() {
return autheticatorFlow;
}
public void setAutheticatorFlow(boolean autheticatorFlow) {
this.autheticatorFlow = autheticatorFlow;
}
public enum Requirement {
REQUIRED,
OPTIONAL,
ALTERNATIVE
}
}

View file

@ -0,0 +1,35 @@
package org.keycloak.models;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationFlowModel {
private String id;
private String alias;
private String description;
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}

View file

@ -9,23 +9,9 @@ import java.util.Map;
*/ */
public class AuthenticatorModel { public class AuthenticatorModel {
public enum Requirement {
REQUIRED,
OPTIONAL,
ALTERNATIVE
}
private String id; private String id;
private String alias; private String alias;
private String providerId; 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<String, String> config = new HashMap<String, String>(); private Map<String, String> config = new HashMap<String, String>();
@ -53,70 +39,6 @@ public class AuthenticatorModel {
this.providerId = 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<String, String> getConfig() { public Map<String, String> getConfig() {
return config; return config;
} }

View file

@ -173,6 +173,25 @@ public interface RealmModel extends RoleContainerModel {
void setSmtpConfig(Map<String, String> smtpConfig); void setSmtpConfig(Map<String, String> smtpConfig);
List<AuthenticationFlowModel> getAuthenticationFlows();
AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model);
AuthenticationFlowModel getAuthenticationFlowById(String id);
void removeAuthenticationFlow(AuthenticationFlowModel model);
void updateAuthenticationFlow(AuthenticationFlowModel model);
List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId);
AuthenticationExecutionModel getAuthenticationExecutionById(String id);
AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model);
void updateAuthenticatorExecution(AuthenticationExecutionModel model);
void removeAuthenticatorExecution(AuthenticationExecutionModel model);
List<AuthenticatorModel> getAuthenticators();
AuthenticatorModel addAuthenticator(AuthenticatorModel model);
void updateAuthenticator(AuthenticatorModel model);
void removeAuthenticator(AuthenticatorModel model);
AuthenticatorModel getAuthenticatorById(String id);
List<IdentityProviderModel> getIdentityProviders(); List<IdentityProviderModel> getIdentityProviders();
IdentityProviderModel getIdentityProviderByAlias(String alias); IdentityProviderModel getIdentityProviderByAlias(String alias);
void addIdentityProvider(IdentityProviderModel identityProvider); void addIdentityProvider(IdentityProviderModel identityProvider);

View file

@ -0,0 +1,74 @@
package org.keycloak.models.entities;
import org.keycloak.models.AuthenticationExecutionModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationExecutionEntity {
protected String id;
protected String authenticator;
protected AuthenticationExecutionModel.Requirement requirement;
protected int priority;
private boolean userSetupAllowed;
private boolean autheticatorFlow;
private String parentFlow;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getAuthenticator() {
return authenticator;
}
public void setAuthenticator(String authenticator) {
this.authenticator = authenticator;
}
public AuthenticationExecutionModel.Requirement getRequirement() {
return requirement;
}
public void setRequirement(AuthenticationExecutionModel.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 isAutheticatorFlow() {
return autheticatorFlow;
}
public void setAutheticatorFlow(boolean autheticatorFlow) {
this.autheticatorFlow = autheticatorFlow;
}
public String getParentFlow() {
return parentFlow;
}
public void setParentFlow(String parentFlow) {
this.parentFlow = parentFlow;
}
}

View file

@ -0,0 +1,47 @@
package org.keycloak.models.entities;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationFlowEntity {
protected String id;
protected String alias;
protected String description;
List<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
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 getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public List<AuthenticationExecutionEntity> getExecutions() {
return executions;
}
public void setExecutions(List<AuthenticationExecutionEntity> executions) {
this.executions = executions;
}
}

View file

@ -0,0 +1,46 @@
package org.keycloak.models.entities;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticatorEntity {
protected String id;
protected String alias;
protected String providerId;
private Map<String, String> config;
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 Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View file

@ -74,6 +74,8 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List<String> supportedLocales = new ArrayList<String>(); private List<String> supportedLocales = new ArrayList<String>();
private String defaultLocale; private String defaultLocale;
private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>(); private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
private List<AuthenticatorEntity> authenticators = new ArrayList<>();
public String getName() { public String getName() {
@ -482,6 +484,22 @@ public class RealmEntity extends AbstractIdentifiableEntity {
public void setIdentityProviderMappers(List<IdentityProviderMapperEntity> identityProviderMappers) { public void setIdentityProviderMappers(List<IdentityProviderMapperEntity> identityProviderMappers) {
this.identityProviderMappers = identityProviderMappers; this.identityProviderMappers = identityProviderMappers;
} }
public List<AuthenticationFlowEntity> getAuthenticationFlows() {
return authenticationFlows;
}
public void setAuthenticationFlows(List<AuthenticationFlowEntity> authenticationFlows) {
this.authenticationFlows = authenticationFlows;
}
public List<AuthenticatorEntity> getAuthenticators() {
return authenticators;
}
public void setAuthenticators(List<AuthenticatorEntity> authenticators) {
this.authenticators = authenticators;
}
} }

View file

@ -18,6 +18,9 @@ package org.keycloak.models.file.adapter;
import org.keycloak.connections.file.InMemoryModel; import org.keycloak.connections.file.InMemoryModel;
import org.keycloak.enums.SslRequired; import org.keycloak.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
@ -31,6 +34,9 @@ import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderCreationEventImpl;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.entities.AuthenticationExecutionEntity;
import org.keycloak.models.entities.AuthenticationFlowEntity;
import org.keycloak.models.entities.AuthenticatorEntity;
import org.keycloak.models.entities.ClientEntity; import org.keycloak.models.entities.ClientEntity;
import org.keycloak.models.entities.IdentityProviderMapperEntity; import org.keycloak.models.entities.IdentityProviderMapperEntity;
import org.keycloak.models.entities.RealmEntity; import org.keycloak.models.entities.RealmEntity;
@ -1191,6 +1197,234 @@ public class RealmAdapter implements RealmModel {
return mapping; return mapping;
} }
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
if (flows.size() == 0) return Collections.EMPTY_LIST;
List<AuthenticationFlowModel> models = new LinkedList<>();
for (AuthenticationFlowEntity entity : flows) {
AuthenticationFlowModel model = entityToModel(entity);
models.add(model);
}
return models;
}
protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
AuthenticationFlowModel model = new AuthenticationFlowModel();
model.setId(entity.getId());
model.setAlias(entity.getAlias());
model.setDescription(entity.getDescription());
return model;
}
@Override
public AuthenticationFlowModel getAuthenticationFlowById(String id) {
for (AuthenticationFlowModel model : getAuthenticationFlows()) {
if (model.getId().equals(id)) return model;
}
return null;
}
protected AuthenticationFlowEntity getFlowEntity(String id) {
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
for (AuthenticationFlowEntity entity : flows) {
if (id.equals(entity.getId())) return entity;
}
return null;
}
@Override
public void removeAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity toDelete = getFlowEntity(model.getId());
if (toDelete == null) return;
realm.getAuthenticationFlows().remove(toDelete);
}
@Override
public void updateAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity toUpdate = getFlowEntity(model.getId());
if (toUpdate == null) return;
toUpdate.setAlias(model.getAlias());
toUpdate.setDescription(model.getDescription());
}
@Override
public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAlias(model.getAlias());
entity.setDescription(model.getDescription());
realm.getAuthenticationFlows().add(entity);
model.setId(entity.getId());
return model;
}
@Override
public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
AuthenticationFlowEntity flow = getFlowEntity(flowId);
if (flow == null) return Collections.EMPTY_LIST;
List<AuthenticationExecutionEntity> queryResult = flow.getExecutions();
List<AuthenticationExecutionModel> executions = new LinkedList<>();
for (AuthenticationExecutionEntity entity : queryResult) {
AuthenticationExecutionModel model = entityToModel(entity);
executions.add(model);
}
return executions;
}
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator());
model.setParentFlow(entity.getParentFlow());
model.setAutheticatorFlow(entity.isAutheticatorFlow());
return model;
}
@Override
public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
AuthenticationExecutionEntity execution = getAuthenticationExecutionEntity(id);
return entityToModel(execution);
}
public AuthenticationExecutionEntity getAuthenticationExecutionEntity(String id) {
List<AuthenticationFlowEntity> flows = realm.getAuthenticationFlows();
for (AuthenticationFlowEntity entity : flows) {
for (AuthenticationExecutionEntity exe : entity.getExecutions()) {
if (exe.getId().equals(id)) {
return exe;
}
}
}
return null;
}
@Override
public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAutheticatorFlow(model.isAutheticatorFlow());
AuthenticationFlowEntity flow = getFlowEntity(model.getId());
flow.getExecutions().add(entity);
model.setId(entity.getId());
return model;
}
@Override
public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = null;
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
if (exe.getId().equals(model.getId())) {
entity = exe;
}
}
if (entity == null) return;
entity.setAutheticatorFlow(model.isAutheticatorFlow());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
}
@Override
public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = null;
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
if (exe.getId().equals(model.getId())) {
entity = exe;
}
}
if (entity == null) return;
flow.getExecutions().remove(entity);
}
@Override
public List<AuthenticatorModel> getAuthenticators() {
List<AuthenticatorModel> authenticators = new LinkedList<>();
for (AuthenticatorEntity entity : realm.getAuthenticators()) {
authenticators.add(entityToModel(entity));
}
return authenticators;
}
@Override
public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity auth = new AuthenticatorEntity();
auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias());
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
realm.getAuthenticators().add(auth);
model.setId(auth.getId());
return model;
}
@Override
public void removeAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
if (entity == null) return;
realm.getAuthenticators().remove(entity);
}
@Override
public AuthenticatorModel getAuthenticatorById(String id) {
AuthenticatorEntity entity = getAuthenticatorEntity(id);
if (entity == null) return null;
return entityToModel(entity);
}
public AuthenticatorEntity getAuthenticatorEntity(String id) {
AuthenticatorEntity entity = null;
for (AuthenticatorEntity auth : realm.getAuthenticators()) {
if (auth.getId().equals(id)) {
entity = auth;
break;
}
}
return entity;
}
public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
AuthenticatorModel model = new AuthenticatorModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
}
@Override @Override
public Set<UserFederationMapperModel> getUserFederationMappers() { public Set<UserFederationMapperModel> getUserFederationMappers() {
Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>(); Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>();

View file

@ -2,6 +2,9 @@ package org.keycloak.models.cache;
import org.keycloak.Config; import org.keycloak.Config;
import org.keycloak.enums.SslRequired; import org.keycloak.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
@ -1015,4 +1018,104 @@ public class RealmAdapter implements RealmModel {
return null; return null;
} }
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
if (updated != null) return updated.getAuthenticationFlows();
List<AuthenticationFlowModel> models = new ArrayList<>();
models.addAll(cached.getAuthenticationFlows().values());
return models;
}
@Override
public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
getDelegateForUpdate();
return updated.addAuthenticationFlow(model);
}
@Override
public AuthenticationFlowModel getAuthenticationFlowById(String id) {
if (updated != null) return updated.getAuthenticationFlowById(id);
return cached.getAuthenticationFlows().get(id);
}
@Override
public void removeAuthenticationFlow(AuthenticationFlowModel model) {
getDelegateForUpdate();
updated.removeAuthenticationFlow(model);
}
@Override
public void updateAuthenticationFlow(AuthenticationFlowModel model) {
getDelegateForUpdate();
updated.updateAuthenticationFlow(model);
}
@Override
public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
if (updated != null) return updated.getAuthenticationExecutions(flowId);
List<AuthenticationExecutionModel> models = new ArrayList<>();
return cached.getAuthenticationExecutions().get(flowId);
}
@Override
public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
if (updated != null) return updated.getAuthenticationExecutionById(id);
return cached.getExecutionsById().get(id);
}
@Override
public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
getDelegateForUpdate();
return updated.addAuthenticatorExecution(model);
}
@Override
public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
getDelegateForUpdate();
updated.updateAuthenticatorExecution(model);
}
@Override
public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
getDelegateForUpdate();
updated.removeAuthenticatorExecution(model);
}
@Override
public List<AuthenticatorModel> getAuthenticators() {
if (updated != null) return updated.getAuthenticators();
List<AuthenticatorModel> models = new ArrayList<>();
models.addAll(cached.getAuthenticators().values());
return models;
}
@Override
public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
getDelegateForUpdate();
return updated.addAuthenticator(model);
}
@Override
public void updateAuthenticator(AuthenticatorModel model) {
getDelegateForUpdate();
updated.updateAuthenticator(model);
}
@Override
public void removeAuthenticator(AuthenticatorModel model) {
getDelegateForUpdate();
updated.removeAuthenticator(model);
}
@Override
public AuthenticatorModel getAuthenticatorById(String id) {
if (updated != null) return updated.getAuthenticatorById(id);
return cached.getAuthenticators().get(id);
}
} }

View file

@ -1,6 +1,9 @@
package org.keycloak.models.cache.entities; package org.keycloak.models.cache.entities;
import org.keycloak.enums.SslRequired; import org.keycloak.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
@ -76,6 +79,10 @@ public class CachedRealm {
private Map<String, String> browserSecurityHeaders = new HashMap<String, String>(); private Map<String, String> browserSecurityHeaders = new HashMap<String, String>();
private Map<String, String> smtpConfig = new HashMap<String, String>(); private Map<String, String> smtpConfig = new HashMap<String, String>();
private Map<String, AuthenticationFlowModel> authenticationFlows = new HashMap<>();
private Map<String, AuthenticatorModel> authenticators = new HashMap<>();
private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
private boolean eventsEnabled; private boolean eventsEnabled;
private long eventsExpiration; private long eventsExpiration;
@ -183,6 +190,16 @@ public class CachedRealm {
internationalizationEnabled = model.isInternationalizationEnabled(); internationalizationEnabled = model.isInternationalizationEnabled();
supportedLocales.addAll(model.getSupportedLocales()); supportedLocales.addAll(model.getSupportedLocales());
defaultLocale = model.getDefaultLocale(); defaultLocale = model.getDefaultLocale();
for (AuthenticationFlowModel flow : model.getAuthenticationFlows()) {
authenticationFlows.put(flow.getId(), flow);
for (AuthenticationExecutionModel execution : model.getAuthenticationExecutions(flow.getId())) {
authenticationExecutions.add(flow.getId(), execution);
executionsById.put(execution.getId(), execution);
}
}
for (AuthenticatorModel authenticator : model.getAuthenticators()) {
authenticators.put(authenticator.getId(), authenticator);
}
} }
@ -405,4 +422,20 @@ public class CachedRealm {
public MultivaluedHashMap<String, IdentityProviderMapperModel> getIdentityProviderMappers() { public MultivaluedHashMap<String, IdentityProviderMapperModel> getIdentityProviderMappers() {
return identityProviderMappers; return identityProviderMappers;
} }
public Map<String, AuthenticationFlowModel> getAuthenticationFlows() {
return authenticationFlows;
}
public Map<String, AuthenticatorModel> getAuthenticators() {
return authenticators;
}
public MultivaluedHashMap<String, AuthenticationExecutionModel> getAuthenticationExecutions() {
return authenticationExecutions;
}
public Map<String, AuthenticationExecutionModel> getExecutionsById() {
return executionsById;
}
} }

View file

@ -1,6 +1,9 @@
package org.keycloak.models.jpa; package org.keycloak.models.jpa;
import org.keycloak.enums.SslRequired; import org.keycloak.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
@ -13,6 +16,9 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderCreationEventImpl;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.jpa.entities.AuthenticationExecutionEntity;
import org.keycloak.models.jpa.entities.AuthenticationFlowEntity;
import org.keycloak.models.jpa.entities.AuthenticatorEntity;
import org.keycloak.models.jpa.entities.ClientEntity; import org.keycloak.models.jpa.entities.ClientEntity;
import org.keycloak.models.jpa.entities.IdentityProviderEntity; import org.keycloak.models.jpa.entities.IdentityProviderEntity;
import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity; import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
@ -1486,4 +1492,207 @@ public class RealmAdapter implements RealmModel {
return mapper; return mapper;
} }
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
TypedQuery<AuthenticationFlowEntity> query = em.createNamedQuery("getAuthenticationFlowsByRealm", AuthenticationFlowEntity.class);
query.setParameter("realm", realm);
List<AuthenticationFlowEntity> flows = query.getResultList();
if (flows.size() == 0) return Collections.EMPTY_LIST;
List<AuthenticationFlowModel> models = new LinkedList<>();
for (AuthenticationFlowEntity entity : flows) {
AuthenticationFlowModel model = entityToModel(entity);
models.add(model);
}
return models;
}
protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
AuthenticationFlowModel model = new AuthenticationFlowModel();
model.setId(entity.getId());
model.setAlias(entity.getAlias());
model.setDescription(entity.getDescription());
return model;
}
@Override
public AuthenticationFlowModel getAuthenticationFlowById(String id) {
AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, id);
if (entity == null) return null;
return entityToModel(entity);
}
@Override
public void removeAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, model.getId());
if (entity == null) return;
em.remove(entity);
em.flush();
}
@Override
public void updateAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity entity = em.find(AuthenticationFlowEntity.class, model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setDescription(model.getDescription());
}
@Override
public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAlias(model.getAlias());
entity.setDescription(model.getDescription());
entity.setRealm(realm);
realm.getAuthenticationFlows().add(entity);
em.persist(entity);
em.flush();
model.setId(entity.getId());
return model;
}
@Override
public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
TypedQuery<AuthenticationExecutionEntity> query = em.createNamedQuery("getAuthenticationExecutionsByFlow", AuthenticationExecutionEntity.class);
AuthenticationFlowEntity flow = em.getReference(AuthenticationFlowEntity.class, flowId);
query.setParameter("realm", realm);
query.setParameter("flow", flow);
List<AuthenticationExecutionEntity> queryResult = query.getResultList();
List<AuthenticationExecutionModel> executions = new LinkedList<>();
for (AuthenticationExecutionEntity entity : queryResult) {
AuthenticationExecutionModel model = entityToModel(entity);
executions.add(model);
}
return executions;
}
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator());
model.setParentFlow(entity.getFlow().getId());
model.setAutheticatorFlow(entity.isAutheticatorFlow());
return model;
}
@Override
public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, id);
if (entity == null) return null;
return entityToModel(entity);
}
@Override
public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
AuthenticationFlowEntity flow = em.find(AuthenticationFlowEntity.class, model.getParentFlow());
entity.setFlow(flow);
flow.getExecutions().add(entity);
entity.setRealm(realm);
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAutheticatorFlow(model.isAutheticatorFlow());
em.persist(entity);
em.flush();
model.setId(entity.getId());
return model;
}
@Override
public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
if (entity == null) return;
entity.setAutheticatorFlow(model.isAutheticatorFlow());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
em.flush();
}
@Override
public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
if (entity == null) return;
em.remove(entity);
em.flush();
}
@Override
public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity auth = new AuthenticatorEntity();
auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias());
auth.setRealm(realm);
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
realm.getAuthenticators().add(auth);
em.persist(auth);
em.flush();
model.setId(auth.getId());
return model;
}
@Override
public void removeAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, model.getId());
if (entity == null) return;
em.remove(entity);
em.flush();
}
@Override
public AuthenticatorModel getAuthenticatorById(String id) {
AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, id);
if (entity == null) return null;
return entityToModel(entity);
}
public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
AuthenticatorModel model = new AuthenticatorModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = em.find(AuthenticatorEntity.class, model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
em.flush();
}
@Override
public List<AuthenticatorModel> getAuthenticators() {
List<AuthenticatorModel> authenticators = new LinkedList<>();
for (AuthenticatorEntity entity : realm.getAuthenticators()) {
authenticators.add(entityToModel(entity));
}
return authenticators;
}
} }

View file

@ -0,0 +1,117 @@
package org.keycloak.models.jpa.entities;
import org.keycloak.models.AuthenticationExecutionModel;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Table(name="AUTHENTICATION_EXECUTION")
@Entity
@NamedQueries({
@NamedQuery(name="getAuthenticationExecutionsByFlow", query="select authenticator from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
@NamedQuery(name="deleteAuthenticationExecutionsByRealm", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm"),
@NamedQuery(name="deleteAuthenticationExecutionsByRealmAndFlow", query="delete from AuthenticationExecutionEntity authenticator where authenticator.realm = :realm and authenticator.flow = :flow"),
})
public class AuthenticationExecutionEntity {
@Id
@Column(name="ID", length = 36)
protected String id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID")
protected RealmEntity realm;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "FLOW_ID")
protected AuthenticationFlowEntity flow;
@Column(name="AUTHENTICATOR")
protected String authenticator;
@Column(name="REQUIREMENT")
protected AuthenticationExecutionModel.Requirement requirement;
@Column(name="PRIORITY")
protected int priority;
@Column(name="USER_SETUP_ALLOWED")
private boolean userSetupAllowed;
@Column(name="AUTHENTICATOR_FLOW")
private boolean autheticatorFlow;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public RealmEntity getRealm() {
return realm;
}
public void setRealm(RealmEntity realm) {
this.realm = realm;
}
public String getAuthenticator() {
return authenticator;
}
public void setAuthenticator(String authenticator) {
this.authenticator = authenticator;
}
public AuthenticationExecutionModel.Requirement getRequirement() {
return requirement;
}
public void setRequirement(AuthenticationExecutionModel.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 isAutheticatorFlow() {
return autheticatorFlow;
}
public void setAutheticatorFlow(boolean autheticatorFlow) {
this.autheticatorFlow = autheticatorFlow;
}
public AuthenticationFlowEntity getFlow() {
return flow;
}
public void setFlow(AuthenticationFlowEntity flow) {
this.flow = flow;
}
}

View file

@ -0,0 +1,89 @@
package org.keycloak.models.jpa.entities;
import org.keycloak.models.AuthenticatorModel;
import javax.persistence.CascadeType;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Table(name="AUTHENTICATION_FLOW")
@Entity
@NamedQueries({
@NamedQuery(name="getAuthenticationFlowsByRealm", query="select flow from AuthenticationFlowEntity flow where flow.realm = :realm"),
@NamedQuery(name="deleteAuthenticationFlowByRealm", query="delete from AuthenticationFlowEntity flow where flow.realm = :realm")
})
public class AuthenticationFlowEntity {
@Id
@Column(name="ID", length = 36)
protected String id;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID")
protected RealmEntity realm;
@Column(name="ALIAS")
protected String alias;
@Column(name="DESCRIPTION")
protected String description;
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "flow")
Collection<AuthenticationExecutionEntity> executions = new ArrayList<AuthenticationExecutionEntity>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public RealmEntity getRealm() {
return realm;
}
public void setRealm(RealmEntity realm) {
this.realm = realm;
}
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public Collection<AuthenticationExecutionEntity> getExecutions() {
return executions;
}
public void setExecutions(Collection<AuthenticationExecutionEntity> executions) {
this.executions = executions;
}
}

View file

@ -0,0 +1,89 @@
package org.keycloak.models.jpa.entities;
import org.keycloak.models.AuthenticatorModel;
import javax.persistence.CollectionTable;
import javax.persistence.Column;
import javax.persistence.ElementCollection;
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.MapKeyColumn;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import java.io.Serializable;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Table(name="AUTHENTICATOR")
@Entity
@NamedQueries({
@NamedQuery(name="deleteAuthenticatorsByRealm", query="delete from AuthenticatorEntity authenticator where authenticator.realm = :realm"),})
public class AuthenticatorEntity {
@Id
@Column(name="ID", length = 36)
protected String id;
@Column(name="ALIAS")
protected String alias;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID")
protected RealmEntity realm;
@Column(name="PROVIDER_ID")
protected String providerId;
@ElementCollection
@MapKeyColumn(name="NAME")
@Column(name="VALUE")
@CollectionTable(name="AUTHENTICATOR_CONFIG", joinColumns={ @JoinColumn(name="AUTHENTICATOR_ID") })
private Map<String, String> config;
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 RealmEntity getRealm() {
return realm;
}
public void setRealm(RealmEntity realm) {
this.realm = realm;
}
public Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}

View file

@ -154,6 +154,12 @@ public class RealmEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>(); Collection<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<AuthenticatorEntity> authenticators = new ArrayList<>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
@Column(name="INTERNATIONALIZATION_ENABLED") @Column(name="INTERNATIONALIZATION_ENABLED")
@ -546,5 +552,21 @@ public class RealmEntity {
public void setIdentityProviderMappers(Collection<IdentityProviderMapperEntity> identityProviderMappers) { public void setIdentityProviderMappers(Collection<IdentityProviderMapperEntity> identityProviderMappers) {
this.identityProviderMappers = identityProviderMappers; this.identityProviderMappers = identityProviderMappers;
} }
public Collection<AuthenticatorEntity> getAuthenticators() {
return authenticators;
}
public void setAuthenticators(Collection<AuthenticatorEntity> authenticators) {
this.authenticators = authenticators;
}
public Collection<AuthenticationFlowEntity> getAuthenticationFlows() {
return authenticationFlows;
}
public void setAuthenticationFlows(Collection<AuthenticationFlowEntity> authenticationFlows) {
this.authenticationFlows = authenticationFlows;
}
} }

View file

@ -5,6 +5,9 @@ import com.mongodb.QueryBuilder;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.enums.SslRequired; import org.keycloak.enums.SslRequired;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.IdentityProviderMapperModel; import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
@ -18,6 +21,9 @@ import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationMapperModel;
import org.keycloak.models.UserFederationProviderCreationEventImpl; import org.keycloak.models.UserFederationProviderCreationEventImpl;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.entities.AuthenticationExecutionEntity;
import org.keycloak.models.entities.AuthenticationFlowEntity;
import org.keycloak.models.entities.AuthenticatorEntity;
import org.keycloak.models.entities.IdentityProviderEntity; import org.keycloak.models.entities.IdentityProviderEntity;
import org.keycloak.models.entities.IdentityProviderMapperEntity; import org.keycloak.models.entities.IdentityProviderMapperEntity;
import org.keycloak.models.entities.RequiredCredentialEntity; import org.keycloak.models.entities.RequiredCredentialEntity;
@ -1178,7 +1184,6 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
this.realm.getIdentityProviderMappers().remove(toDelete); this.realm.getIdentityProviderMappers().remove(toDelete);
updateMongoEntity(); updateMongoEntity();
} }
} }
@Override @Override
@ -1222,6 +1227,243 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
return mapping; return mapping;
} }
@Override
public List<AuthenticationFlowModel> getAuthenticationFlows() {
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
if (flows.size() == 0) return Collections.EMPTY_LIST;
List<AuthenticationFlowModel> models = new LinkedList<>();
for (AuthenticationFlowEntity entity : flows) {
AuthenticationFlowModel model = entityToModel(entity);
models.add(model);
}
return models;
}
protected AuthenticationFlowModel entityToModel(AuthenticationFlowEntity entity) {
AuthenticationFlowModel model = new AuthenticationFlowModel();
model.setId(entity.getId());
model.setAlias(entity.getAlias());
model.setDescription(entity.getDescription());
return model;
}
@Override
public AuthenticationFlowModel getAuthenticationFlowById(String id) {
for (AuthenticationFlowModel model : getAuthenticationFlows()) {
if (model.getId().equals(id)) return model;
}
return null;
}
protected AuthenticationFlowEntity getFlowEntity(String id) {
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
for (AuthenticationFlowEntity entity : flows) {
if (id.equals(entity.getId())) return entity;
}
return null;
}
@Override
public void removeAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity toDelete = getFlowEntity(model.getId());
if (toDelete == null) return;
getMongoEntity().getAuthenticationFlows().remove(toDelete);
updateMongoEntity();
}
@Override
public void updateAuthenticationFlow(AuthenticationFlowModel model) {
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
AuthenticationFlowEntity toUpdate = getFlowEntity(model.getId());;
if (toUpdate == null) return;
toUpdate.setAlias(model.getAlias());
toUpdate.setDescription(model.getDescription());
updateMongoEntity();
}
@Override
public AuthenticationFlowModel addAuthenticationFlow(AuthenticationFlowModel model) {
AuthenticationFlowEntity entity = new AuthenticationFlowEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAlias(model.getAlias());
entity.setDescription(model.getDescription());
getMongoEntity().getAuthenticationFlows().add(entity);
model.setId(entity.getId());
updateMongoEntity();
return model;
}
@Override
public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
AuthenticationFlowEntity flow = getFlowEntity(flowId);
if (flow == null) return Collections.EMPTY_LIST;
List<AuthenticationExecutionEntity> queryResult = flow.getExecutions();
List<AuthenticationExecutionModel> executions = new LinkedList<>();
for (AuthenticationExecutionEntity entity : queryResult) {
AuthenticationExecutionModel model = entityToModel(entity);
executions.add(model);
}
return executions;
}
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator());
model.setParentFlow(entity.getParentFlow());
model.setAutheticatorFlow(entity.isAutheticatorFlow());
return model;
}
@Override
public AuthenticationExecutionModel getAuthenticationExecutionById(String id) {
AuthenticationExecutionEntity execution = getAuthenticationExecutionEntity(id);
return entityToModel(execution);
}
public AuthenticationExecutionEntity getAuthenticationExecutionEntity(String id) {
List<AuthenticationFlowEntity> flows = getMongoEntity().getAuthenticationFlows();
for (AuthenticationFlowEntity entity : flows) {
for (AuthenticationExecutionEntity exe : entity.getExecutions()) {
if (exe.getId().equals(id)) {
return exe;
}
}
}
return null;
}
@Override
public AuthenticationExecutionModel addAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = new AuthenticationExecutionEntity();
entity.setId(KeycloakModelUtils.generateId());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAutheticatorFlow(model.isAutheticatorFlow());
AuthenticationFlowEntity flow = getFlowEntity(model.getId());
flow.getExecutions().add(entity);
updateMongoEntity();
model.setId(entity.getId());
return model;
}
@Override
public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = null;
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
if (exe.getId().equals(model.getId())) {
entity = exe;
}
}
if (entity == null) return;
entity.setAutheticatorFlow(model.isAutheticatorFlow());
entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
updateMongoEntity();
}
@Override
public void removeAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = null;
AuthenticationFlowEntity flow = getFlowEntity(model.getParentFlow());
for (AuthenticationExecutionEntity exe : flow.getExecutions()) {
if (exe.getId().equals(model.getId())) {
entity = exe;
}
}
if (entity == null) return;
flow.getExecutions().remove(entity);
updateMongoEntity();
}
@Override
public List<AuthenticatorModel> getAuthenticators() {
List<AuthenticatorModel> authenticators = new LinkedList<>();
for (AuthenticatorEntity entity : getMongoEntity().getAuthenticators()) {
authenticators.add(entityToModel(entity));
}
return authenticators;
}
@Override
public AuthenticatorModel addAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity auth = new AuthenticatorEntity();
auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias());
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
realm.getAuthenticators().add(auth);
model.setId(auth.getId());
updateMongoEntity();
return model;
}
@Override
public void removeAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
if (entity == null) return;
getMongoEntity().getAuthenticators().remove(entity);
updateMongoEntity();
}
@Override
public AuthenticatorModel getAuthenticatorById(String id) {
AuthenticatorEntity entity = getAuthenticatorEntity(id);
if (entity == null) return null;
return entityToModel(entity);
}
public AuthenticatorEntity getAuthenticatorEntity(String id) {
AuthenticatorEntity entity = null;
for (AuthenticatorEntity auth : getMongoEntity().getAuthenticators()) {
if (auth.getId().equals(id)) {
entity = auth;
break;
}
}
return entity;
}
public AuthenticatorModel entityToModel(AuthenticatorEntity entity) {
AuthenticatorModel model = new AuthenticatorModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateAuthenticator(AuthenticatorModel model) {
AuthenticatorEntity entity = getAuthenticatorEntity(model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
updateMongoEntity();
}
@Override @Override
public Set<UserFederationMapperModel> getUserFederationMappers() { public Set<UserFederationMapperModel> getUserFederationMappers() {
Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>(); Set<UserFederationMapperModel> mappers = new HashSet<UserFederationMapperModel>();

View file

@ -3,6 +3,7 @@ package org.keycloak.authentication;
import org.jboss.resteasy.spi.HttpRequest; import org.jboss.resteasy.spi.HttpRequest;
import org.keycloak.ClientConnection; import org.keycloak.ClientConnection;
import org.keycloak.events.EventBuilder; import org.keycloak.events.EventBuilder;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorModel; import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
@ -52,7 +53,7 @@ public class AuthenticationProcessor {
protected ClientConnection connection; protected ClientConnection connection;
protected UriInfo uriInfo; protected UriInfo uriInfo;
protected KeycloakSession session; protected KeycloakSession session;
protected List<AuthenticatorModel> authenticators; protected List<AuthenticationExecutionModel> executions;
protected BruteForceProtector protector; protected BruteForceProtector protector;
protected EventBuilder eventBuilder; protected EventBuilder eventBuilder;
protected HttpRequest request; protected HttpRequest request;
@ -159,6 +160,13 @@ public class AuthenticationProcessor {
this.status = Status.FAILURE_CHALLENGE; this.status = Status.FAILURE_CHALLENGE;
this.challenge = challenge; this.challenge = challenge;
}
@Override
public void failure(Error error, Response challenge) {
this.error = error;
this.status = Status.FAILED;
this.challenge = challenge;
} }
@Override @Override
@ -264,28 +272,29 @@ public class AuthenticationProcessor {
validateUser(authUser); validateUser(authUser);
Response challenge = null; Response challenge = null;
Map<String, UserSessionModel.AuthenticatorStatus> previousAttempts = clientSession.getAuthenticators(); Map<String, UserSessionModel.AuthenticatorStatus> previousAttempts = clientSession.getAuthenticators();
for (AuthenticatorModel model : authenticators) { for (AuthenticationExecutionModel model : executions) {
UserSessionModel.AuthenticatorStatus oldStatus = previousAttempts.get(model.getAlias()); UserSessionModel.AuthenticatorStatus oldStatus = previousAttempts.get(model.getId());
if (isProcessed(oldStatus)) continue; if (isProcessed(oldStatus)) continue;
AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, model.getProviderId()); AuthenticatorModel authenticatorModel = realm.getAuthenticatorById(model.getAuthenticator());
Authenticator authenticator = factory.create(model); AuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, authenticatorModel.getProviderId());
Authenticator authenticator = factory.create(authenticatorModel);
if (authenticator.requiresUser() && authUser == null){ if (authenticator.requiresUser() && authUser == null){
if ( authenticator.requiresUser()) { if ( authenticator.requiresUser()) {
if (challenge != null) return challenge; if (challenge != null) return challenge;
throw new AuthException(Error.UNKNOWN_USER); throw new AuthException(Error.UNKNOWN_USER);
} }
} }
if (authUser != null && model.getRequirement() == AuthenticatorModel.Requirement.ALTERNATIVE) { if (authUser != null && model.getRequirement() == AuthenticationExecutionModel.Requirement.ALTERNATIVE) {
clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SKIPPED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SKIPPED);
continue; continue;
} }
authUser = clientSession.getAuthenticatedUser(); authUser = clientSession.getAuthenticatedUser();
if (authenticator.requiresUser() && authUser != null && !authenticator.configuredFor(authUser)) { if (authenticator.requiresUser() && authUser != null && !authenticator.configuredFor(authUser)) {
if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) { if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) {
if (model.isUserSetupAllowed()) { if (model.isUserSetupAllowed()) {
clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SETUP_REQUIRED);
authUser.addRequiredAction(authenticator.getRequiredAction()); authUser.addRequiredAction(authenticator.getRequiredAction());
} else { } else {
@ -294,25 +303,26 @@ public class AuthenticationProcessor {
} }
continue; continue;
} }
Result context = new Result(model, authenticator); Result context = new Result(authenticatorModel, authenticator);
authenticator.authenticate(context); authenticator.authenticate(context);
Status result = context.getStatus(); Status result = context.getStatus();
if (result == Status.SUCCESS){ if (result == Status.SUCCESS){
clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.SUCCESS); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.SUCCESS);
if (model.isMasterAuthenticator()) return authenticationComplete(); //if (model.isMasterAuthenticator()) return authenticationComplete();
continue; continue;
} else if (result == Status.FAILED) { } else if (result == Status.FAILED) {
if (context.challenge != null) return context.challenge;
throw new AuthException(context.error); throw new AuthException(context.error);
} else if (result == Status.CHALLENGE) { } else if (result == Status.CHALLENGE) {
if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) return context.challenge; if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) return context.challenge;
if (challenge != null) challenge = context.challenge; if (challenge != null) challenge = context.challenge;
continue; continue;
} else if (result == Status.FAILURE_CHALLENGE) { } else if (result == Status.FAILURE_CHALLENGE) {
logUserFailure(); logUserFailure();
return context.challenge; return context.challenge;
} else if (result == Status.ATTEMPTED) { } else if (result == Status.ATTEMPTED) {
if (model.getRequirement() == AuthenticatorModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS); if (model.getRequirement() == AuthenticationExecutionModel.Requirement.REQUIRED) throw new AuthException(Error.INVALID_CREDENTIALS);
clientSession.setAuthenticatorStatus(model.getAlias(), UserSessionModel.AuthenticatorStatus.ATTEMPTED); clientSession.setAuthenticatorStatus(model.getId(), UserSessionModel.AuthenticatorStatus.ATTEMPTED);
continue; continue;
} }
} }

View file

@ -9,6 +9,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.ClientSessionCode;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
@ -48,6 +49,7 @@ public interface AuthenticatorContext {
void success(); void success();
void failure(AuthenticationProcessor.Error error); void failure(AuthenticationProcessor.Error error);
void failure(AuthenticationProcessor.Error error, Response response);
void challenge(Response challenge); void challenge(Response challenge);
void failureChallenge(AuthenticationProcessor.Error error, Response challenge); void failureChallenge(AuthenticationProcessor.Error error, Response challenge);
void attempted(); void attempted();

View file

@ -1,13 +1,16 @@
package org.keycloak.authentication; package org.keycloak.authentication;
import org.keycloak.models.AuthenticatorModel; import org.keycloak.models.AuthenticatorModel;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface AuthenticatorFactory extends ProviderFactory<Authenticator> { public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider {
Authenticator create(AuthenticatorModel model); Authenticator create(AuthenticatorModel model);
String getDisplayCategory();
String getDisplayType();
} }

View file

@ -0,0 +1,33 @@
package org.keycloak.authentication;
import org.keycloak.protocol.ProtocolMapper;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuthenticatorSpi implements Spi {
@Override
public boolean isPrivate() {
return false;
}
@Override
public String getName() {
return "authenticator";
}
@Override
public Class<? extends Provider> getProviderClass() {
return Authenticator.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuthenticatorFactory.class;
}
}

View file

@ -0,0 +1,60 @@
package org.keycloak.authentication.authenticators;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorModel;
import java.util.ArrayList;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class AuthenticationFlow {
/**
* Hardcoded models just to test this stuff. It is temporary
*/
static List<AuthenticationExecutionModel> hardcoded = new ArrayList<>();
/*
static {
AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId("1");
model.setAlias("cookie");
model.setMasterAuthenticator(true);
model.setProviderId(CookieAuthenticatorFactory.PROVIDER_ID);
model.setPriority(0);
model.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
model.setUserSetupAllowed(false);
hardcoded.add(model);
model = new AuthenticatorModel();
model.setId("2");
model.setAlias("user form");
model.setMasterAuthenticator(false);
model.setProviderId(LoginFormUsernameAuthenticatorFactory.PROVIDER_ID);
model.setPriority(1);
model.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
model.setUserSetupAllowed(false);
hardcoded.add(model);
model = new AuthenticatorModel();
model.setId("3");
model.setAlias("password form");
model.setMasterAuthenticator(false);
model.setProviderId(LoginFormUsernameAuthenticatorFactory.PROVIDER_ID);
model.setPriority(2);
model.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
model.setUserSetupAllowed(false);
hardcoded.add(model);
model = new AuthenticatorModel();
model.setId("4");
model.setAlias("otp form");
model.setMasterAuthenticator(false);
model.setProviderId(OTPFormAuthenticatorFactory.PROVIDER_ID);
model.setPriority(3);
model.setRequirement(AuthenticationExecutionModel.Requirement.OPTIONAL);
model.setUserSetupAllowed(false);
hardcoded.add(model);
}
*/
}

View file

@ -6,12 +6,16 @@ import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.models.AuthenticatorModel; import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import java.util.List;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class CookieAuthenticatorFactory implements AuthenticatorFactory { public class CookieAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-cookie";
static CookieAuthenticator SINGLETON = new CookieAuthenticator(); static CookieAuthenticator SINGLETON = new CookieAuthenticator();
@Override @Override
public Authenticator create(AuthenticatorModel model) { public Authenticator create(AuthenticatorModel model) {
@ -40,6 +44,26 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
@Override @Override
public String getId() { public String getId() {
return "auth-cookie"; return PROVIDER_ID;
}
@Override
public String getDisplayCategory() {
return "Complete Authenticator";
}
@Override
public String getDisplayType() {
return "Cookie Authenticator";
}
@Override
public String getHelpText() {
return "Validates the SSO cookie set by the auth server.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
} }
} }

View file

@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -31,19 +32,23 @@ public class LoginFormOTPAuthenticator extends LoginFormUsernameAuthenticator {
validateOTP(context); validateOTP(context);
} }
protected Response badPassword(AuthenticatorContext context) {
return loginForm(context).setError(Messages.INVALID_USER).createLogin();
}
public void validateOTP(AuthenticatorContext context) { public void validateOTP(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters(); MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>(); List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.TOTP); String password = inputData.getFirst(CredentialRepresentation.TOTP);
if (password == null) { if (password == null) {
Response challengeResponse = challenge(context); Response challengeResponse = badPassword(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return; return;
} }
credentials.add(UserCredentialModel.totp(password)); credentials.add(UserCredentialModel.totp(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials); boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
if (!valid) { if (!valid) {
Response challengeResponse = challenge(context); Response challengeResponse = badPassword(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return; return;
} }

View file

@ -0,0 +1,70 @@
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 LoginFormOTPAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-login-form-otp";
@Override
public Authenticator create(AuthenticatorModel model) {
return new LoginFormOTPAuthenticator(model);
}
@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 "Credential Validation";
}
@Override
public String getDisplayType() {
return "Login Form OTP";
}
@Override
public String getHelpText() {
return "Validates an OTP that is specified on the login page.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
}

View file

@ -6,6 +6,7 @@ import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.messages.Messages;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
@ -31,19 +32,24 @@ public class LoginFormPasswordAuthenticator extends LoginFormUsernameAuthenticat
validatePassword(context); validatePassword(context);
} }
protected Response badPassword(AuthenticatorContext context) {
return loginForm(context).setError(Messages.INVALID_USER).createLogin();
}
public void validatePassword(AuthenticatorContext context) { public void validatePassword(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters(); MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
List<UserCredentialModel> credentials = new LinkedList<>(); List<UserCredentialModel> credentials = new LinkedList<>();
String password = inputData.getFirst(CredentialRepresentation.PASSWORD); String password = inputData.getFirst(CredentialRepresentation.PASSWORD);
if (password == null) { if (password == null) {
Response challengeResponse = challenge(context); Response challengeResponse = badPassword(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return; return;
} }
credentials.add(UserCredentialModel.password(password)); credentials.add(UserCredentialModel.password(password));
boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials); boolean valid = context.getSession().users().validCredentials(context.getRealm(), context.getUser(), credentials);
if (!valid) { if (!valid) {
Response challengeResponse = challenge(context); Response challengeResponse = badPassword(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_CREDENTIALS, challengeResponse);
return; return;
} }

View file

@ -0,0 +1,70 @@
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 LoginFormPasswordAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-login-form-password";
@Override
public Authenticator create(AuthenticatorModel model) {
return new LoginFormPasswordAuthenticator(model);
}
@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 "Credential Validation";
}
@Override
public String getDisplayType() {
return "Login Form Password";
}
@Override
public String getHelpText() {
return "Validates a user password that is specified on the login page.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
}

View file

@ -6,11 +6,13 @@ import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorContext; import org.keycloak.authentication.AuthenticatorContext;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticatorModel; import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.protocol.oidc.OIDCLoginProtocol; import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.services.managers.AuthenticationManager; import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientSessionCode; import org.keycloak.services.managers.ClientSessionCode;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.LoginActionsService; import org.keycloak.services.resources.LoginActionsService;
import javax.ws.rs.core.MultivaluedMap; import javax.ws.rs.core.MultivaluedMap;
@ -59,19 +61,37 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
} }
protected Response challenge(AuthenticatorContext context, MultivaluedMap<String, String> formData) { protected Response challenge(AuthenticatorContext context, MultivaluedMap<String, String> formData) {
LoginFormsProvider forms = context.getSession().getProvider(LoginFormsProvider.class) LoginFormsProvider forms = loginForm(context);
.setClientSessionCode(new ClientSessionCode(context.getRealm(), context.getClientSession()).getCode());
if (formData.size() > 0) forms.setFormData(formData); if (formData.size() > 0) forms.setFormData(formData);
return forms.createLogin(); return forms.createLogin();
} }
protected LoginFormsProvider loginForm(AuthenticatorContext context) {
ClientSessionCode code = new ClientSessionCode(context.getRealm(), context.getClientSession());
code.setAction(ClientSessionModel.Action.AUTHENTICATE);
return context.getSession().getProvider(LoginFormsProvider.class)
.setClientSessionCode(code.getCode());
}
protected Response invalidUser(AuthenticatorContext context) {
return loginForm(context).setError(Messages.INVALID_USER).createLogin();
}
protected Response disabledUser(AuthenticatorContext context) {
return loginForm(context).setError(Messages.ACCOUNT_DISABLED).createLogin();
}
protected Response temporarilyDisabledUser(AuthenticatorContext context) {
return loginForm(context).setError(Messages.ACCOUNT_TEMPORARILY_DISABLED).createLogin();
}
public void validateUser(AuthenticatorContext context) { public void validateUser(AuthenticatorContext context) {
MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters(); MultivaluedMap<String, String> inputData = context.getHttpRequest().getFormParameters();
String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME); String username = inputData.getFirst(AuthenticationManager.FORM_USERNAME);
if (username == null) { if (username == null) {
Response challengeResponse = challenge(context); Response challengeResponse = invalidUser(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
return; return;
} }
@ -82,17 +102,19 @@ public class LoginFormUsernameAuthenticator implements Authenticator {
public boolean invalidUser(AuthenticatorContext context, UserModel user) { public boolean invalidUser(AuthenticatorContext context, UserModel user) {
if (user == null) { if (user == null) {
Response challengeResponse = challenge(context); Response challengeResponse = invalidUser(context);
context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse); context.failureChallenge(AuthenticationProcessor.Error.INVALID_USER, challengeResponse);
return true; return true;
} }
if (!user.isEnabled()) { if (!user.isEnabled()) {
context.failure(AuthenticationProcessor.Error.USER_DISABLED); Response challengeResponse = disabledUser(context);
context.failureChallenge(AuthenticationProcessor.Error.USER_DISABLED, challengeResponse);
return true; return true;
} }
if (context.getRealm().isBruteForceProtected()) { if (context.getRealm().isBruteForceProtected()) {
if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) { if (context.getProtector().isTemporarilyDisabled(context.getSession(), context.getRealm(), user.getUsername())) {
context.failure(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED); Response challengeResponse = temporarilyDisabledUser(context);
context.failureChallenge(AuthenticationProcessor.Error.USER_TEMPORARILY_DISABLED, challengeResponse);
return true; return true;
} }
} }

View file

@ -0,0 +1,70 @@
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 LoginFormUsernameAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-login-form-username";
@Override
public Authenticator create(AuthenticatorModel model) {
return new LoginFormUsernameAuthenticator(model);
}
@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 "User Validation";
}
@Override
public String getDisplayType() {
return "Login Form Username";
}
@Override
public String getHelpText() {
return "Validates a username that is specified on the login page.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
}

View file

@ -0,0 +1,70 @@
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 OTPFormAuthenticatorFactory implements AuthenticatorFactory {
public static final String PROVIDER_ID = "auth-otp-form";
@Override
public Authenticator create(AuthenticatorModel model) {
return new OTPFormAuthenticator(model);
}
@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 "Credential Validation";
}
@Override
public String getDisplayType() {
return "OTP Form";
}
@Override
public String getHelpText() {
return "Validates a OTP on a separate OTP form.";
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
}

View file

@ -0,0 +1,5 @@
org.keycloak.authentication.authenticators.CookieAuthenticatorFactory
org.keycloak.authentication.authenticators.LoginFormOTPAuthenticatorFactory
org.keycloak.authentication.authenticators.LoginFormPasswordAuthenticatorFactory
org.keycloak.authentication.authenticators.LoginFormUsernameAuthenticatorFactory
org.keycloak.authentication.authenticators.OTPFormAuthenticatorFactory