required actions storage/display

This commit is contained in:
Bill Burke 2015-06-16 22:48:39 -04:00
parent 3b78fa2d5d
commit dddc5181e7
24 changed files with 345 additions and 31 deletions

View file

@ -77,6 +77,7 @@
<constraints nullable="false"/> <constraints nullable="false"/>
</column> </column>
<column name="ALIAS" type="VARCHAR(255)"/> <column name="ALIAS" type="VARCHAR(255)"/>
<column name="NAME" type="VARCHAR(255)"/>
<column name="REALM_ID" type="VARCHAR(36)"/> <column name="REALM_ID" type="VARCHAR(36)"/>
<column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false"> <column name="ENABLED" type="BOOLEAN" defaultValueBoolean="false">
<constraints nullable="false"/> <constraints nullable="false"/>

View file

@ -1611,13 +1611,28 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, Authenticat
}); });
module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) { module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) {
console.log('RequiredActionsCtrl');
$scope.realm = realm; $scope.realm = realm;
$scope.requiredActions = [];
var setupRequiredActionsForm = function() {
console.log('setupRequiredActionsForm');
RequiredActions.query({id: realm.realm}, function(data) {
$scope.requiredActions = [];
for (var i = 0; i < data.length; i++) {
$scope.requiredActions.push(data[i]);
}
});
};
$scope.updateRequiredAction = function(action) {
RequiredActions.update({realm: realm.realm, alias: action.alias}, action, function() {
Notifications.success("Auth requirement updated");
setupForm();
setupRequiredActionsForm();
});
}
}); setupRequiredActionsForm();
module.controller('DefaultRequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) {
$scope.realm = realm;
}); });
@ -1627,3 +1642,4 @@ module.controller('DefaultRequiredActionsCtrl', function($scope, realm, Required

View file

@ -228,8 +228,8 @@ module.controller('UserDetailCtrl', function($scope, realm, user, User, UserFede
RequiredActions.query({id: realm.realm}, function(data) { RequiredActions.query({id: realm.realm}, function(data) {
$scope.userReqActionList = []; $scope.userReqActionList = [];
for (var i = 0; i < data.length; i++) { for (var i = 0; i < data.length; i++) {
console.log("listed required action: " + data[i].text); console.log("listed required action: " + data[i].name);
item = { id: data[i].id, text: data[i].text }; item = data[i];
$scope.userReqActionList.push(item); $scope.userReqActionList.push(item);
} }

View file

@ -63,6 +63,14 @@ module.factory('UserListLoader', function(Loader, User, $route, $q) {
}); });
}); });
module.factory('RequiredActionsListLoader', function(Loader, RequiredActions, $route, $q) {
return Loader.query(RequiredActions, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $route, $q) { module.factory('RealmSessionStatsLoader', function(Loader, RealmSessionStats, $route, $q) {
return Loader.get(RealmSessionStats, function() { return Loader.get(RealmSessionStats, function() {
return { return {

View file

@ -187,8 +187,13 @@ module.factory('RealmAdminEvents', function($resource) {
}); });
module.factory('RequiredActions', function($resource) { module.factory('RequiredActions', function($resource) {
return $resource(authUrl + '/admin/realms/:id/required-actions', { return $resource(authUrl + '/admin/realms/:id/authentication/required-actions/:alias', {
id : '@realm' realm : '@realm',
alias : '@alias'
}, {
update : {
method : 'PUT'
}
}); });
}); });

View file

@ -27,7 +27,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="execution in executions"> <tr ng-repeat="execution in executions" data-ng-show="executions.length > 0">
<td ng-show="execution.subFlow"></td> <td ng-show="execution.subFlow"></td>
<td><h2>{{execution.referenceType}}</h2></td> <td><h2>{{execution.referenceType}}</h2></td>
<td ng-hide="execution.subFlow"></td> <td ng-hide="execution.subFlow"></td>

View file

@ -2,6 +2,25 @@
<h1>Authentication</h1> <h1>Authentication</h1>
<kc-tabs-authentication></kc-tabs-authentication> <kc-tabs-authentication></kc-tabs-authentication>
<table class="table table-striped table-bordered">
<thead>
<tr data-ng-hide="requiredActions.length == 0">
<th>Required Action</th>
<th>Enabled</th>
<th>Default Action</th>
</tr>
</thead>
<tbody>
<tr ng-repeat="requiredAction in requiredActions" data-ng-show="requiredActions.length > 0">
<td>{{requiredAction.name}}</td>
<td>{{requiredAction.enabled}}</td>
<td>{{requiredAction.defaultAction}}</td>
</tr>
<tr data-ng-show="requiredActions.length == 0">
<td>No required actions configured</td>
</tr>
</tbody>
</table>
</div> </div>

View file

@ -80,7 +80,7 @@
<div class="col-md-6"> <div class="col-md-6">
<select ui-select2 id="reqActions" ng-model="user.requiredActions" data-placeholder="Select an action..." multiple> <select ui-select2 id="reqActions" ng-model="user.requiredActions" data-placeholder="Select an action..." multiple>
<option ng-repeat="action in userReqActionList" value="{{action.id}}">{{action.text}}</option> <option ng-repeat="action in userReqActionList" value="{{action.alias}}">{{action.name}}</option>
</select> </select>
</div> </div>
<kc-tooltip>Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip> <kc-tooltip>Require an action when the user logs in. 'Verify email' sends an email to the user to verify their email address. 'Update profile' requires user to enter in new personal information. 'Update password' requires user to enter in a new password. 'Configure TOTP' requires setup of a mobile password generator.</kc-tooltip>

View file

@ -10,6 +10,7 @@ import org.keycloak.models.UserFederationProvider;
import org.keycloak.models.UserFederationProviderFactory; import org.keycloak.models.UserFederationProviderFactory;
import org.keycloak.models.UserFederationProviderModel; import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultRequiredActions;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -28,10 +29,6 @@ public class MigrateTo1_3_0 {
public void migrate(KeycloakSession session) { public void migrate(KeycloakSession session) {
List<RealmModel> realms = session.realms().getRealms(); List<RealmModel> realms = session.realms().getRealms();
for (RealmModel realm : realms) { for (RealmModel realm : realms) {
if (realm.getAuthenticationFlows().size() == 0) {
DefaultAuthenticationFlows.addFlows(realm);
}
migrateLDAPProviders(session, realm); migrateLDAPProviders(session, realm);
} }

View file

@ -4,6 +4,7 @@ import org.keycloak.migration.ModelVersion;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultRequiredActions;
import java.util.List; import java.util.List;
@ -20,6 +21,7 @@ public class MigrateTo1_4_0 {
for (RealmModel realm : realms) { for (RealmModel realm : realms) {
if (realm.getAuthenticationFlows().size() == 0) { if (realm.getAuthenticationFlows().size() == 0) {
DefaultAuthenticationFlows.addFlows(realm); DefaultAuthenticationFlows.addFlows(realm);
DefaultRequiredActions.addActions(realm);
} }
} }

View file

@ -204,6 +204,7 @@ public interface RealmModel extends RoleContainerModel {
void updateRequiredActionProvider(RequiredActionProviderModel model); void updateRequiredActionProvider(RequiredActionProviderModel model);
void removeRequiredActionProvider(RequiredActionProviderModel model); void removeRequiredActionProvider(RequiredActionProviderModel model);
RequiredActionProviderModel getRequiredActionProviderById(String id); RequiredActionProviderModel getRequiredActionProviderById(String id);
RequiredActionProviderModel getRequiredActionProviderByAlias(String alias);
List<IdentityProviderModel> getIdentityProviders(); List<IdentityProviderModel> getIdentityProviders();
IdentityProviderModel getIdentityProviderByAlias(String alias); IdentityProviderModel getIdentityProviderByAlias(String alias);

View file

@ -11,6 +11,7 @@ public class RequiredActionProviderModel {
private String id; private String id;
private String alias; private String alias;
private String name;
private String providerId; private String providerId;
private boolean enabled; private boolean enabled;
private boolean defaultAction; private boolean defaultAction;
@ -33,6 +34,20 @@ public class RequiredActionProviderModel {
this.alias = alias; this.alias = alias;
} }
/**
* Used for display purposes. Probably should clean this code up and make alias and name the same, but
* the old code references an Enum and the admin console creates a "friendly" name for each enum.
*
* @return
*/
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
} }

View file

@ -9,6 +9,7 @@ import java.util.Map;
public class RequiredActionProviderEntity { public class RequiredActionProviderEntity {
protected String id; protected String id;
protected String alias; protected String alias;
protected String name;
protected String providerId; protected String providerId;
protected boolean enabled; protected boolean enabled;
protected boolean defaultAction; protected boolean defaultAction;
@ -30,6 +31,14 @@ public class RequiredActionProviderEntity {
this.alias = alias; this.alias = alias;
} }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
} }

View file

@ -0,0 +1,69 @@
package org.keycloak.models.utils;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.UserModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class DefaultRequiredActions {
public static void addActions(RealmModel realm) {
if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.VERIFY_EMAIL.name()) == null) {
RequiredActionProviderModel verifyEmail = new RequiredActionProviderModel();
verifyEmail.setEnabled(true);
verifyEmail.setAlias(UserModel.RequiredAction.VERIFY_EMAIL.name());
verifyEmail.setName("Verify Email");
verifyEmail.setProviderId(UserModel.RequiredAction.VERIFY_EMAIL.name());
verifyEmail.setDefaultAction(false);
realm.addRequiredActionProvider(verifyEmail);
}
if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_PROFILE.name()) == null) {
RequiredActionProviderModel updateProfile = new RequiredActionProviderModel();
updateProfile.setEnabled(true);
updateProfile.setAlias(UserModel.RequiredAction.UPDATE_PROFILE.name());
updateProfile.setName("Update Profile");
updateProfile.setProviderId(UserModel.RequiredAction.UPDATE_PROFILE.name());
updateProfile.setDefaultAction(false);
realm.addRequiredActionProvider(updateProfile);
}
if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name()) == null) {
RequiredActionProviderModel totp = new RequiredActionProviderModel();
totp.setEnabled(true);
totp.setAlias(UserModel.RequiredAction.CONFIGURE_TOTP.name());
totp.setName("Configure Totp");
totp.setProviderId(UserModel.RequiredAction.CONFIGURE_TOTP.name());
totp.setDefaultAction(false);
realm.addRequiredActionProvider(totp);
}
if (realm.getRequiredActionProviderByAlias(UserModel.RequiredAction.UPDATE_PASSWORD.name()) == null) {
RequiredActionProviderModel updatePassword = new RequiredActionProviderModel();
updatePassword.setEnabled(true);
updatePassword.setAlias(UserModel.RequiredAction.UPDATE_PASSWORD.name());
updatePassword.setName("Update Password");
updatePassword.setProviderId(UserModel.RequiredAction.UPDATE_PASSWORD.name());
updatePassword.setDefaultAction(false);
realm.addRequiredActionProvider(updatePassword);
}
if (realm.getRequiredActionProviderByAlias("terms_and_conditions") == null) {
RequiredActionProviderModel termsAndConditions = new RequiredActionProviderModel();
termsAndConditions.setEnabled(false);
termsAndConditions.setAlias("terms_and_conditions");
termsAndConditions.setName("Terms and Conditions");
termsAndConditions.setProviderId("terms_and_conditions");
termsAndConditions.setDefaultAction(false);
realm.addRequiredActionProvider(termsAndConditions);
}
}
}

View file

@ -1449,6 +1449,7 @@ public class RealmAdapter implements RealmModel {
RequiredActionProviderEntity auth = new RequiredActionProviderEntity(); RequiredActionProviderEntity auth = new RequiredActionProviderEntity();
auth.setId(KeycloakModelUtils.generateId()); auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias()); auth.setAlias(model.getAlias());
auth.setName(model.getName());
auth.setProviderId(model.getProviderId()); auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig()); auth.setConfig(model.getConfig());
auth.setEnabled(model.isEnabled()); auth.setEnabled(model.isEnabled());
@ -1477,6 +1478,7 @@ public class RealmAdapter implements RealmModel {
model.setId(entity.getId()); model.setId(entity.getId());
model.setProviderId(entity.getProviderId()); model.setProviderId(entity.getProviderId());
model.setAlias(entity.getAlias()); model.setAlias(entity.getAlias());
model.setName(entity.getName());
model.setEnabled(entity.isEnabled()); model.setEnabled(entity.isEnabled());
model.setDefaultAction(entity.isDefaultAction()); model.setDefaultAction(entity.isDefaultAction());
Map<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
@ -1492,6 +1494,7 @@ public class RealmAdapter implements RealmModel {
entity.setAlias(model.getAlias()); entity.setAlias(model.getAlias());
entity.setProviderId(model.getProviderId()); entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled()); entity.setEnabled(model.isEnabled());
entity.setName(model.getName());
entity.setDefaultAction(model.isDefaultAction()); entity.setDefaultAction(model.isDefaultAction());
if (entity.getConfig() == null) { if (entity.getConfig() == null) {
entity.setConfig(model.getConfig()); entity.setConfig(model.getConfig());
@ -1521,6 +1524,15 @@ public class RealmAdapter implements RealmModel {
return entity; return entity;
} }
@Override
public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) {
for (RequiredActionProviderModel action : getRequiredActionProviders()) {
if (action.getAlias().equals(alias)) return action;
}
return null;
}
@Override @Override

View file

@ -1161,4 +1161,10 @@ public class RealmAdapter implements RealmModel {
if (updated != null) return updated.getRequiredActionProviderById(id); if (updated != null) return updated.getRequiredActionProviderById(id);
return cached.getRequiredActionProviders().get(id); return cached.getRequiredActionProviders().get(id);
} }
@Override
public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) {
if (updated != null) return updated.getRequiredActionProviderByAlias(alias);
return cached.getRequiredActionProvidersByAlias().get(alias);
}
} }

View file

@ -83,6 +83,7 @@ public class CachedRealm {
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 Map<String, RequiredActionProviderModel> requiredActionProviders = new HashMap<>();
private Map<String, RequiredActionProviderModel> requiredActionProvidersByAlias = 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<>();
@ -204,6 +205,7 @@ public class CachedRealm {
} }
for (RequiredActionProviderModel action : model.getRequiredActionProviders()) { for (RequiredActionProviderModel action : model.getRequiredActionProviders()) {
requiredActionProviders.put(action.getId(), action); requiredActionProviders.put(action.getId(), action);
requiredActionProvidersByAlias.put(action.getAlias(), action);
} }
} }
@ -447,4 +449,8 @@ public class CachedRealm {
public Map<String, RequiredActionProviderModel> getRequiredActionProviders() { public Map<String, RequiredActionProviderModel> getRequiredActionProviders() {
return requiredActionProviders; return requiredActionProviders;
} }
public Map<String, RequiredActionProviderModel> getRequiredActionProvidersByAlias() {
return requiredActionProvidersByAlias;
}
} }

View file

@ -1732,6 +1732,7 @@ public class RealmAdapter implements RealmModel {
RequiredActionProviderEntity auth = new RequiredActionProviderEntity(); RequiredActionProviderEntity auth = new RequiredActionProviderEntity();
auth.setId(KeycloakModelUtils.generateId()); auth.setId(KeycloakModelUtils.generateId());
auth.setAlias(model.getAlias()); auth.setAlias(model.getAlias());
auth.setName(model.getName());
auth.setRealm(realm); auth.setRealm(realm);
auth.setProviderId(model.getProviderId()); auth.setProviderId(model.getProviderId());
auth.setConfig(model.getConfig()); auth.setConfig(model.getConfig());
@ -1767,6 +1768,7 @@ public class RealmAdapter implements RealmModel {
model.setAlias(entity.getAlias()); model.setAlias(entity.getAlias());
model.setEnabled(entity.isEnabled()); model.setEnabled(entity.isEnabled());
model.setDefaultAction(entity.isDefaultAction()); model.setDefaultAction(entity.isDefaultAction());
model.setName(entity.getName());
Map<String, String> config = new HashMap<>(); Map<String, String> config = new HashMap<>();
if (entity.getConfig() != null) config.putAll(entity.getConfig()); if (entity.getConfig() != null) config.putAll(entity.getConfig());
model.setConfig(config); model.setConfig(config);
@ -1781,6 +1783,7 @@ public class RealmAdapter implements RealmModel {
entity.setProviderId(model.getProviderId()); entity.setProviderId(model.getProviderId());
entity.setEnabled(model.isEnabled()); entity.setEnabled(model.isEnabled());
entity.setDefaultAction(model.isDefaultAction()); entity.setDefaultAction(model.isDefaultAction());
entity.setName(model.getName());
if (entity.getConfig() == null) { if (entity.getConfig() == null) {
entity.setConfig(model.getConfig()); entity.setConfig(model.getConfig());
} else { } else {
@ -1799,4 +1802,12 @@ public class RealmAdapter implements RealmModel {
} }
return actions; return actions;
} }
@Override
public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) {
for (RequiredActionProviderModel action : getRequiredActionProviders()) {
if (action.getAlias().equals(alias)) return action;
}
return null;
}
} }

View file

@ -30,6 +30,9 @@ public class RequiredActionProviderEntity {
@Column(name="ALIAS") @Column(name="ALIAS")
protected String alias; protected String alias;
@Column(name="NAME")
protected String name;
@ManyToOne(fetch = FetchType.LAZY) @ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "REALM_ID") @JoinColumn(name = "REALM_ID")
protected RealmEntity realm; protected RealmEntity realm;
@ -104,4 +107,12 @@ public class RequiredActionProviderEntity {
public void setConfig(Map<String, String> config) { public void setConfig(Map<String, String> config) {
this.config = config; this.config = config;
} }
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} }

View file

@ -1607,6 +1607,15 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
return entity; return entity;
} }
@Override
public RequiredActionProviderModel getRequiredActionProviderByAlias(String alias) {
for (RequiredActionProviderModel action : getRequiredActionProviders()) {
if (action.getAlias().equals(alias)) return action;
}
return null;
}

View file

@ -17,6 +17,7 @@ import org.keycloak.jose.jws.JWSBuilder;
import org.keycloak.login.LoginFormsProvider; import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.ClientSessionModel; import org.keycloak.models.ClientSessionModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.UserConsentModel; import org.keycloak.models.UserConsentModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel; import org.keycloak.models.ProtocolMapperModel;
@ -483,8 +484,9 @@ public class AuthenticationManager {
}; };
// see if any required actions need triggering, i.e. an expired password // see if any required actions need triggering, i.e. an expired password
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class)) { for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
RequiredActionProvider provider = ((RequiredActionFactory)factory).create(session); if (!model.isEnabled()) continue;
RequiredActionProvider provider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
provider.evaluateTriggers(context); provider.evaluateTriggers(context);
} }
@ -495,7 +497,8 @@ public class AuthenticationManager {
Set<String> requiredActions = user.getRequiredActions(); Set<String> requiredActions = user.getRequiredActions();
for (String action : requiredActions) { for (String action : requiredActions) {
RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, action); RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(action);
RequiredActionProvider actionProvider = session.getProvider(RequiredActionProvider.class, model.getProviderId());
Response challenge = actionProvider.invokeRequiredAction(context); Response challenge = actionProvider.invokeRequiredAction(context);
if (challenge != null) { if (challenge != null) {
return challenge; return challenge;

View file

@ -17,6 +17,7 @@ import org.keycloak.models.UserFederationProviderModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionProvider; import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.utils.DefaultAuthenticationFlows; import org.keycloak.models.utils.DefaultAuthenticationFlows;
import org.keycloak.models.utils.DefaultRequiredActions;
import org.keycloak.models.utils.KeycloakModelUtils; import org.keycloak.models.utils.KeycloakModelUtils;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
@ -88,6 +89,7 @@ public class RealmManager {
setupBrokerService(realm); setupBrokerService(realm);
setupAdminConsole(realm); setupAdminConsole(realm);
setupAuthenticationFlows(realm); setupAuthenticationFlows(realm);
setupRequiredActions(realm);
return realm; return realm;
} }
@ -96,6 +98,10 @@ public class RealmManager {
if (realm.getAuthenticationFlows().size() == 0) DefaultAuthenticationFlows.addFlows(realm); if (realm.getAuthenticationFlows().size() == 0) DefaultAuthenticationFlows.addFlows(realm);
} }
protected void setupRequiredActions(RealmModel realm) {
if (realm.getRequiredActionProviders().size() == 0) DefaultRequiredActions.addActions(realm);
}
protected void setupAdminConsole(RealmModel realm) { protected void setupAdminConsole(RealmModel realm) {
ClientModel adminConsole = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID); ClientModel adminConsole = realm.getClientByClientId(Constants.ADMIN_CONSOLE_CLIENT_ID);
if (adminConsole == null) adminConsole = new ClientManager(this).createClient(realm, Constants.ADMIN_CONSOLE_CLIENT_ID); if (adminConsole == null) adminConsole = new ClientManager(this).createClient(realm, Constants.ADMIN_CONSOLE_CLIENT_ID);
@ -261,6 +267,7 @@ public class RealmManager {
RepresentationToModel.importRealm(session, rep, realm); RepresentationToModel.importRealm(session, rep, realm);
setupAuthenticationFlows(realm); setupAuthenticationFlows(realm);
setupRequiredActions(realm);
// Refresh periodic sync tasks for configured federationProviders // Refresh periodic sync tasks for configured federationProviders
List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders(); List<UserFederationProviderModel> federationProviders = realm.getUserFederationProviders();

View file

@ -6,13 +6,18 @@ import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.authentication.Authenticator; import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory; import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.AuthenticatorUtil; import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.authentication.RequiredActionFactory;
import org.keycloak.authentication.RequiredActionProvider;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorModel; import org.keycloak.models.AuthenticatorModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.provider.ProviderFactory;
import javax.ws.rs.Consumes; import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path; import javax.ws.rs.Path;
@ -20,8 +25,10 @@ import javax.ws.rs.PathParam;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response; import javax.ws.rs.core.Response;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map;
import static javax.ws.rs.core.Response.Status.NOT_FOUND; import static javax.ws.rs.core.Response.Status.NOT_FOUND;
@ -176,4 +183,118 @@ public class AuthenticationManagementResource {
realm.updateAuthenticatorExecution(model); realm.updateAuthenticatorExecution(model);
} }
} }
public static class RequiredActionProviderRepresentation {
private String alias;
private String name;
private boolean enabled;
private boolean defaultAction;
private Map<String, String> config = new HashMap<String, String>();
public String getAlias() {
return alias;
}
public void setAlias(String alias) {
this.alias = alias;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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 Map<String, String> getConfig() {
return config;
}
public void setConfig(Map<String, String> config) {
this.config = config;
}
}
@Path("required-actions")
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<RequiredActionProviderRepresentation> getRequiredActions() {
List<RequiredActionProviderRepresentation> list = new LinkedList<>();
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
RequiredActionProviderRepresentation rep = toRepresentation(model);
list.add(rep);
}
return list;
}
public static RequiredActionProviderRepresentation toRepresentation(RequiredActionProviderModel model) {
RequiredActionProviderRepresentation rep = new RequiredActionProviderRepresentation();
rep.setAlias(model.getAlias());
rep.setName(model.getName());
rep.setDefaultAction(model.isDefaultAction());
rep.setEnabled(model.isEnabled());
rep.setConfig(model.getConfig());
return rep;
}
@Path("required-actions/{alias}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
}
return toRepresentation(model);
}
@Path("required-actions/{alias}")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
public void updateRequiredAction(@PathParam("alias") String alias, RequiredActionProviderRepresentation rep) {
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
}
RequiredActionProviderModel update = new RequiredActionProviderModel();
update.setId(update.getId());
update.setName(rep.getName());
update.setAlias(rep.getAlias());
update.setProviderId(model.getProviderId());
update.setDefaultAction(rep.isDefaultAction());
update.setEnabled(rep.isEnabled());
update.setConfig(rep.getConfig());
realm.updateRequiredActionProvider(update);
}
@Path("required-actions/{alias}")
@DELETE
public void updateRequiredAction(@PathParam("alias") String alias) {
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
throw new NotFoundException("Failed to find required action: " + alias);
}
realm.removeRequiredActionProvider(model);
}
} }

View file

@ -565,18 +565,4 @@ public class RealmAdminResource {
return new IdentityProvidersResource(realm, session, this.auth, adminEvent); return new IdentityProvidersResource(realm, session, this.auth, adminEvent);
} }
@Path("required-actions")
@GET
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getRequiredActions() {
List<Map<String, String>> list = new LinkedList<>();
for (ProviderFactory factory : session.getKeycloakSessionFactory().getProviderFactories(RequiredActionProvider.class)) {
RequiredActionFactory actionFactory = (RequiredActionFactory)factory;
Map<String, String> data = new HashMap<>();
data.put("id", actionFactory.getId());
data.put("text", actionFactory.getDisplayText());
list.add(data);
}
return list;
}
} }