commit
3c88d9666d
17 changed files with 525 additions and 24 deletions
|
@ -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'
|
||||
})
|
||||
|
|
|
@ -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();
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -32,6 +32,8 @@ public class AuthenticatorConfigModel implements Serializable {
|
|||
this.alias = alias;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Map<String, String> getConfig() {
|
||||
return config;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
Loading…
Reference in a new issue