required actions model

This commit is contained in:
Bill Burke 2015-06-16 18:17:38 -04:00
parent 2a64f5e3ce
commit 3b78fa2d5d
26 changed files with 614 additions and 146 deletions

View file

@ -7,12 +7,6 @@
<delete tableName="CLIENT_SESSION"/> <delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/> <delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/> <delete tableName="USER_SESSION"/>
<createTable tableName="DEFAULT_REQUIRED_ACTIONS">
<column name="REALM_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="VARCHAR(36)"/>
</createTable>
<addColumn tableName="CLIENT_SESSION"> <addColumn tableName="CLIENT_SESSION">
<column name="CURRENT_ACTION" type="VARCHAR(36)"> <column name="CURRENT_ACTION" type="VARCHAR(36)">
<constraints nullable="false"/> <constraints nullable="false"/>
@ -78,9 +72,35 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
</createTable> </createTable>
<createTable tableName="REQUIRED_ACTION_PROVIDER">
<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="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="DEFAULT_ACTION" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/>
</column>
<column name="PROVIDER_ID" type="VARCHAR(255)"/>
</createTable>
<createTable tableName="REQUIRED_ACTION_CONFIG">
<column name="REQUIRED_ACTION_ID" type="VARCHAR(36)">
<constraints nullable="false"/>
</column>
<column name="VALUE" type="CLOB"/>
<column name="NAME" type="VARCHAR(255)">
<constraints nullable="false"/>
</column>
</createTable>
<addPrimaryKey columnNames="ID" constraintName="CONSTRAINT_REQ_ACT_PRV_PK" tableName="REQUIRED_ACTION_PROVIDER"/>
<addPrimaryKey columnNames="REQUIRED_ACTION_ID, NAME" constraintName="CONSTRAINT_REQ_ACT_CFG_PK" tableName="REQUIRED_ACTION_CONFIG"/>
<addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTR_CL_USR_SES_NOTE" tableName="CLIENT_USER_SESSION_NOTE"/> <addPrimaryKey columnNames="CLIENT_SESSION, NAME" constraintName="CONSTR_CL_USR_SES_NOTE" tableName="CLIENT_USER_SESSION_NOTE"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REQUIRED_ACTION_PROVIDER" constraintName="FK_REQ_ACT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/> <addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/> <dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="DEFAULT_REQUIRED_ACTIONS" constraintName="FK_DEF_REQ_ACTS_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>

View file

@ -28,6 +28,7 @@
<class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class> <class>org.keycloak.models.jpa.entities.AuthenticationFlowEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class> <class>org.keycloak.models.jpa.entities.AuthenticationExecutionEntity</class>
<class>org.keycloak.models.jpa.entities.AuthenticatorEntity</class> <class>org.keycloak.models.jpa.entities.AuthenticatorEntity</class>
<class>org.keycloak.models.jpa.entities.RequiredActionProviderEntity</class>
<!-- JpaUserSessionProvider --> <!-- JpaUserSessionProvider -->
<class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class> <class>org.keycloak.models.sessions.jpa.entities.ClientSessionEntity</class>

View file

@ -1045,7 +1045,7 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'ProtocolListCtrl' controller : 'ProtocolListCtrl'
}) })
.when('/realms/:realm/authentication', { .when('/realms/:realm/authentication/flows', {
templateUrl : resourceUrl + '/partials/authentication-flows.html', templateUrl : resourceUrl + '/partials/authentication-flows.html',
resolve : { resolve : {
realm : function(RealmLoader) { realm : function(RealmLoader) {
@ -1054,7 +1054,15 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
controller : 'AuthenticationFlowsCtrl' controller : 'AuthenticationFlowsCtrl'
}) })
.when('/realms/:realm/authentication/required-actions', {
templateUrl : resourceUrl + '/partials/required-actions.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
}
},
controller : 'RequiredActionsCtrl'
})
.when('/server-info', { .when('/server-info', {
templateUrl : resourceUrl + '/partials/server-info.html' templateUrl : resourceUrl + '/partials/server-info.html'
}) })
@ -1502,6 +1510,15 @@ module.directive('kcTabsRealm', function () {
} }
}); });
module.directive('kcTabsAuthentication', function () {
return {
scope: true,
restrict: 'E',
replace: true,
templateUrl: resourceUrl + '/templates/kc-tabs-authentication.html'
}
});
module.directive('kcTabsUser', function () { module.directive('kcTabsUser', function () {
return { return {
scope: true, scope: true,

View file

@ -1610,6 +1610,19 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, Authenticat
}); });
module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) {
$scope.realm = realm;
});
module.controller('DefaultRequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) {
$scope.realm = realm;
});

View file

@ -1074,7 +1074,7 @@ module.factory('IdentityProviderMapper', function($resource) {
}); });
module.factory('AuthenticationExecutions', function($resource) { module.factory('AuthenticationExecutions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication-flows/flow/:alias/executions', { return $resource(authUrl + '/admin/realms/:realm/authentication/flow/:alias/executions', {
realm : '@realm', realm : '@realm',
alias : '@alias' alias : '@alias'
}, { }, {

View file

@ -1,5 +1,7 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2"> <div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1><strong>Authentication Flows</strong> {{realm.realm|capitalize}}</h1> <h1>Authentication</h1>
<kc-tabs-authentication></kc-tabs-authentication>
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>

View file

@ -0,0 +1,8 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>Authentication</h1>
<kc-tabs-authentication></kc-tabs-authentication>
</div>
<kc-menu></kc-menu>

View file

@ -16,7 +16,7 @@
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li> <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'roles' || path[2] == 'default-roles' || (path[1] == 'role' && path[3] != 'clients')) && 'active'"><a href="#/realms/{{realm.realm}}/roles">Roles</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li> <li data-ng-show="access.viewRealm" data-ng-class="(path[2] == 'identity-provider-settings' || path[2] == 'identity-provider-mappers') && 'active'"><a href="#/realms/{{realm.realm}}/identity-provider-settings">Identity Providers</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li> <li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'user-federation' || path[2] == 'user-federation') && 'active'"><a href="#/realms/{{realm.realm}}/user-federation">User Federation</a></li>
<li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication">Authentication</a></li> <li data-ng-show="access.viewRealm" data-ng-class="(path[1] == 'authentication' || path[2] == 'authentication') && 'active'"><a href="#/realms/{{realm.realm}}/authentication/flows">Authentication</a></li>
</ul> </ul>
</div> </div>

View file

@ -0,0 +1,4 @@
<ul class="nav nav-tabs">
<li ng-class="{active: path[3] == 'flows'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/flows">Authenticators</a></li>
<li ng-class="{active: path[3] == 'required-actions'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/authentication/required-actions">Required Actions</a></li>
</ul>

View file

@ -156,13 +156,6 @@ public interface RealmModel extends RoleContainerModel {
void updateDefaultRoles(String[] defaultRoles); void updateDefaultRoles(String[] defaultRoles);
Set<String> getDefaultRequiredActions();
void addDefaultRequiredAction(String action);
void removeDefaultRequiredAction(String action);
void setDefaultRequiredActions(Set<String> action);
// Key is clientId // Key is clientId
Map<String, ClientModel> getClientNameMap(); Map<String, ClientModel> getClientNameMap();
@ -206,6 +199,12 @@ public interface RealmModel extends RoleContainerModel {
void removeAuthenticator(AuthenticatorModel model); void removeAuthenticator(AuthenticatorModel model);
AuthenticatorModel getAuthenticatorById(String id); AuthenticatorModel getAuthenticatorById(String id);
List<RequiredActionProviderModel> getRequiredActionProviders();
RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model);
void updateRequiredActionProvider(RequiredActionProviderModel model);
void removeRequiredActionProvider(RequiredActionProviderModel model);
RequiredActionProviderModel getRequiredActionProviderById(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,67 @@
package org.keycloak.models;
import java.util.HashMap;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RequiredActionProviderModel {
private String id;
private String alias;
private String providerId;
private boolean enabled;
private boolean defaultAction;
private Map<String, String> config = new HashMap<String, String>();
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 boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isDefaultAction() {
return defaultAction;
}
public void setDefaultAction(boolean defaultAction) {
this.defaultAction = defaultAction;
}
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

@ -78,7 +78,7 @@ public class RealmEntity extends AbstractIdentifiableEntity {
private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>(); private List<IdentityProviderMapperEntity> identityProviderMappers = new ArrayList<IdentityProviderMapperEntity>();
private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>(); private List<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
private List<AuthenticatorEntity> authenticators = new ArrayList<>(); private List<AuthenticatorEntity> authenticators = new ArrayList<>();
private List<String> defaultRequiredActions = new ArrayList<>(); private List<RequiredActionProviderEntity> requiredActionProviders = new ArrayList<>();
public String getName() { public String getName() {
@ -504,12 +504,12 @@ public class RealmEntity extends AbstractIdentifiableEntity {
this.authenticators = authenticators; this.authenticators = authenticators;
} }
public List<String> getDefaultRequiredActions() { public List<RequiredActionProviderEntity> getRequiredActionProviders() {
return defaultRequiredActions; return requiredActionProviders;
} }
public void setDefaultRequiredActions(List<String> defaultRequiredActions) { public void setRequiredActionProviders(List<RequiredActionProviderEntity> requiredActionProviders) {
this.defaultRequiredActions = defaultRequiredActions; this.requiredActionProviders = requiredActionProviders;
} }
} }

View file

@ -0,0 +1,64 @@
package org.keycloak.models.entities;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class RequiredActionProviderEntity {
protected String id;
protected String alias;
protected String providerId;
protected boolean enabled;
protected boolean defaultAction;
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 boolean isEnabled() {
return enabled;
}
public boolean isDefaultAction() {
return defaultAction;
}
public void setDefaultAction(boolean defaultAction) {
this.defaultAction = defaultAction;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
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

@ -25,6 +25,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
@ -285,8 +286,10 @@ public class FileUserProvider implements UserProvider {
} }
if (addDefaultRequiredActions) { if (addDefaultRequiredActions) {
for (String r : realm.getDefaultRequiredActions()) { for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
userModel.addRequiredAction(r); if (r.isEnabled() && r.isDefaultAction()) {
userModel.addRequiredAction(r.getAlias());
}
} }
} }

View file

@ -28,6 +28,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperEventImpl; import org.keycloak.models.UserFederationMapperEventImpl;
@ -41,6 +42,7 @@ 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;
import org.keycloak.models.entities.RequiredActionProviderEntity;
import org.keycloak.models.entities.RequiredCredentialEntity; import org.keycloak.models.entities.RequiredCredentialEntity;
import org.keycloak.models.entities.RoleEntity; import org.keycloak.models.entities.RoleEntity;
import org.keycloak.models.entities.UserFederationMapperEntity; import org.keycloak.models.entities.UserFederationMapperEntity;
@ -1442,6 +1444,84 @@ public class RealmAdapter implements RealmModel {
} }
} }
@Override
public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity auth = new RequiredActionProviderEntity();
auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias());
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
auth.setEnabled(model.isEnabled());
auth.setDefaultAction(model.isDefaultAction());
realm.getRequiredActionProviders().add(auth);
model.setId(auth.getId());
return model;
}
@Override
public void removeRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId());
if (entity == null) return;
realm.getRequiredActionProviders().remove(entity);
}
@Override
public RequiredActionProviderModel getRequiredActionProviderById(String id) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(id);
if (entity == null) return null;
return entityToModel(entity);
}
public RequiredActionProviderModel entityToModel(RequiredActionProviderEntity entity) {
RequiredActionProviderModel model = new RequiredActionProviderModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
model.setEnabled(entity.isEnabled());
model.setDefaultAction(entity.isDefaultAction());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled());
entity.setDefaultAction(model.isDefaultAction());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
}
@Override
public List<RequiredActionProviderModel> getRequiredActionProviders() {
List<RequiredActionProviderModel> actions = new LinkedList<>();
for (RequiredActionProviderEntity entity : realm.getRequiredActionProviders()) {
actions.add(entityToModel(entity));
}
return actions;
}
public RequiredActionProviderEntity getRequiredActionProviderEntity(String id) {
RequiredActionProviderEntity entity = null;
for (RequiredActionProviderEntity auth : realm.getRequiredActionProviders()) {
if (auth.getId().equals(id)) {
entity = auth;
break;
}
}
return entity;
}
@Override @Override
public Set<UserFederationMapperModel> getUserFederationMappers() { public Set<UserFederationMapperModel> getUserFederationMappers() {
@ -1563,36 +1643,4 @@ public class RealmAdapter implements RealmModel {
return mapper; return mapper;
} }
@Override }
public Set<String> getDefaultRequiredActions() {
Set<String> result = new HashSet<String>();
if (realm.getDefaultRequiredActions() != null) {
result.addAll(realm.getDefaultRequiredActions());
}
return result;
}
@Override
public void addDefaultRequiredAction(String action) {
Set<String> actions = getDefaultRequiredActions();
actions.add(action);
setDefaultRequiredActions(actions);
}
@Override
public void removeDefaultRequiredAction(String action) {
Set<String> actions = getDefaultRequiredActions();
actions.remove(action);
setDefaultRequiredActions(actions);
}
@Override
public void setDefaultRequiredActions(Set<String> action) {
List<String> result = new ArrayList<String>();
result.addAll(action);
realm.setDefaultRequiredActions(result);
}
}

View file

@ -10,6 +10,7 @@ import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationMapperModel;
@ -1128,28 +1129,36 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public Set<String> getDefaultRequiredActions() { public List<RequiredActionProviderModel> getRequiredActionProviders() {
return cached.getDefaultRequiredActions(); if (updated != null) return updated.getRequiredActionProviders();
List<RequiredActionProviderModel> models = new ArrayList<>();
models.addAll(cached.getRequiredActionProviders().values());
return models;
} }
@Override @Override
public void addDefaultRequiredAction(String action) { public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) {
getDelegateForUpdate(); getDelegateForUpdate();
updated.addDefaultRequiredAction(action); return updated.addRequiredActionProvider(model);
}
@Override
public void updateRequiredActionProvider(RequiredActionProviderModel model) {
getDelegateForUpdate();
updated.updateRequiredActionProvider(model);
} }
@Override @Override
public void removeDefaultRequiredAction(String action) { public void removeRequiredActionProvider(RequiredActionProviderModel model) {
getDelegateForUpdate(); getDelegateForUpdate();
updated.removeDefaultRequiredAction(action); updated.removeRequiredActionProvider(model);
} }
@Override @Override
public void setDefaultRequiredActions(Set<String> action) { public RequiredActionProviderModel getRequiredActionProviderById(String id) {
getDelegateForUpdate(); if (updated != null) return updated.getRequiredActionProviderById(id);
updated.setDefaultRequiredActions(action); return cached.getRequiredActionProviders().get(id);
} }
} }

View file

@ -10,6 +10,7 @@ import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider; import org.keycloak.models.RealmProvider;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperModel; import org.keycloak.models.UserFederationMapperModel;
@ -81,6 +82,7 @@ public class CachedRealm {
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, AuthenticationFlowModel> authenticationFlows = new HashMap<>();
private Map<String, AuthenticatorModel> authenticators = new HashMap<>(); private Map<String, AuthenticatorModel> authenticators = new HashMap<>();
private Map<String, RequiredActionProviderModel> requiredActionProviders = new HashMap<>();
private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>(); private MultivaluedHashMap<String, AuthenticationExecutionModel> authenticationExecutions = new MultivaluedHashMap<>();
private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>(); private Map<String, AuthenticationExecutionModel> executionsById = new HashMap<>();
@ -98,7 +100,6 @@ public class CachedRealm {
private Set<String> supportedLocales = new HashSet<String>(); private Set<String> supportedLocales = new HashSet<String>();
private String defaultLocale; private String defaultLocale;
private MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>(); private MultivaluedHashMap<String, IdentityProviderMapperModel> identityProviderMappers = new MultivaluedHashMap<>();
private Set<String> defaultRequiredActions = new HashSet<>();
public CachedRealm() { public CachedRealm() {
} }
@ -201,7 +202,9 @@ public class CachedRealm {
for (AuthenticatorModel authenticator : model.getAuthenticators()) { for (AuthenticatorModel authenticator : model.getAuthenticators()) {
authenticators.put(authenticator.getId(), authenticator); authenticators.put(authenticator.getId(), authenticator);
} }
this.defaultRequiredActions.addAll(model.getDefaultRequiredActions()); for (RequiredActionProviderModel action : model.getRequiredActionProviders()) {
requiredActionProviders.put(action.getId(), action);
}
} }
@ -441,7 +444,7 @@ public class CachedRealm {
return executionsById; return executionsById;
} }
public Set<String> getDefaultRequiredActions() { public Map<String, RequiredActionProviderModel> getRequiredActionProviders() {
return defaultRequiredActions; return requiredActionProviders;
} }
} }

View file

@ -6,6 +6,7 @@ import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
@ -68,9 +69,9 @@ public class JpaUserProvider implements UserProvider {
} }
} }
} }
if (addDefaultRequiredActions) { for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
for (String r : realm.getDefaultRequiredActions()) { if (r.isEnabled() && r.isDefaultAction()) {
userModel.addRequiredAction(r); userModel.addRequiredAction(r.getAlias());
} }
} }

View file

@ -11,6 +11,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException; import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperEventImpl; import org.keycloak.models.UserFederationMapperEventImpl;
@ -25,6 +26,7 @@ import org.keycloak.models.jpa.entities.IdentityProviderEntity;
import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity; import org.keycloak.models.jpa.entities.IdentityProviderMapperEntity;
import org.keycloak.models.jpa.entities.RealmAttributeEntity; import org.keycloak.models.jpa.entities.RealmAttributeEntity;
import org.keycloak.models.jpa.entities.RealmEntity; import org.keycloak.models.jpa.entities.RealmEntity;
import org.keycloak.models.jpa.entities.RequiredActionProviderEntity;
import org.keycloak.models.jpa.entities.RequiredCredentialEntity; import org.keycloak.models.jpa.entities.RequiredCredentialEntity;
import org.keycloak.models.jpa.entities.RoleEntity; import org.keycloak.models.jpa.entities.RoleEntity;
import org.keycloak.models.jpa.entities.UserFederationMapperEntity; import org.keycloak.models.jpa.entities.UserFederationMapperEntity;
@ -1726,29 +1728,75 @@ public class RealmAdapter implements RealmModel {
} }
@Override @Override
public Set<String> getDefaultRequiredActions() { public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) {
Set<String> result = new HashSet<String>(); RequiredActionProviderEntity auth = new RequiredActionProviderEntity();
result.addAll(realm.getDefaultRequiredActions()); auth.setId(KeycloakModelUtils.generateId());
return result; auth.setAlias(model.getAlias());
} auth.setRealm(realm);
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
auth.setEnabled(model.isEnabled());
@Override auth.setDefaultAction(model.isDefaultAction());
public void setDefaultRequiredActions(Set<String> actions) { realm.getRequiredActionProviders().add(auth);
realm.setDefaultRequiredActions(actions); em.persist(auth);
em.flush();
model.setId(auth.getId());
return model;
} }
@Override @Override
public void addDefaultRequiredAction(String action) { public void removeRequiredActionProvider(RequiredActionProviderModel model) {
realm.getDefaultRequiredActions().add(action); RequiredActionProviderEntity entity = em.find(RequiredActionProviderEntity.class, model.getId());
if (entity == null) return;
em.remove(entity);
em.flush();
} }
@Override @Override
public void removeDefaultRequiredAction(String action) { public RequiredActionProviderModel getRequiredActionProviderById(String id) {
realm.getDefaultRequiredActions().remove(action); RequiredActionProviderEntity entity = em.find(RequiredActionProviderEntity.class, id);
if (entity == null) return null;
return entityToModel(entity);
} }
public RequiredActionProviderModel entityToModel(RequiredActionProviderEntity entity) {
RequiredActionProviderModel model = new RequiredActionProviderModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
model.setEnabled(entity.isEnabled());
model.setDefaultAction(entity.isDefaultAction());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity entity = em.find(RequiredActionProviderEntity.class, model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled());
entity.setDefaultAction(model.isDefaultAction());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
em.flush();
}
@Override
public List<RequiredActionProviderModel> getRequiredActionProviders() {
List<RequiredActionProviderModel> actions = new LinkedList<>();
for (RequiredActionProviderEntity entity : realm.getRequiredActionProviders()) {
actions.add(entityToModel(entity));
}
return actions;
}
} }

View file

@ -113,12 +113,6 @@ public class RealmEntity {
@OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(fetch = FetchType.LAZY, cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RoleEntity> roles = new ArrayList<RoleEntity>(); Collection<RoleEntity> roles = new ArrayList<RoleEntity>();
@ElementCollection
@Column(name="VALUE")
@CollectionTable(name = "DEFAULT_REQUIRED_ACTIONS", joinColumns={ @JoinColumn(name="REALM_ID") })
protected Set<String> defaultRequiredActions = new HashSet<String>();
@ElementCollection @ElementCollection
@MapKeyColumn(name="NAME") @MapKeyColumn(name="NAME")
@Column(name="VALUE") @Column(name="VALUE")
@ -163,6 +157,9 @@ public class RealmEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<AuthenticatorEntity> authenticators = new ArrayList<>(); Collection<AuthenticatorEntity> authenticators = new ArrayList<>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<RequiredActionProviderEntity> requiredActionProviders = new ArrayList<>();
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "realm")
Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>(); Collection<AuthenticationFlowEntity> authenticationFlows = new ArrayList<>();
@ -567,6 +564,14 @@ public class RealmEntity {
this.authenticators = authenticators; this.authenticators = authenticators;
} }
public Collection<RequiredActionProviderEntity> getRequiredActionProviders() {
return requiredActionProviders;
}
public void setRequiredActionProviders(Collection<RequiredActionProviderEntity> requiredActionProviders) {
this.requiredActionProviders = requiredActionProviders;
}
public Collection<AuthenticationFlowEntity> getAuthenticationFlows() { public Collection<AuthenticationFlowEntity> getAuthenticationFlows() {
return authenticationFlows; return authenticationFlows;
} }
@ -575,12 +580,5 @@ public class RealmEntity {
this.authenticationFlows = authenticationFlows; this.authenticationFlows = authenticationFlows;
} }
public Set<String> getDefaultRequiredActions() {
return defaultRequiredActions;
}
public void setDefaultRequiredActions(Set<String> defaultRequiredActions) {
this.defaultRequiredActions = defaultRequiredActions;
}
} }

View file

@ -0,0 +1,107 @@
package org.keycloak.models.jpa.entities;
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.Table;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
@Table(name="REQUIRED_ACTION_PROVIDER")
@Entity
@NamedQueries({
@NamedQuery(name="deleteRequiredActionProviderByRealm", query="delete from RequiredActionProviderEntity action where action.realm = :realm"),})
public class RequiredActionProviderEntity {
@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;
@Column(name="ENABLED")
protected boolean enabled;
@Column(name="DEFAULT_ACTION")
protected boolean defaultAction;
@ElementCollection
@MapKeyColumn(name="NAME")
@Column(name="VALUE")
@CollectionTable(name="REQUIRED_ACTION_CONFIG", joinColumns={ @JoinColumn(name="REQUIRED_ACTION_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 boolean isEnabled() {
return enabled;
}
public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
public boolean isDefaultAction() {
return defaultAction;
}
public void setDefaultAction(boolean defaultAction) {
this.defaultAction = defaultAction;
}
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

@ -11,6 +11,7 @@ import org.keycloak.models.FederatedIdentityModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
@ -258,8 +259,10 @@ public class MongoUserProvider implements UserProvider {
} }
if (addDefaultRequiredActions) { if (addDefaultRequiredActions) {
for (String r : realm.getDefaultRequiredActions()) { for (RequiredActionProviderModel r : realm.getRequiredActionProviders()) {
userModel.addRequiredAction(r); if (r.isEnabled() && r.isDefaultAction()) {
userModel.addRequiredAction(r.getAlias());
}
} }
} }

View file

@ -16,6 +16,7 @@ import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RealmProvider; import org.keycloak.models.RealmProvider;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserFederationMapperEventImpl; import org.keycloak.models.UserFederationMapperEventImpl;
@ -27,6 +28,7 @@ import org.keycloak.models.entities.AuthenticationFlowEntity;
import org.keycloak.models.entities.AuthenticatorEntity; 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.RequiredActionProviderEntity;
import org.keycloak.models.entities.RequiredCredentialEntity; import org.keycloak.models.entities.RequiredCredentialEntity;
import org.keycloak.models.entities.UserFederationMapperEntity; import org.keycloak.models.entities.UserFederationMapperEntity;
import org.keycloak.models.entities.UserFederationProviderEntity; import org.keycloak.models.entities.UserFederationProviderEntity;
@ -1525,6 +1527,88 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
updateMongoEntity(); updateMongoEntity();
} }
@Override
public RequiredActionProviderModel addRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity auth = new RequiredActionProviderEntity();
auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias());
auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig());
auth.setEnabled(model.isEnabled());
auth.setDefaultAction(model.isDefaultAction());
realm.getRequiredActionProviders().add(auth);
model.setId(auth.getId());
updateMongoEntity();
return model;
}
@Override
public void removeRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId());
if (entity == null) return;
getMongoEntity().getRequiredActionProviders().remove(entity);
updateMongoEntity();
}
@Override
public RequiredActionProviderModel getRequiredActionProviderById(String id) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(id);
if (entity == null) return null;
return entityToModel(entity);
}
public RequiredActionProviderModel entityToModel(RequiredActionProviderEntity entity) {
RequiredActionProviderModel model = new RequiredActionProviderModel();
model.setId(entity.getId());
model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias());
model.setEnabled(entity.isEnabled());
model.setDefaultAction(entity.isDefaultAction());
Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config);
return model;
}
@Override
public void updateRequiredActionProvider(RequiredActionProviderModel model) {
RequiredActionProviderEntity entity = getRequiredActionProviderEntity(model.getId());
if (entity == null) return;
entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled());
entity.setDefaultAction(model.isDefaultAction());
if (entity.getConfig() == null) {
entity.setConfig(model.getConfig());
} else {
entity.getConfig().clear();
entity.getConfig().putAll(model.getConfig());
}
updateMongoEntity();
}
@Override
public List<RequiredActionProviderModel> getRequiredActionProviders() {
List<RequiredActionProviderModel> actions = new LinkedList<>();
for (RequiredActionProviderEntity entity : realm.getRequiredActionProviders()) {
actions.add(entityToModel(entity));
}
return actions;
}
public RequiredActionProviderEntity getRequiredActionProviderEntity(String id) {
RequiredActionProviderEntity entity = null;
for (RequiredActionProviderEntity auth : getMongoEntity().getRequiredActionProviders()) {
if (auth.getId().equals(id)) {
entity = auth;
break;
}
}
return entity;
}
@Override @Override
public Set<UserFederationMapperModel> getUserFederationMappers() { public Set<UserFederationMapperModel> getUserFederationMappers() {
@ -1648,32 +1732,4 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
mapper.setConfig(config); mapper.setConfig(config);
return mapper; return mapper;
} }
@Override
public Set<String> getDefaultRequiredActions() {
Set<String> result = new HashSet<String>();
result.addAll(realm.getDefaultRequiredActions());
return result;
}
@Override
public void setDefaultRequiredActions(Set<String> actions) {
List<String> result = new ArrayList<String>();
result.addAll(actions);
getMongoEntity().setDefaultRequiredActions(result);
updateMongoEntity();
}
@Override
public void addDefaultRequiredAction(String action) {
getMongoStore().pushItemToList(getMongoEntity(), "defaultRequiredActions", action, true, invocationContext);
}
@Override
public void removeDefaultRequiredAction(String action) {
getMongoStore().pullItemFromList(getMongoEntity(), "defaultRequiredActions", action, invocationContext);
}
} }

View file

@ -17,7 +17,7 @@ public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, Co
String getDisplayType(); String getDisplayType();
/** /**
* General authenticator type, i.e. totp, password, cert * General authenticator type, i.e. totp, password, cert.
* *
* @return null if not a referencable type * @return null if not a referencable type
*/ */
@ -26,8 +26,7 @@ public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, Co
boolean isConfigurable(); boolean isConfigurable();
/** /**
* What requirement settings are allowed. For example, KERBEROS can only be required because of the way its challenges * What requirement settings are allowed.
* work.
* *
* @return * @return
*/ */

View file

@ -26,17 +26,17 @@ import java.util.List;
import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static javax.ws.rs.core.Response.Status.NOT_FOUND;
/** /**
* @author Pedro Igor * @author Bill Burke
*/ */
public class AuthenticationFlowResource { public class AuthenticationManagementResource {
private final RealmModel realm; private final RealmModel realm;
private final KeycloakSession session; private final KeycloakSession session;
private RealmAuth auth; private RealmAuth auth;
private AdminEventBuilder adminEvent; private AdminEventBuilder adminEvent;
private static Logger logger = Logger.getLogger(AuthenticationFlowResource.class); private static Logger logger = Logger.getLogger(AuthenticationManagementResource.class);
public AuthenticationFlowResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) { public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
this.realm = realm; this.realm = realm;
this.session = session; this.session = session;
this.auth = auth; this.auth = auth;
@ -176,5 +176,4 @@ public class AuthenticationFlowResource {
realm.updateAuthenticatorExecution(model); realm.updateAuthenticatorExecution(model);
} }
} }
} }

View file

@ -56,7 +56,6 @@ import javax.ws.rs.core.UriInfo;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date; import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
@ -239,9 +238,9 @@ public class RealmAdminResource {
return fed; return fed;
} }
@Path("authentication-flows") @Path("authentication")
public AuthenticationFlowResource flows() { public AuthenticationManagementResource flows() {
AuthenticationFlowResource resource = new AuthenticationFlowResource(realm, session, auth, adminEvent); AuthenticationManagementResource resource = new AuthenticationManagementResource(realm, session, auth, adminEvent);
ResteasyProviderFactory.getInstance().injectProperties(resource); ResteasyProviderFactory.getInstance().injectProperties(resource);
//resourceContext.initResource(resource); //resourceContext.initResource(resource);
return resource; return resource;