Merge pull request #1430 from patriot1burke/master

auth config ui
This commit is contained in:
Bill Burke 2015-07-06 17:56:16 -04:00
commit 3c88d9666d
17 changed files with 525 additions and 24 deletions

View file

@ -978,7 +978,7 @@ module.config([ '$routeProvider', function($routeProvider) {
},
clients : function(ClientListLoader) {
return ClientListLoader();
},
}
},
controller : 'UserFederationMapperCtrl'
})
@ -1066,6 +1066,36 @@ module.config([ '$routeProvider', function($routeProvider) {
},
controller : 'RealmPasswordPolicyCtrl'
})
.when('/realms/:realm/authentication/config/:provider/:config', {
templateUrl : resourceUrl + '/partials/authenticator-config.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
configType : function(AuthenticationConfigDescriptionLoader) {
return AuthenticationConfigDescriptionLoader();
},
config : function(AuthenticationConfigLoader) {
return AuthenticationConfigLoader();
}
},
controller : 'AuthenticationConfigCtrl'
})
.when('/create/authentication/:realm/execution/:executionId/provider/:provider', {
templateUrl : resourceUrl + '/partials/authenticator-config.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
configType : function(AuthenticationConfigDescriptionLoader) {
return AuthenticationConfigDescriptionLoader();
},
execution : function(ExecutionIdLoader) {
return ExecutionIdLoader();
}
},
controller : 'AuthenticationConfigCreateCtrl'
})
.when('/server-info', {
templateUrl : resourceUrl + '/partials/server-info.html'
})

View file

@ -1634,6 +1634,93 @@ module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions
});
module.controller('AuthenticationConfigCtrl', function($scope, realm, configType, config, AuthenticationConfig, Notifications, Dialog, $location) {
$scope.realm = realm;
$scope.configType = configType;
$scope.create = false;
$scope.config = angular.copy(config);
$scope.changed = false;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.$watch('config', function() {
if (!angular.equals($scope.config, config)) {
$scope.changed = true;
}
}, true);
$scope.save = function() {
AuthenticationConfig.update({
realm : realm.realm,
config : config.id
}, $scope.config, function() {
$scope.changed = false;
config = angular.copy($scope.config);
$location.url("/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + config.id);
Notifications.success("Your changes have been saved.");
});
};
$scope.reset = function() {
$scope.config = angular.copy(config);
$scope.changed = false;
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
$scope.remove = function() {
Dialog.confirmDelete($scope.config.alias, 'config', function() {
AuthenticationConfig.remove({ realm: realm.realm, config : $scope.config.id }, function() {
Notifications.success("The config has been deleted.");
$location.url("/realms/" + realm.realm + '/authentication/flows');
});
});
};
});
module.controller('AuthenticationConfigCreateCtrl', function($scope, realm, configType, execution, AuthenticationExecutionConfig, Notifications, Dialog, $location) {
$scope.realm = realm;
$scope.create = true;
$scope.config = { config: {}};
$scope.configType = configType;
$scope.$watch(function() {
return $location.path();
}, function() {
$scope.path = $location.path().substring(1).split("/");
});
$scope.save = function() {
AuthenticationExecutionConfig.save({
realm : realm.realm,
execution: execution
}, $scope.config, function(data, headers) {
var l = headers().location;
var id = l.substring(l.lastIndexOf("/") + 1);
var url = "/realms/" + realm.realm + '/authentication/config/' + configType.providerId + "/" + id;
console.log('redirect url: ' + url);
$location.url(url);
Notifications.success("Config has been created.");
});
};
$scope.cancel = function() {
//$location.url("/realms");
window.history.back();
};
});

View file

@ -348,5 +348,29 @@ module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows
});
});
module.factory('AuthenticationConfigDescriptionLoader', function(Loader, AuthenticationConfigDescription, $route, $q) {
return Loader.get(AuthenticationConfigDescription, function () {
return {
realm: $route.current.params.realm,
provider: $route.current.params.provider
}
});
});
module.factory('ExecutionIdLoader', function($route) {
return function() { return $route.current.params.executionId; };
});
module.factory('AuthenticationConfigLoader', function(Loader, AuthenticationConfig, $route, $q) {
return Loader.get(AuthenticationConfig, function () {
return {
realm: $route.current.params.realm,
config: $route.current.params.config
}
});
});

View file

@ -1094,5 +1094,28 @@ module.factory('AuthenticationFlows', function($resource) {
realm : '@realm'
});
});
module.factory('AuthenticationConfigDescription', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
realm : '@realm',
provider: '@provider'
});
});
module.factory('AuthenticationConfig', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/config/:config', {
realm : '@realm',
config: '@config'
}, {
update: {
method : 'PUT'
}
});
});
module.factory('AuthenticationExecutionConfig', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/executions/:execution/config', {
realm : '@realm',
execution: '@execution'
});
});

View file

@ -6,7 +6,7 @@
<table class="table table-striped table-bordered">
<thead>
<tr>
<th colspan="5" class="kc-table-actions">
<th colspan="6" class="kc-table-actions">
<div class="dropdown pull-left">
<select class="form-control" ng-model="flow"
ng-options="flow.alias for flow in flows"
@ -18,6 +18,7 @@
<tr data-ng-hide="executions.length == 0">
<th colspan="2">Auth Type</th>
<th colspan="{{flowmax}}">Requirement</th>
<th></th>
</tr>
</thead>
<tbody>
@ -49,6 +50,10 @@
</td>
<td ng-repeat="emptee in execution.empties"></td>
<td>
<a data-ng-show="execution.configurable && execution.authenticationConfig == null" class="btn btn-default" href="#/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Configure</a>
<a data-ng-show="execution.configurable && execution.authenticationConfig != null" class="btn btn-default" href="#/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Configure</a>
</td>
</tr>
<tr data-ng-show="executions.length == 0">
<td>No executions available</td>

View file

@ -0,0 +1,64 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<ol class="breadcrumb">
<li><a href="#/realms/{{realm.realm}}/authentication/flows">Authentication Flows</a></li>
<li class="active" data-ng-show="create">Create Authenticator Config</li>
<li class="active" data-ng-hide="create">{{config.alias}}</li>
</ol>
<h1 data-ng-show="create">Create Authenticator Config</h1>
<h1 data-ng-hide="create">{{config.alias|capitalize}}<a><i style="padding-left: 20px" class="pficon pficon-delete" data-ng-show="!create && access.manageRealm"
data-ng-hide="changed" data-ng-click="remove()"></i></a></h1>
<form class="form-horizontal" name="realmForm" novalidate kc-read-only="!access.manageRealm">
<fieldset>
<div class="form-group clearfix" data-ng-show="!create">
<label class="col-md-2 control-label" for="configId">ID </label>
<div class="col-md-6">
<input class="form-control" id="configId" type="text" ng-model="config.id" readonly>
</div>
</div>
<div class="form-group clearfix">
<label class="col-md-2 control-label" for="name">Alias</label>
<div class="col-md-6">
<input class="form-control" id="name" type="text" ng-model="config.alias" data-ng-readonly="!create">
</div>
<kc-tooltip>Name of the configuration.</kc-tooltip>
</div>
<div data-ng-repeat="option in configType.properties" class="form-group">
<label class="col-md-2 control-label">{{option.label}}</label>
<div class="col-sm-4" data-ng-hide="option.type == 'boolean' || option.type == 'List'">
<input class="form-control" type="text" data-ng-model="config.config[ option.name ]" >
</div>
<div class="col-sm-4" data-ng-show="option.type == 'boolean'">
<input ng-model="config.config[ option.name ]" value="'true'" name="option.name" id="option.name" onoffswitchmodel />
</div>
<div class="col-sm-4" data-ng-show="option.type == 'List'">
<select ng-model="config.config[ option.name ]" ng-options="data for data in option.defaultValue">
<option value="" selected> Select one... </option>
</select>
</div>
<kc-tooltip>{{option.helpText}}</kc-tooltip>
</div>
</fieldset>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageRealm">
<button kc-save>Save</button>
<button kc-cancel data-ng-click="cancel()">Cancel</button>
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageRealm">
<button kc-save data-ng-show="changed">Save</button>
<button kc-reset data-ng-show="changed">Cancel</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -110,7 +110,7 @@
<#if recaptchaRequired??>
<div class="form-group">
<div class="${properties.kcInputWrapperClass!}">
<div class="g-recaptcha" data-sitekey="${recaptchaSiteKey}"></div>
<div class="g-recaptcha" data-size="compact" data-sitekey="${recaptchaSiteKey}"></div>
</div>
</div>
</#if>

View file

@ -32,6 +32,8 @@ public class AuthenticatorConfigModel implements Serializable {
this.alias = alias;
}
public Map<String, String> getConfig() {
return config;
}

View file

@ -9,7 +9,7 @@ import org.keycloak.provider.ProviderFactory;
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfiguredProvider, ConfigurableAuthenticatorFactory {
public interface AuthenticatorFactory extends ProviderFactory<Authenticator>, ConfigurableAuthenticatorFactory {
Authenticator create();
}

View file

@ -1,12 +1,13 @@
package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.provider.ConfiguredProvider;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface ConfigurableAuthenticatorFactory {
public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
String getDisplayType();
/**

View file

@ -57,7 +57,7 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
@Override
public boolean isConfigurable() {
return true;
return false;
}
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED

View file

@ -8,8 +8,10 @@ import org.keycloak.login.LoginFormsProvider;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderConfigProperty;
import javax.ws.rs.core.Response;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -41,6 +43,16 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
return "Registration Page";
}
@Override
public String getHelpText() {
return null;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public String getReferenceCategory() {
return null;

View file

@ -16,6 +16,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.CredentialRepresentation;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
@ -31,6 +32,16 @@ import java.util.List;
public class RegistrationPassword implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "registration-password-action";
@Override
public String getHelpText() {
return null;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public void validate(ValidationContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();

View file

@ -14,6 +14,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.validation.Validation;
@ -28,6 +29,15 @@ import java.util.List;
public class RegistrationProfile implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "registration-profile-action";
@Override
public String getHelpText() {
return null;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public void validate(ValidationContext context) {

View file

@ -23,6 +23,7 @@ import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.messages.Messages;
@ -43,6 +44,8 @@ import java.util.Map;
public class RegistrationRecaptcha implements FormAction, FormActionFactory, ConfiguredProvider {
public static final String G_RECAPTCHA_RESPONSE = "g-recaptcha-response";
public static final String RECAPTCHA_REFERENCE_CATEGORY = "recaptcha";
public static final String SITE_KEY = "site.key";
public static final String SITE_SECRET = "secret";
protected static Logger logger = Logger.getLogger(RegistrationRecaptcha.class);
public static final String PROVIDER_ID = "registration-recaptcha-action";
@ -74,13 +77,13 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
public void buildPage(FormContext context, LoginFormsProvider form) {
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
if (captchaConfig == null || captchaConfig.getConfig() == null
|| captchaConfig.getConfig().get("site.key") == null
|| captchaConfig.getConfig().get("secret") == null
|| captchaConfig.getConfig().get(SITE_KEY) == null
|| captchaConfig.getConfig().get(SITE_SECRET) == null
) {
form.addError(new FormMessage(null, Messages.RECAPTCHA_NOT_CONFIGURED));
return;
}
String siteKey = captchaConfig.getConfig().get("site.key");
String siteKey = captchaConfig.getConfig().get(SITE_KEY);
form.setAttribute("recaptchaRequired", true);
form.setAttribute("recaptchaSiteKey", siteKey);
List<String> scripts = new LinkedList<>();
@ -98,7 +101,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
String captcha = formData.getFirst(G_RECAPTCHA_RESPONSE);
if (!Validation.isBlank(captcha)) {
AuthenticatorConfigModel captchaConfig = context.getAuthenticatorConfig();
String secret = captchaConfig.getConfig().get("secret");
String secret = captchaConfig.getConfig().get(SITE_SECRET);
HttpClient httpClient = context.getSession().getProvider(HttpClientProvider.class).getHttpClient();
HttpPost post = new HttpPost("https://www.google.com/recaptcha/api/siteverify");
@ -185,8 +188,28 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
return null;
}
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();
static {
ProviderConfigProperty property;
property = new ProviderConfigProperty();
property.setName(SITE_KEY);
property.setLabel("Recaptcha Site Key");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Google Recaptcha Site Key");
configProperties.add(property);
property = new ProviderConfigProperty();
property.setName(SITE_SECRET);
property.setLabel("Recaptcha Secret");
property.setType(ProviderConfigProperty.STRING_TYPE);
property.setHelpText("Google Recaptcha Secret");
configProperties.add(property);
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
return configProperties;
}
}

View file

@ -16,6 +16,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.utils.FormMessage;
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.resources.AttributeFormDataProcessor;
import org.keycloak.services.validation.Validation;
@ -32,6 +33,16 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
public static final String PROVIDER_ID = "registration-user-creation";
@Override
public String getHelpText() {
return null;
}
@Override
public List<ProviderConfigProperty> getConfigProperties() {
return null;
}
@Override
public void validate(ValidationContext context) {
MultivaluedMap<String, String> formData = context.getHttpRequest().getDecodedFormParameters();

View file

@ -8,24 +8,29 @@ import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorFactory;
import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.authentication.DefaultAuthenticationFlow;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormActionFactory;
import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@ -42,6 +47,9 @@ public class AuthenticationManagementResource {
private final KeycloakSession session;
private RealmAuth auth;
private AdminEventBuilder adminEvent;
@Context
private UriInfo uriInfo;
private static Logger logger = Logger.getLogger(AuthenticationManagementResource.class);
public AuthenticationManagementResource(RealmModel realm, KeycloakSession session, RealmAuth auth, AdminEventBuilder adminEvent) {
@ -53,19 +61,21 @@ public class AuthenticationManagementResource {
}
public static class AuthenticationExecutionRepresentation {
protected String execution;
protected String id;
protected String referenceType;
protected String requirement;
protected List<String> requirementChoices;
protected Boolean configurable;
protected Boolean subFlow;
protected String providerId;
protected String authenticationConfig;
public String getExecution() {
return execution;
public String getId() {
return id;
}
public void setExecution(String execution) {
this.execution = execution;
public void setId(String execution) {
this.id = execution;
}
public String getReferenceType() {
@ -107,6 +117,22 @@ public class AuthenticationManagementResource {
public void setSubFlow(Boolean subFlow) {
this.subFlow = subFlow;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public String getAuthenticationConfig() {
return authenticationConfig;
}
public void setAuthenticationConfig(String authenticationConfig) {
this.authenticationConfig = authenticationConfig;
}
}
@Path("/flows")
@ -150,27 +176,30 @@ public class AuthenticationManagementResource {
} else if (AuthenticationFlow.FORM_FLOW.equals(flowRef.getProviderId())) {
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.REQUIRED.name());
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
rep.setProviderId(execution.getAuthenticator());
rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
}
rep.setReferenceType(flowRef.getAlias());
rep.setConfigurable(false);
rep.setExecution(execution.getId());
rep.setId(execution.getId());
rep.setRequirement(execution.getRequirement().name());
result.add(rep);
} else {
if (!flow.getId().equals(execution.getParentFlow())) {
rep.setSubFlow(true);
}
ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, execution.getAuthenticator());
if (factory == null) {
factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, execution.getAuthenticator());
}
String providerId = execution.getAuthenticator();
ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(providerId);
rep.setReferenceType(factory.getDisplayType());
rep.setConfigurable(factory.isConfigurable());
for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
rep.getRequirementChoices().add(choice.name());
}
rep.setExecution(execution.getId());
rep.setId(execution.getId());
rep.setRequirement(execution.getRequirement().name());
rep.setProviderId(execution.getAuthenticator());
rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
result.add(rep);
}
@ -179,6 +208,14 @@ public class AuthenticationManagementResource {
return Response.ok(result).build();
}
public ConfigurableAuthenticatorFactory getConfigurableAuthenticatorFactory(String providerId) {
ConfigurableAuthenticatorFactory factory = (AuthenticatorFactory)session.getKeycloakSessionFactory().getProviderFactory(Authenticator.class, providerId);
if (factory == null) {
factory = (FormActionFactory)session.getKeycloakSessionFactory().getProviderFactory(FormAction.class, providerId);
}
return factory;
}
@Path("/flows/{flowAlias}/executions")
@PUT
@NoCache
@ -192,7 +229,7 @@ public class AuthenticationManagementResource {
throw new NotFoundException("flow not found");
}
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(rep.getExecution());
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(rep.getId());
if (model == null) {
session.getTransaction().setRollbackOnly();
throw new NotFoundException("Illegal execution");
@ -204,6 +241,39 @@ public class AuthenticationManagementResource {
}
}
@Path("/executions/{executionId}/config")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response newExecutionConfig(@PathParam("executionId") String execution, AuthenticatorConfigModel config) {
this.auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
session.getTransaction().setRollbackOnly();
throw new NotFoundException("Illegal execution");
}
config = realm.addAuthenticatorConfig(config);
model.setAuthenticatorConfig(config.getId());
realm.updateAuthenticatorExecution(model);
return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
}
@Path("/executions/{executionId}/config/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("executionId") String execution,@PathParam("id") String id) {
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
}
return config;
}
public static class RequiredActionProviderRepresentation {
private String alias;
private String name;
@ -256,6 +326,7 @@ public class AuthenticationManagementResource {
@Path("required-actions")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public List<RequiredActionProviderRepresentation> getRequiredActions() {
List<RequiredActionProviderRepresentation> list = new LinkedList<>();
for (RequiredActionProviderModel model : realm.getRequiredActionProviders()) {
@ -278,6 +349,7 @@ public class AuthenticationManagementResource {
@Path("required-actions/{alias}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public RequiredActionProviderRepresentation getRequiredAction(@PathParam("alias") String alias) {
RequiredActionProviderModel model = realm.getRequiredActionProviderByAlias(alias);
if (model == null) {
@ -316,5 +388,131 @@ public class AuthenticationManagementResource {
realm.removeRequiredActionProvider(model);
}
public class AuthenticatorConfigDescription {
protected String name;
protected String providerId;
protected String helpText;
protected List<ConfigPropertyRepresentation> properties;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getHelpText() {
return helpText;
}
public String getProviderId() {
return providerId;
}
public void setProviderId(String providerId) {
this.providerId = providerId;
}
public void setHelpText(String helpText) {
this.helpText = helpText;
}
public List<ConfigPropertyRepresentation> getProperties() {
return properties;
}
public void setProperties(List<ConfigPropertyRepresentation> properties) {
this.properties = properties;
}
}
@Path("config-description/{providerId}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigDescription getAuthenticatorConfigDescription(@PathParam("providerId") String providerId) {
ConfigurableAuthenticatorFactory factory = getConfigurableAuthenticatorFactory(providerId);
if (factory == null) {
throw new NotFoundException("Could not find authenticator provider");
}
AuthenticatorConfigDescription rep = new AuthenticatorConfigDescription();
rep.setProviderId(providerId);
rep.setName(factory.getDisplayType());
rep.setHelpText(factory.getHelpText());
rep.setProperties(new LinkedList<ConfigPropertyRepresentation>());
List<ProviderConfigProperty> configProperties = factory.getConfigProperties();
for (ProviderConfigProperty prop : configProperties) {
ConfigPropertyRepresentation propRep = new ConfigPropertyRepresentation();
propRep.setName(prop.getName());
propRep.setLabel(prop.getLabel());
propRep.setType(prop.getType());
propRep.setDefaultValue(prop.getDefaultValue());
propRep.setHelpText(prop.getHelpText());
rep.getProperties().add(propRep);
}
return rep;
}
@Path("config")
@POST
@NoCache
public Response createAuthenticatorConfig(AuthenticatorConfigModel config) {
config = realm.addAuthenticatorConfig(config);
return Response.created(uriInfo.getAbsolutePathBuilder().path(config.getId()).build()).build();
}
@Path("config/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
@NoCache
public AuthenticatorConfigModel getAuthenticatorConfig(@PathParam("id") String id) {
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
}
return config;
}
@Path("config/{id}")
@DELETE
@NoCache
public void removeAuthenticatorConfig(@PathParam("id") String id) {
AuthenticatorConfigModel config = realm.getAuthenticatorConfigById(id);
if (config == null) {
throw new NotFoundException("Could not find authenticator config");
}
List<AuthenticationFlowModel> flows = new LinkedList<>();
for (AuthenticationFlowModel flow : realm.getAuthenticationFlows()) {
for (AuthenticationExecutionModel exe : realm.getAuthenticationExecutions(flow.getId())) {
if (id.equals(exe.getAuthenticatorConfig())) {
exe.setAuthenticatorConfig(null);
realm.updateAuthenticatorExecution(exe);
}
}
}
realm.removeAuthenticatorConfig(config);
}
@Path("config/{id}")
@PUT
@Consumes(MediaType.APPLICATION_JSON)
@NoCache
public void updateAuthenticatorConfig(@PathParam("id") String id, AuthenticatorConfigModel config) {
AuthenticatorConfigModel exists = realm.getAuthenticatorConfigById(id);
if (exists == null) {
throw new NotFoundException("Could not find authenticator config");
}
exists.setAlias(config.getAlias());
exists.setConfig(config.getConfig());
realm.updateAuthenticatorConfig(exists);
}
}