Merge pull request #1518 from patriot1burke/master

auth flow editing
This commit is contained in:
Bill Burke 2015-08-03 19:26:11 -04:00
commit 3b2e679aed
41 changed files with 1091 additions and 162 deletions

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-3.1.xsd">
<changeSet author="bburke@redhat.com" id="1.5.0">
<delete tableName="CLIENT_SESSION_AUTH_STATUS"/>
<delete tableName="CLIENT_SESSION_ROLE"/>
<delete tableName="CLIENT_SESSION_PROT_MAPPER"/>
<delete tableName="CLIENT_SESSION_NOTE"/>
<delete tableName="CLIENT_SESSION"/>
<delete tableName="USER_SESSION_NOTE"/>
<delete tableName="USER_SESSION"/>
<dropColumn tableName="AUTHENTICATION_EXECUTION" columnName="USER_SETUP_ALLOWED"/>
</changeSet>
</databaseChangeLog>

View file

@ -8,4 +8,5 @@
<include file="META-INF/jpa-changelog-1.2.0.Final.xml"/> <include file="META-INF/jpa-changelog-1.2.0.Final.xml"/>
<include file="META-INF/jpa-changelog-1.3.0.xml"/> <include file="META-INF/jpa-changelog-1.3.0.xml"/>
<include file="META-INF/jpa-changelog-1.4.0.xml"/> <include file="META-INF/jpa-changelog-1.4.0.xml"/>
<include file="META-INF/jpa-changelog-1.5.0.xml"/>
</databaseChangeLog> </databaseChangeLog>

View file

@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
public String FIRST_VERSION = "1.0.0.Final"; public String FIRST_VERSION = "1.0.0.Final";
public String LAST_VERSION = "1.4.0"; public String LAST_VERSION = "1.5.0";
public String getCurrentVersionSql(String defaultSchema); public String getCurrentVersionSql(String defaultSchema);

View file

@ -1080,10 +1080,70 @@ module.config([ '$routeProvider', function($routeProvider) {
}, },
flows : function(AuthenticationFlowsLoader) { flows : function(AuthenticationFlowsLoader) {
return AuthenticationFlowsLoader(); return AuthenticationFlowsLoader();
},
selectedFlow : function() {
return null;
} }
}, },
controller : 'AuthenticationFlowsCtrl' controller : 'AuthenticationFlowsCtrl'
}) })
.when('/realms/:realm/authentication/flows/:flow', {
templateUrl : resourceUrl + '/partials/authentication-flows.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
flows : function(AuthenticationFlowsLoader) {
return AuthenticationFlowsLoader();
},
selectedFlow : function($route) {
return $route.current.params.flow;
}
},
controller : 'AuthenticationFlowsCtrl'
})
.when('/realms/:realm/authentication/flows/:flow/create/execution', {
templateUrl : resourceUrl + '/partials/create-execution.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
parentFlow : function(AuthenticationFlowLoader) {
return AuthenticationFlowLoader();
},
formActionProviders : function(AuthenticationFormActionProvidersLoader) {
return AuthenticationFormActionProvidersLoader();
},
authenticatorProviders : function(AuthenticatorProvidersLoader) {
return AuthenticatorProvidersLoader();
}
},
controller : 'CreateExecutionCtrl'
})
.when('/realms/:realm/authentication/flows/:flow/create/flow/execution', {
templateUrl : resourceUrl + '/partials/create-flow-execution.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
},
parentFlow : function(AuthenticationFlowLoader) {
return AuthenticationFlowLoader();
},
formProviders : function(AuthenticationFormProvidersLoader) {
return AuthenticationFormProvidersLoader();
}
},
controller : 'CreateExecutionFlowCtrl'
})
.when('/realms/:realm/authentication/create/flow', {
templateUrl : resourceUrl + '/partials/create-flow.html',
resolve : {
realm : function(RealmLoader) {
return RealmLoader();
}
},
controller : 'CreateFlowCtrl'
})
.when('/realms/:realm/authentication/required-actions', { .when('/realms/:realm/authentication/required-actions', {
templateUrl : resourceUrl + '/partials/required-actions.html', templateUrl : resourceUrl + '/partials/required-actions.html',
resolve : { resolve : {

View file

@ -1616,41 +1616,222 @@ module.controller('IdentityProviderMapperCreateCtrl', function($scope, realm, id
}); });
module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, AuthenticationExecutions, Notifications, Dialog, $location) { module.controller('CreateFlowCtrl', function($scope, realm,
AuthenticationFlows,
Notifications, $location) {
$scope.realm = realm;
$scope.flow = {
alias: "",
providerId: "basic-flow",
description: "",
topLevel: true,
builtIn: false
}
$scope.save = function() {
AuthenticationFlows.save({realm: realm.realm, flow: ""}, $scope.flow, function() {
$location.url("/realms/" + realm.realm + "/authentication/flows/" + $scope.flow.alias);
Notifications.success("Flow Created.");
})
}
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/authentication/flows");
};
});
module.controller('CreateExecutionFlowCtrl', function($scope, realm, parentFlow, formProviders,
CreateExecutionFlow,
Notifications, $location) {
$scope.realm = realm;
$scope.formProviders = formProviders;
$scope.flow = {
alias: "",
type: "basic-flow",
description: ""
}
$scope.provider = {};
if (formProviders.length > 0) {
$scope.provider = formProviders[0];
}
$scope.save = function() {
$scope.flow.provider = $scope.provider.id;
CreateExecutionFlow.save({realm: realm.realm, alias: parentFlow.alias}, $scope.flow, function() {
$location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
Notifications.success("Flow Created.");
})
}
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
};
});
module.controller('CreateExecutionCtrl', function($scope, realm, parentFlow, formActionProviders, authenticatorProviders,
CreateExecution,
Notifications, $location) {
$scope.realm = realm;
$scope.parentFlow = parentFlow;
console.log('parentFlow.providerId: ' + parentFlow.providerId);
if (parentFlow.providerId == 'form-flow') {
$scope.providers = formActionProviders;
} else {
$scope.providers = authenticatorProviders;
}
$scope.provider = {};
if ($scope.providers.length > 0) {
$scope.provider = $scope.providers[0];
}
$scope.save = function() {
var execution = {
provider: $scope.provider.id
}
CreateExecution.save({realm: realm.realm, alias: parentFlow.alias}, execution, function() {
$location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
Notifications.success("Execution Created.");
})
}
$scope.cancel = function() {
$location.url("/realms/" + realm.realm + "/authentication/flows/" + parentFlow.alias);
};
});
module.controller('AuthenticationFlowsCtrl', function($scope, $route, realm, flows, selectedFlow,
AuthenticationFlows, AuthenticationFlowsCopy, AuthenticationFlowExecutions,
AuthenticationExecution, AuthenticationExecutionRaisePriority, AuthenticationExecutionLowerPriority,
$modal, Notifications, CopyDialog, $location) {
$scope.realm = realm; $scope.realm = realm;
$scope.flows = flows; $scope.flows = flows;
if (flows.length > 0) { if (flows.length > 0) {
$scope.flow = flows[0]; $scope.flow = flows[0];
if (selectedFlow) {
for (var i = 0; i < flows.length; i++) {
if (flows[i].alias == selectedFlow) {
$scope.flow = flows[i];
break;
} }
}
}
}
var setupForm = function() { var setupForm = function() {
AuthenticationExecutions.query({realm: realm.realm, alias: $scope.flow.alias}, function(data) { AuthenticationFlowExecutions.query({realm: realm.realm, alias: $scope.flow.alias}, function(data) {
$scope.executions = data; $scope.executions = data;
$scope.flowmax = 0; $scope.choicesmax = 0;
$scope.levelmax = 0;
for (var i = 0; i < $scope.executions.length; i++ ) { for (var i = 0; i < $scope.executions.length; i++ ) {
execution = $scope.executions[i]; var execution = $scope.executions[i];
if (execution.requirementChoices.length > $scope.flowmax) { if (execution.requirementChoices.length > $scope.choicesmax) {
$scope.flowmax = execution.requirementChoices.length; $scope.choicesmax = execution.requirementChoices.length;
}
if (execution.level > $scope.levelmax) {
$scope.levelmax = execution.level;
} }
} }
console.log("levelmax: " + $scope.levelmax);
$scope.levelmaxempties = [];
for (j = 0; j < $scope.levelmax; j++) {
$scope.levelmaxempties.push(j);
}
for (var i = 0; i < $scope.executions.length; i++ ) { for (var i = 0; i < $scope.executions.length; i++ ) {
execution = $scope.executions[i]; var execution = $scope.executions[i];
execution.empties = []; execution.empties = [];
for (j = 0; j < $scope.flowmax - execution.requirementChoices.length; j++) { for (j = 0; j < $scope.choicesmax - execution.requirementChoices.length; j++) {
execution.empties.push(j); execution.empties.push(j);
} }
execution.preLevels = [];
for (j = 0; j < execution.level; j++) {
execution.preLevels.push(j);
}
execution.postLevels = [];
for (j = execution.level; j < $scope.levelmax; j++) {
execution.postLevels.push(j);
}
} }
}) })
}; };
$scope.copyFlow = function() {
CopyDialog.open('Copy AuthenticationFlow', $scope.flow.alias, function(name) {
AuthenticationFlowsCopy.save({realm: realm.realm, alias: $scope.flow.alias}, {
newName: name
}, function() {
$location.url("/realms/" + realm.realm + '/authentication/flows/' + name);
Notifications.success("Flow copied.");
})
})
};
$scope.removeFlow = function() {
AuthenticationFlows.remove({realm: realm, flow: flow.id}, function() {
$route.reload();
Notifications.success("Flow removed");
})
};
$scope.addFlow = function() {
$location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/flow/execution');
}
$scope.addSubFlow = function(execution) {
$location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/flow/execution');
}
$scope.addSubFlowExecution = function(execution) {
$location.url("/realms/" + realm.realm + '/authentication/flows/' + execution.flowId + '/create/execution');
}
$scope.addExecution = function() {
$location.url("/realms/" + realm.realm + '/authentication/flows/' + $scope.flow.id + '/create/execution');
}
$scope.createFlow = function() {
$location.url("/realms/" + realm.realm + '/authentication/create/flow');
}
$scope.updateExecution = function(execution) { $scope.updateExecution = function(execution) {
var copy = angular.copy(execution); var copy = angular.copy(execution);
delete copy.empties; delete copy.empties;
AuthenticationExecutions.update({realm: realm.realm, alias: $scope.flow.alias}, copy, function() { delete copy.levels;
AuthenticationFlowExecutions.update({realm: realm.realm, alias: $scope.flow.alias}, copy, function() {
Notifications.success("Auth requirement updated"); Notifications.success("Auth requirement updated");
setupForm(); setupForm();
}); });
}; };
$scope.removeExecution = function(execution) {
console.log('removeExecution: ' + execution.id);
AuthenticationExecution.remove({realm: realm.realm, execution: execution.id}, function() {
Notifications.success("Execution removed");
setupForm();
})
}
$scope.raisePriority = function(execution) {
AuthenticationExecutionRaisePriority.save({realm: realm.realm, execution: execution.id}, function() {
Notifications.success("Priority raised");
setupForm();
})
}
$scope.lowerPriority = function(execution) {
AuthenticationExecutionLowerPriority.save({realm: realm.realm, execution: execution.id}, function() {
Notifications.success("Priority lowered");
setupForm();
})
}
$scope.setupForm = setupForm; $scope.setupForm = setupForm;
setupForm(); setupForm();
@ -1658,7 +1839,7 @@ module.controller('AuthenticationFlowsCtrl', function($scope, realm, flows, Auth
}); });
module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications, Dialog, $location) { module.controller('RequiredActionsCtrl', function($scope, realm, RequiredActions, Notifications) {
console.log('RequiredActionsCtrl'); console.log('RequiredActionsCtrl');
$scope.realm = realm; $scope.realm = realm;
$scope.requiredActions = []; $scope.requiredActions = [];

View file

@ -353,12 +353,46 @@ module.factory('IdentityProviderMapperLoader', function(Loader, IdentityProvider
module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows, $route, $q) { module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows, $route, $q) {
return Loader.query(AuthenticationFlows, function() { return Loader.query(AuthenticationFlows, function() {
return {
realm : $route.current.params.realm,
flow: ''
}
});
});
module.factory('AuthenticationFormProvidersLoader', function(Loader, AuthenticationFormProviders, $route, $q) {
return Loader.query(AuthenticationFormProviders, function() {
return { return {
realm : $route.current.params.realm realm : $route.current.params.realm
} }
}); });
}); });
module.factory('AuthenticationFormActionProvidersLoader', function(Loader, AuthenticationFormActionProviders, $route, $q) {
return Loader.query(AuthenticationFormActionProviders, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('AuthenticatorProvidersLoader', function(Loader, AuthenticatorProviders, $route, $q) {
return Loader.query(AuthenticatorProviders, function() {
return {
realm : $route.current.params.realm
}
});
});
module.factory('AuthenticationFlowLoader', function(Loader, AuthenticationFlows, $route, $q) {
return Loader.get(AuthenticationFlows, function() {
return {
realm : $route.current.params.realm,
flow: $route.current.params.flow
}
});
});
module.factory('AuthenticationConfigDescriptionLoader', function(Loader, AuthenticationConfigDescription, $route, $q) { module.factory('AuthenticationConfigDescriptionLoader', function(Loader, AuthenticationConfigDescription, $route, $q) {
return Loader.get(AuthenticationConfigDescription, function () { return Loader.get(AuthenticationConfigDescription, function () {
return { return {

View file

@ -94,6 +94,34 @@ module.service('Dialog', function($modal) {
return dialog return dialog
}); });
module.service('CopyDialog', function($modal) {
var dialog = {};
dialog.open = function (title, suggested, success) {
var controller = function($scope, $modalInstance, title) {
$scope.title = title;
$scope.name = { value: 'Copy of ' + suggested };
$scope.ok = function () {
console.log('ok with name: ' + $scope.name);
$modalInstance.close();
success($scope.name.value);
};
$scope.cancel = function () {
$modalInstance.dismiss('cancel');
};
}
$modal.open({
templateUrl: resourceUrl + '/templates/kc-copy.html',
controller: controller,
resolve: {
title: function() {
return title;
}
}
});
};
return dialog;
});
module.factory('Notifications', function($rootScope, $timeout) { module.factory('Notifications', function($rootScope, $timeout) {
// time (in ms) the notifications are shown // time (in ms) the notifications are shown
var delay = 5000; var delay = 5000;
@ -1133,7 +1161,7 @@ module.factory('IdentityProviderMapper', function($resource) {
}); });
}); });
module.factory('AuthenticationExecutions', function($resource) { module.factory('AuthenticationFlowExecutions', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions', { return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions', {
realm : '@realm', realm : '@realm',
alias : '@alias' alias : '@alias'
@ -1144,11 +1172,52 @@ module.factory('AuthenticationExecutions', function($resource) {
}); });
}); });
module.factory('CreateExecutionFlow', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions/flow', {
realm : '@realm',
alias : '@alias'
});
});
module.factory('CreateExecution', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/executions/execution', {
realm : '@realm',
alias : '@alias'
});
});
module.factory('AuthenticationFlows', function($resource) { module.factory('AuthenticationFlows', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/flows', { return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:flow', {
realm : '@realm',
flow: '@flow'
});
});
module.factory('AuthenticationFormProviders', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/form-providers', {
realm : '@realm' realm : '@realm'
}); });
}); });
module.factory('AuthenticationFormActionProviders', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/form-action-providers', {
realm : '@realm'
});
});
module.factory('AuthenticatorProviders', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/authenticator-providers', {
realm : '@realm'
});
});
module.factory('AuthenticationFlowsCopy', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/flows/:alias/copy', {
realm : '@realm',
alias : '@alias'
});
});
module.factory('AuthenticationConfigDescription', function($resource) { module.factory('AuthenticationConfigDescription', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', { return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
realm : '@realm', realm : '@realm',
@ -1173,6 +1242,33 @@ module.factory('AuthenticationExecutionConfig', function($resource) {
}); });
}); });
module.factory('AuthenticationExecution', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/executions/:execution', {
realm : '@realm',
execution : '@execution'
}, {
update : {
method : 'PUT'
}
});
});
module.factory('AuthenticationExecutionRaisePriority', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/executions/:execution/raise-priority', {
realm : '@realm',
execution : '@execution'
});
});
module.factory('AuthenticationExecutionLowerPriority', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/authentication/executions/:execution/lower-priority', {
realm : '@realm',
execution : '@execution'
});
});
module.service('SelectRoleDialog', function($modal) { module.service('SelectRoleDialog', function($modal) {
var dialog = {}; var dialog = {};

View file

@ -6,43 +6,35 @@
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th colspan="6" class="kc-table-actions"> <th colspan="{{levelmax + 1 + choicesmax + 4}}" class="kc-table-actions">
<div class="dropdown pull-left"> <div class="dropdown pull-left">
<select class="form-control" ng-model="flow" <select class="form-control" ng-model="flow"
ng-options="(flow.alias|capitalize) for flow in flows" ng-options="(flow.alias|capitalize) for flow in flows"
data-ng-change="setupForm()"> data-ng-change="setupForm()">
</select> </select>
</div> </div>
&nbsp;&nbsp;<i class="fa fa-question-circle text-muted" tooltip="{{flow.description}}" tooltip-placement="right"> </i>
<div class="pull-right" data-ng-show="access.manageRealm">
<button class="btn btn-default" data-ng-click="createFlow()">New</button>
<button class="btn btn-default" data-ng-click="copyFlow()">Copy</button>
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="removeFlow()">Delete</button>
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addExecution()">Add Execution</button>
<button class="btn btn-default" data-ng-hide="flow.builtIn" data-ng-click="addFlow()">Add Flow</button>
</div>
</th> </th>
</tr> </tr>
<tr data-ng-hide="executions.length == 0"> <tr data-ng-hide="executions.length == 0">
<th colspan="2">Auth Type</th> <th colspan="{{levelmax + 1}}">Auth Type</th>
<th colspan="{{flowmax}}">Requirement</th> <th colspan="{{choicesmax}}">Requirement</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
<tr ng-repeat="execution in executions" data-ng-show="executions.length > 0"> <tr ng-repeat="execution in executions" data-ng-show="executions.length > 0">
<td ng-show="execution.subFlow"></td> <td ng-repeat="lev in execution.preLevels"></td>
<td>{{execution.referenceType|capitalize}}</td> <td><button data-ng-hide="flow.builtIn || $first" class="btn btn-default" data-ng-click="raisePriority(execution)"><i class="fa fa-angle-up"></i></button><button data-ng-hide="flow.builtIn || $last" class="btn btn-default" data-ng-click="lowerPriority(execution)"><i class="fa fa-angle-down"></i></button> {{execution.displayName|capitalize}}</td>
<td ng-hide="execution.subFlow"></td> <td ng-repeat="lev in execution.postLevels"></td>
<td ng-repeat="choice in execution.requirementChoices"> <td ng-repeat="choice in execution.requirementChoices">
<!--
<div class="dropdown pull-left">
<select class="form-control"
ng-model="execution.requirement"
ng-options="choice for choice in execution.requirementChoices">
</select>
</div>
-->
<!--
<div ng-repeat="choice in execution.requirementChoices">
<label >
<input type="radio" ng-model="execution.requirement" ng-value="choice">
{{choice}}
</label>
</div>
-->
<label > <label >
<input type="radio" ng-model="execution.requirement" ng-value="choice" ng-change="updateExecution(execution)"> <input type="radio" ng-model="execution.requirement" ng-value="choice" ng-change="updateExecution(execution)">
{{choice}} {{choice}}
@ -51,8 +43,20 @@
</td> </td>
<td ng-repeat="emptee in execution.empties"></td> <td ng-repeat="emptee in execution.empties"></td>
<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> <ul class="nav navbar-nav navbar-utility" data-ng-hide="flow.builtIn && !execution.configurable">
<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> <li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Actions <b class="caret"></b>
</a>
<ul class="dropdown-menu" >
<li data-ng-hide="flow.builtIn"><a href="" ng-click="removeExecution(execution)">Delete</a></li>
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlowExecution(execution)">Add Execution</a></li>
<li data-ng-hide="flow.builtIn || !execution.authenticationFlow"><a href="" ng-click="addSubFlow(execution)">Add Flow</a></li>
<li data-ng-show="execution.configurable && execution.authenticationConfig == null"><a href="#/create/authentication/{{realm.realm}}/execution/{{execution.id}}/provider/{{execution.providerId}}">Config</a></li>
<li data-ng-show="execution.configurable && execution.authenticationConfig != null"><a href="#/realms/{{realm.realm}}/authentication/config/{{execution.providerId}}/{{execution.authenticationConfig}}">Config</a></li>
</ul>
</li>
</ul>
</td> </td>
</tr> </tr>
<tr data-ng-show="executions.length == 0"> <tr data-ng-show="executions.length == 0">

View file

@ -0,0 +1,29 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<div>
<h1 data-ng-show="parentFlow.providerId == 'basic-flow'">Create Authenticator Execution</h1>
<h1 data-ng-show="parentFlow.providerId == 'for-flow'">Create Form Action Execution</h1>
</div>
<kc-tabs-authentication></kc-tabs-authentication>
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
<div class="form-group">
<label class="col-md-2 control-label" for="provider">Provider</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="provider"
ng-model="provider"
ng-options="provider.id for provider in providers">
</select>
</div>
</div>
<kc-tooltip>{{provider.description}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save>Save</button>
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,55 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>Create Execution Flow</h1>
<kc-tabs-authentication></kc-tabs-authentication>
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
<div class="form-group">
<label class="col-md-2 control-label" for="alias">Alias </label>
<div class="col-sm-6">
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
</div>
<kc-tooltip>Specifies display name for the flow.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="description">Description </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="flow.description"></textarea>
</div>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="flowType">Flow Type</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="flowType"
ng-model="flow.type">
<option value="basic-flow">generic</option>
<option value="form-flow">form</option>
</select>
</div>
</div>
<kc-tooltip>What kind of form is it</kc-tooltip>
</div>
<div class="form-group" data-ng-show="flow.type == 'form-flow'">
<label class="col-md-2 control-label" for="provider">Form Provider</label>
<div class="col-sm-6">
<div>
<select class="form-control" id="provider"
ng-model="provider"
ng-options="provider.id for provider in formProviders">
</select>
</div>
</div>
<kc-tooltip>{{provider.description}}</kc-tooltip>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save>Save</button>
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -0,0 +1,30 @@
<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
<h1>Create Top Level Flow</h1>
<kc-tabs-authentication></kc-tabs-authentication>
<form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageRealm">
<div class="form-group">
<label class="col-md-2 control-label" for="alias">Alias </label>
<div class="col-sm-6">
<input class="form-control" type="text" id="alias" name="alias" data-ng-model="flow.alias" autofocus>
</div>
<kc-tooltip>Specifies display name for the flow.</kc-tooltip>
</div>
<div class="form-group">
<label class="col-md-2 control-label" for="description">Description </label>
<div class="col-md-6">
<textarea class="form-control" rows="5" cols="50" id="description" name="description" data-ng-model="flow.description"></textarea>
</div>
</div>
<div class="form-group">
<div class="col-md-10 col-md-offset-2">
<button kc-save>Save</button>
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
</div>
</div>
</form>
</div>
<kc-menu></kc-menu>

View file

@ -1,5 +1,10 @@
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">
<span class="pficon pficon-close"></span>
</button>
<h4 class="modal-title">Role Selector</h4>
</div>
<div style="padding: 15px 60px 75px 60px"> <div style="padding: 15px 60px 75px 60px">
<h2>Role Selector</h2>
<form> <form>
<div data-ng-show="realmRoles.length > 0"> <div data-ng-show="realmRoles.length > 0">
<label class="control-label" for="available">Realm Roles</label> <label class="control-label" for="available">Realm Roles</label>

View file

@ -0,0 +1,18 @@
<div class="modal-header">
<button type="button" class="close" ng-click="cancel()">
<span class="pficon pficon-close"></span>
</button>
<h4 class="modal-title">{{title}}</h4>
</div>
<div class="modal-body">
<form>
<div>
<label class="control-label" for="name">New name</label>
<input class="form-control" type="text" id="name" data-ng-model="name.value">
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" ng-click="cancel()">Cancel</button>
<button type="button" class="btn btn-primary" ng-click="ok()">Ok</button>
</div>

View file

@ -23,9 +23,8 @@ public class AuthenticationExecutionModel implements Serializable {
private String authenticatorConfig; private String authenticatorConfig;
private String authenticator; private String authenticator;
private String flowId; private String flowId;
private boolean autheticatorFlow; private boolean authenticatorFlow;
private Requirement requirement; private Requirement requirement;
private boolean userSetupAllowed;
private int priority; private int priority;
private String parentFlow; private String parentFlow;
@ -69,14 +68,6 @@ public class AuthenticationExecutionModel implements Serializable {
this.priority = priority; this.priority = priority;
} }
public boolean isUserSetupAllowed() {
return userSetupAllowed;
}
public void setUserSetupAllowed(boolean userSetupAllowed) {
this.userSetupAllowed = userSetupAllowed;
}
public String getParentFlow() { public String getParentFlow() {
return parentFlow; return parentFlow;
} }
@ -103,12 +94,12 @@ public class AuthenticationExecutionModel implements Serializable {
* *
* @return * @return
*/ */
public boolean isAutheticatorFlow() { public boolean isAuthenticatorFlow() {
return autheticatorFlow; return authenticatorFlow;
} }
public void setAutheticatorFlow(boolean autheticatorFlow) { public void setAuthenticatorFlow(boolean authenticatorFlow) {
this.autheticatorFlow = autheticatorFlow; this.authenticatorFlow = authenticatorFlow;
} }
public enum Requirement { public enum Requirement {

View file

@ -2,13 +2,9 @@ package org.keycloak.models.utils;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RequiredCredentialModel; import org.keycloak.models.RequiredCredentialModel;
import java.util.HashMap;
import java.util.Map;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
@ -59,8 +55,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-page-form"); execution.setAuthenticator("registration-page-form");
execution.setPriority(10); execution.setPriority(10);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(true);
execution.setAutheticatorFlow(true);
execution.setFlowId(registrationFormFlow.getId()); execution.setFlowId(registrationFormFlow.getId());
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
@ -69,8 +64,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-user-creation"); execution.setAuthenticator("registration-user-creation");
execution.setPriority(20); execution.setPriority(20);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel(); execution = new AuthenticationExecutionModel();
@ -78,8 +72,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-profile-action"); execution.setAuthenticator("registration-profile-action");
execution.setPriority(40); execution.setPriority(40);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel(); execution = new AuthenticationExecutionModel();
@ -87,8 +80,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("registration-password-action"); execution.setAuthenticator("registration-password-action");
execution.setPriority(50); execution.setPriority(50);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
//AuthenticatorConfigModel captchaConfig = new AuthenticatorConfigModel(); //AuthenticatorConfigModel captchaConfig = new AuthenticatorConfigModel();
@ -103,8 +95,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED); execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
execution.setAuthenticator("registration-recaptcha-action"); execution.setAuthenticator("registration-recaptcha-action");
execution.setPriority(60); execution.setPriority(60);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
//execution.setAuthenticatorConfig(captchaConfig.getId()); //execution.setAuthenticatorConfig(captchaConfig.getId());
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
@ -141,8 +132,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("direct-grant-validate-username"); execution.setAuthenticator("direct-grant-validate-username");
execution.setPriority(10); execution.setPriority(10);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
// password // password
@ -154,8 +144,7 @@ public class DefaultAuthenticationFlows {
} }
execution.setAuthenticator("direct-grant-validate-password"); execution.setAuthenticator("direct-grant-validate-password");
execution.setPriority(20); execution.setPriority(20);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
// otp // otp
@ -167,8 +156,7 @@ public class DefaultAuthenticationFlows {
} }
execution.setAuthenticator("direct-grant-validate-otp"); execution.setAuthenticator("direct-grant-validate-otp");
execution.setPriority(30); execution.setPriority(30);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
@ -188,8 +176,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution.setAuthenticator("auth-cookie"); execution.setAuthenticator("auth-cookie");
execution.setPriority(10); execution.setPriority(10);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
execution = new AuthenticationExecutionModel(); execution = new AuthenticationExecutionModel();
execution.setParentFlow(browser.getId()); execution.setParentFlow(browser.getId());
@ -200,8 +187,7 @@ public class DefaultAuthenticationFlows {
} }
execution.setAuthenticator("auth-spnego"); execution.setAuthenticator("auth-spnego");
execution.setPriority(20); execution.setPriority(20);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
@ -217,8 +203,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE); execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
execution.setFlowId(forms.getId()); execution.setFlowId(forms.getId());
execution.setPriority(30); execution.setPriority(30);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(true);
execution.setAutheticatorFlow(true);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
// forms // forms
@ -228,8 +213,7 @@ public class DefaultAuthenticationFlows {
execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED); execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
execution.setAuthenticator("auth-username-password-form"); execution.setAuthenticator("auth-username-password-form");
execution.setPriority(10); execution.setPriority(10);
execution.setUserSetupAllowed(false); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
// otp processing // otp processing
@ -243,8 +227,7 @@ public class DefaultAuthenticationFlows {
execution.setAuthenticator("auth-otp-form"); execution.setAuthenticator("auth-otp-form");
execution.setPriority(20); execution.setPriority(20);
execution.setUserSetupAllowed(true); execution.setAuthenticatorFlow(false);
execution.setAutheticatorFlow(false);
realm.addAuthenticatorExecution(execution); realm.addAuthenticatorExecution(execution);
} }
} }

View file

@ -462,13 +462,12 @@ public class ModelToRepresentation {
rep.setAuthenticatorConfig(config.getAlias()); rep.setAuthenticatorConfig(config.getAlias());
} }
rep.setAuthenticator(model.getAuthenticator()); rep.setAuthenticator(model.getAuthenticator());
rep.setAutheticatorFlow(model.isAutheticatorFlow()); rep.setAutheticatorFlow(model.isAuthenticatorFlow());
if (model.getFlowId() != null) { if (model.getFlowId() != null) {
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(model.getFlowId()); AuthenticationFlowModel flow = realm.getAuthenticationFlowById(model.getFlowId());
rep.setFlowAlias(flow.getAlias()); rep.setFlowAlias(flow.getAlias());
} }
rep.setPriority(model.getPriority()); rep.setPriority(model.getPriority());
rep.setUserSetupAllowed(model.isUserSetupAllowed());
rep.setRequirement(model.getRequirement().name()); rep.setRequirement(model.getRequirement().name());
return rep; return rep;
} }

View file

@ -1077,13 +1077,12 @@ public class RepresentationToModel {
model.setAuthenticatorConfig(config.getId()); model.setAuthenticatorConfig(config.getId());
} }
model.setAuthenticator(rep.getAuthenticator()); model.setAuthenticator(rep.getAuthenticator());
model.setAutheticatorFlow(rep.isAutheticatorFlow()); model.setAuthenticatorFlow(rep.isAutheticatorFlow());
if (rep.getFlowAlias() != null) { if (rep.getFlowAlias() != null) {
AuthenticationFlowModel flow = realm.getFlowByAlias(rep.getFlowAlias()); AuthenticationFlowModel flow = realm.getFlowByAlias(rep.getFlowAlias());
model.setFlowId(flow.getId()); model.setFlowId(flow.getId());
} }
model.setPriority(rep.getPriority()); model.setPriority(rep.getPriority());
model.setUserSetupAllowed(rep.isUserSetupAllowed());
model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement())); model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement()));
return model; return model;
} }

View file

@ -1308,13 +1308,12 @@ public class RealmAdapter implements RealmModel {
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) { public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel(); AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId()); model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement()); model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority()); model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator()); model.setAuthenticator(entity.getAuthenticator());
model.setParentFlow(entity.getParentFlow()); model.setParentFlow(entity.getParentFlow());
model.setFlowId(entity.getFlowId()); model.setFlowId(entity.getFlowId());
model.setAutheticatorFlow(entity.isAuthenticatorFlow()); model.setAuthenticatorFlow(entity.isAuthenticatorFlow());
model.setAuthenticatorConfig(entity.getAuthenticatorConfig()); model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
return model; return model;
} }
@ -1345,8 +1344,7 @@ public class RealmAdapter implements RealmModel {
entity.setAuthenticator(model.getAuthenticator()); entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority()); entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement()); entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed()); entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
entity.setFlowId(model.getFlowId()); entity.setFlowId(model.getFlowId());
entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
AuthenticationFlowEntity flow = getFlowEntity(model.getId()); AuthenticationFlowEntity flow = getFlowEntity(model.getId());
@ -1366,12 +1364,11 @@ public class RealmAdapter implements RealmModel {
} }
} }
if (entity == null) return; if (entity == null) return;
entity.setAuthenticatorFlow(model.isAutheticatorFlow()); entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
entity.setAuthenticator(model.getAuthenticator()); entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority()); entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement()); entity.setRequirement(model.getRequirement());
entity.setFlowId(model.getFlowId()); entity.setFlowId(model.getFlowId());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
} }

View file

@ -1075,7 +1075,6 @@ public class RealmAdapter implements RealmModel {
@Override @Override
public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) { public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
if (updated != null) return updated.getAuthenticationExecutions(flowId); if (updated != null) return updated.getAuthenticationExecutions(flowId);
List<AuthenticationExecutionModel> models = new ArrayList<>();
return cached.getAuthenticationExecutions().get(flowId); return cached.getAuthenticationExecutions().get(flowId);
} }

View file

@ -197,6 +197,7 @@ public class CachedRealm implements Serializable {
defaultLocale = model.getDefaultLocale(); defaultLocale = model.getDefaultLocale();
for (AuthenticationFlowModel flow : model.getAuthenticationFlows()) { for (AuthenticationFlowModel flow : model.getAuthenticationFlows()) {
authenticationFlows.put(flow.getId(), flow); authenticationFlows.put(flow.getId(), flow);
authenticationExecutions.put(flow.getId(), new LinkedList<AuthenticationExecutionModel>());
for (AuthenticationExecutionModel execution : model.getAuthenticationExecutions(flow.getId())) { for (AuthenticationExecutionModel execution : model.getAuthenticationExecutions(flow.getId())) {
authenticationExecutions.add(flow.getId(), execution); authenticationExecutions.add(flow.getId(), execution);
executionsById.put(execution.getId(), execution); executionsById.put(execution.getId(), execution);

View file

@ -1622,13 +1622,12 @@ public class RealmAdapter implements RealmModel {
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) { public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel(); AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId()); model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement()); model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority()); model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator()); model.setAuthenticator(entity.getAuthenticator());
model.setFlowId(entity.getFlowId()); model.setFlowId(entity.getFlowId());
model.setParentFlow(entity.getParentFlow().getId()); model.setParentFlow(entity.getParentFlow().getId());
model.setAutheticatorFlow(entity.isAutheticatorFlow()); model.setAuthenticatorFlow(entity.isAutheticatorFlow());
model.setAuthenticatorConfig(entity.getAuthenticatorConfig()); model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
return model; return model;
} }
@ -1654,8 +1653,7 @@ public class RealmAdapter implements RealmModel {
entity.setParentFlow(flow); entity.setParentFlow(flow);
flow.getExecutions().add(entity); flow.getExecutions().add(entity);
entity.setRealm(realm); entity.setRealm(realm);
entity.setUserSetupAllowed(model.isUserSetupAllowed()); entity.setAutheticatorFlow(model.isAuthenticatorFlow());
entity.setAutheticatorFlow(model.isAutheticatorFlow());
em.persist(entity); em.persist(entity);
em.flush(); em.flush();
model.setId(entity.getId()); model.setId(entity.getId());
@ -1667,11 +1665,10 @@ public class RealmAdapter implements RealmModel {
public void updateAuthenticatorExecution(AuthenticationExecutionModel model) { public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId()); AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
if (entity == null) return; if (entity == null) return;
entity.setAutheticatorFlow(model.isAutheticatorFlow()); entity.setAutheticatorFlow(model.isAuthenticatorFlow());
entity.setAuthenticator(model.getAuthenticator()); entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority()); entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement()); entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
entity.setFlowId(model.getFlowId()); entity.setFlowId(model.getFlowId());
em.flush(); em.flush();

View file

@ -51,9 +51,6 @@ public class AuthenticationExecutionEntity {
@Column(name="PRIORITY") @Column(name="PRIORITY")
protected int priority; protected int priority;
@Column(name="USER_SETUP_ALLOWED")
private boolean userSetupAllowed;
@Column(name="AUTHENTICATOR_FLOW") @Column(name="AUTHENTICATOR_FLOW")
private boolean autheticatorFlow; private boolean autheticatorFlow;
@ -97,14 +94,6 @@ public class AuthenticationExecutionEntity {
this.priority = priority; this.priority = priority;
} }
public boolean isUserSetupAllowed() {
return userSetupAllowed;
}
public void setUserSetupAllowed(boolean userSetupAllowed) {
this.userSetupAllowed = userSetupAllowed;
}
public boolean isAutheticatorFlow() { public boolean isAutheticatorFlow() {
return autheticatorFlow; return autheticatorFlow;
} }

View file

@ -1384,13 +1384,12 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) { public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
AuthenticationExecutionModel model = new AuthenticationExecutionModel(); AuthenticationExecutionModel model = new AuthenticationExecutionModel();
model.setId(entity.getId()); model.setId(entity.getId());
model.setUserSetupAllowed(entity.isUserSetupAllowed());
model.setRequirement(entity.getRequirement()); model.setRequirement(entity.getRequirement());
model.setPriority(entity.getPriority()); model.setPriority(entity.getPriority());
model.setAuthenticator(entity.getAuthenticator()); model.setAuthenticator(entity.getAuthenticator());
model.setFlowId(entity.getFlowId()); model.setFlowId(entity.getFlowId());
model.setParentFlow(entity.getParentFlow()); model.setParentFlow(entity.getParentFlow());
model.setAutheticatorFlow(entity.isAuthenticatorFlow()); model.setAuthenticatorFlow(entity.isAuthenticatorFlow());
model.setAuthenticatorConfig(entity.getAuthenticatorConfig()); model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
return model; return model;
} }
@ -1421,8 +1420,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
entity.setAuthenticator(model.getAuthenticator()); entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority()); entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement()); entity.setRequirement(model.getRequirement());
entity.setUserSetupAllowed(model.isUserSetupAllowed()); entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
entity.setAuthenticatorFlow(model.isAutheticatorFlow());
entity.setFlowId(model.getFlowId()); entity.setFlowId(model.getFlowId());
entity.setParentFlow(model.getParentFlow()); entity.setParentFlow(model.getParentFlow());
entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
@ -1444,12 +1442,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
} }
} }
if (entity == null) return; if (entity == null) return;
entity.setAuthenticatorFlow(model.isAutheticatorFlow()); entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
entity.setAuthenticator(model.getAuthenticator()); entity.setAuthenticator(model.getAuthenticator());
entity.setPriority(model.getPriority()); entity.setPriority(model.getPriority());
entity.setRequirement(model.getRequirement()); entity.setRequirement(model.getRequirement());
entity.setFlowId(model.getFlowId()); entity.setFlowId(model.getFlowId());
entity.setUserSetupAllowed(model.isUserSetupAllowed());
entity.setAuthenticatorConfig(model.getAuthenticatorConfig()); entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
updateMongoEntity(); updateMongoEntity();
} }

View file

@ -1,7 +1,6 @@
package org.keycloak.authentication; package org.keycloak.authentication;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticatorConfigModel;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import java.util.LinkedList; import java.util.LinkedList;
@ -21,9 +20,11 @@ public class AuthenticatorUtil {
} }
public static void recurseExecutions(RealmModel realm, String flowId, List<AuthenticationExecutionModel> executions) { public static void recurseExecutions(RealmModel realm, String flowId, List<AuthenticationExecutionModel> executions) {
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) { List<AuthenticationExecutionModel> authenticationExecutions = realm.getAuthenticationExecutions(flowId);
if (authenticationExecutions == null) return;
for (AuthenticationExecutionModel model : authenticationExecutions) {
executions.add(model); executions.add(model);
if (model.isAutheticatorFlow() && model.isEnabled()) { if (model.isAuthenticatorFlow() && model.isEnabled()) {
recurseExecutions(realm, model.getFlowId(), executions); recurseExecutions(realm, model.getFlowId(), executions);
} }
} }
@ -31,7 +32,7 @@ public class AuthenticatorUtil {
public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) { public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) {
for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) { for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
if (model.isAutheticatorFlow()) { if (model.isAuthenticatorFlow()) {
AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getFlowId(), authProviderId); AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getFlowId(), authProviderId);
if (recurse != null) return recurse; if (recurse != null) return recurse;

View file

@ -25,4 +25,14 @@ public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
* @return * @return
*/ */
AuthenticationExecutionModel.Requirement[] getRequirementChoices(); AuthenticationExecutionModel.Requirement[] getRequirementChoices();
/**
*
* Does this authenticator have required actions that can set if the user does not have
* this authenticator set up?
*
*
* @return
*/
boolean isUserSetupAllowed();
} }

View file

@ -49,7 +49,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
alternativeSuccessful = true; alternativeSuccessful = true;
continue; continue;
} }
if (model.isAutheticatorFlow()) { if (model.isAuthenticatorFlow()) {
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model); AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
return authenticationFlow.processAction(actionExecution); return authenticationFlow.processAction(actionExecution);
} else if (model.getId().equals(actionExecution)) { } else if (model.getId().equals(actionExecution)) {
@ -79,7 +79,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED); processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue; continue;
} }
if (model.isAutheticatorFlow()) { if (model.isAuthenticatorFlow()) {
AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model); AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
Response flowChallenge = authenticationFlow.processFlow(); Response flowChallenge = authenticationFlow.processFlow();
if (flowChallenge == null) { if (flowChallenge == null) {
@ -124,7 +124,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser); configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser);
if (!configuredFor) { if (!configuredFor) {
if (model.isRequired()) { if (model.isRequired()) {
if (model.isUserSetupAllowed()) { if (factory.isUserSetupAllowed()) {
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId()); AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED); processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser()); authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());

View file

@ -152,7 +152,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED); executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
continue; continue;
} }
FormAction action = processor.getSession().getProvider(FormAction.class, formActionExecution.getAuthenticator()); FormActionFactory factory = (FormActionFactory)processor.getSession().getKeycloakSessionFactory().getProviderFactory(FormAction.class, formActionExecution.getAuthenticator());
FormAction action = factory.create(processor.getSession());
UserModel authUser = processor.getClientSession().getAuthenticatedUser(); UserModel authUser = processor.getClientSession().getAuthenticatedUser();
if (action.requiresUser() && authUser == null) { if (action.requiresUser() && authUser == null) {
@ -163,7 +164,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser); configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
if (!configuredFor) { if (!configuredFor) {
if (formActionExecution.isRequired()) { if (formActionExecution.isRequired()) {
if (formActionExecution.isUserSetupAllowed()) { if (factory.isUserSetupAllowed()) {
AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator()); AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED); executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
requiredActions.add(action); requiredActions.add(action);

View file

@ -78,4 +78,10 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return null; return null;
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
} }

View file

@ -59,6 +59,11 @@ public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
return false; return false;
} }
@Override
public boolean isUserSetupAllowed() {
return true;
}
public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = { public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
AuthenticationExecutionModel.Requirement.REQUIRED, AuthenticationExecutionModel.Requirement.REQUIRED,
AuthenticationExecutionModel.Requirement.OPTIONAL, AuthenticationExecutionModel.Requirement.OPTIONAL,

View file

@ -84,4 +84,10 @@ public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return null; return null;
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
} }

View file

@ -81,4 +81,10 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
public List<ProviderConfigProperty> getConfigProperties() { public List<ProviderConfigProperty> getConfigProperties() {
return null; return null;
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
} }

View file

@ -82,6 +82,12 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public String getDisplayType() { public String getDisplayType() {
return "OTP"; return "OTP";

View file

@ -73,6 +73,12 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public String getDisplayType() { public String getDisplayType() {
return "Password"; return "Password";

View file

@ -103,6 +103,12 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public String getDisplayType() { public String getDisplayType() {
return "Username Validation"; return "Username Validation";

View file

@ -45,7 +45,7 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
@Override @Override
public String getHelpText() { public String getHelpText() {
return null; return "This is the controller for the registration page";
} }
@Override @Override
@ -82,6 +82,11 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public void postInit(KeycloakSessionFactory factory) { public void postInit(KeycloakSessionFactory factory) {

View file

@ -34,7 +34,7 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
@Override @Override
public String getHelpText() { public String getHelpText() {
return null; return "Validates that password matches password confirmation field. It also will store password in user's credential store.";
} }
@Override @Override
@ -105,6 +105,11 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public void close() { public void close() {

View file

@ -31,7 +31,7 @@ public class RegistrationProfile implements FormAction, FormActionFactory {
@Override @Override
public String getHelpText() { public String getHelpText() {
return null; return "Validates email, first name, and last name attributes and stores them in user data.";
} }
@Override @Override
@ -110,6 +110,12 @@ public class RegistrationProfile implements FormAction, FormActionFactory {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public void close() { public void close() {

View file

@ -158,6 +158,12 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public void close() { public void close() {
@ -185,7 +191,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
@Override @Override
public String getHelpText() { public String getHelpText() {
return null; return "Adds Google Recaptcha button. Recaptchas verify that the entity that is registering is a human. This can only be used on the internet and must be configured after you add it.";
} }
private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>(); private static final List<ProviderConfigProperty> configProperties = new ArrayList<ProviderConfigProperty>();

View file

@ -35,7 +35,7 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
@Override @Override
public String getHelpText() { public String getHelpText() {
return null; return "This action must always be first! Validates the username of the user in validation phase. In success phase, this will create the user in the database.";
} }
@Override @Override
@ -149,6 +149,12 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
} }
@Override
public boolean isUserSetupAllowed() {
return false;
}
@Override @Override
public void close() { public void close() {

View file

@ -35,7 +35,7 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
property.setName(ROLE_CONFIG); property.setName(ROLE_CONFIG);
property.setLabel("Role"); property.setLabel("Role");
property.setHelpText("Role name you want changed. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole"); property.setHelpText("Role name you want changed. Click 'Select Role' button to browse roles, or just type it in the textbox. To reference an application role the syntax is appname.approle, i.e. myapp.myrole");
property.setType(ProviderConfigProperty.STRING_TYPE); property.setType(ProviderConfigProperty.ROLE_TYPE);
configProperties.add(property); configProperties.add(property);
property = new ProviderConfigProperty(); property = new ProviderConfigProperty();
property.setName(NEW_ROLE_NAME); property.setName(NEW_ROLE_NAME);

View file

@ -2,17 +2,25 @@ package org.keycloak.services.resources.admin;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.spi.BadRequestException;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
import org.keycloak.authentication.AuthenticationFlow; import org.keycloak.authentication.AuthenticationFlow;
import org.keycloak.authentication.Authenticator;
import org.keycloak.authentication.AuthenticatorUtil; import org.keycloak.authentication.AuthenticatorUtil;
import org.keycloak.authentication.ConfigurableAuthenticatorFactory; import org.keycloak.authentication.ConfigurableAuthenticatorFactory;
import org.keycloak.authentication.DefaultAuthenticationFlow;
import org.keycloak.authentication.FormAction;
import org.keycloak.authentication.FormAuthenticationFlow;
import org.keycloak.authentication.FormAuthenticator;
import org.keycloak.models.AuthenticationExecutionModel; import org.keycloak.models.AuthenticationExecutionModel;
import org.keycloak.models.AuthenticationFlowModel; import org.keycloak.models.AuthenticationFlowModel;
import org.keycloak.models.AuthenticatorConfigModel; import org.keycloak.models.AuthenticatorConfigModel;
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.models.RequiredActionProviderModel;
import org.keycloak.provider.ConfiguredProvider;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.ConfigPropertyRepresentation; import org.keycloak.representations.idm.ConfigPropertyRepresentation;
import org.keycloak.utils.CredentialHelper; import org.keycloak.utils.CredentialHelper;
@ -28,6 +36,7 @@ import javax.ws.rs.core.Context;
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 javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
@ -59,13 +68,16 @@ public class AuthenticationManagementResource {
public static class AuthenticationExecutionRepresentation { public static class AuthenticationExecutionRepresentation {
protected String id; protected String id;
protected String referenceType;
protected String requirement; protected String requirement;
protected String displayName;
protected List<String> requirementChoices; protected List<String> requirementChoices;
protected Boolean configurable; protected Boolean configurable;
protected Boolean subFlow; protected Boolean authenticationFlow;
protected String providerId; protected String providerId;
protected String authenticationConfig; protected String authenticationConfig;
protected String flowId;
protected int level;
protected int index;
public String getId() { public String getId() {
return id; return id;
@ -75,12 +87,12 @@ public class AuthenticationManagementResource {
this.id = execution; this.id = execution;
} }
public String getReferenceType() { public String getDisplayName() {
return referenceType; return displayName;
} }
public void setReferenceType(String referenceType) { public void setDisplayName(String displayName) {
this.referenceType = referenceType; this.displayName = displayName;
} }
public String getRequirement() { public String getRequirement() {
@ -107,14 +119,6 @@ public class AuthenticationManagementResource {
this.configurable = configurable; this.configurable = configurable;
} }
public Boolean getSubFlow() {
return subFlow;
}
public void setSubFlow(Boolean subFlow) {
this.subFlow = subFlow;
}
public String getProviderId() { public String getProviderId() {
return providerId; return providerId;
} }
@ -130,8 +134,83 @@ public class AuthenticationManagementResource {
public void setAuthenticationConfig(String authenticationConfig) { public void setAuthenticationConfig(String authenticationConfig) {
this.authenticationConfig = authenticationConfig; this.authenticationConfig = authenticationConfig;
} }
public Boolean getAuthenticationFlow() {
return authenticationFlow;
} }
public void setAuthenticationFlow(Boolean authenticationFlow) {
this.authenticationFlow = authenticationFlow;
}
public int getLevel() {
return level;
}
public void setLevel(int level) {
this.level = level;
}
public int getIndex() {
return index;
}
public void setIndex(int index) {
this.index = index;
}
public String getFlowId() {
return flowId;
}
public void setFlowId(String flowId) {
this.flowId = flowId;
}
}
@Path("/form-providers")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getFormProviders() {
this.auth.requireView();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAuthenticator.class);
return buildProviderMetadata(factories);
}
@Path("/authenticator-providers")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getAuthenticatorProviders() {
this.auth.requireView();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(Authenticator.class);
return buildProviderMetadata(factories);
}
public List<Map<String, String>> buildProviderMetadata(List<ProviderFactory> factories) {
List<Map<String, String>> providers = new LinkedList<>();
for (ProviderFactory factory : factories) {
Map<String, String> data = new HashMap<>();
data.put("id", factory.getId());
ConfiguredProvider configured = (ConfiguredProvider)factory;
data.put("description", configured.getHelpText());
providers.add(data);
}
return providers;
}
@Path("/form-action-providers")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public List<Map<String, String>> getFormActionProviders() {
this.auth.requireView();
List<ProviderFactory> factories = session.getKeycloakSessionFactory().getProviderFactories(FormAction.class);
return buildProviderMetadata(factories);
}
@Path("/flows") @Path("/flows")
@GET @GET
@NoCache @NoCache
@ -147,6 +226,161 @@ public class AuthenticationManagementResource {
return flows; return flows;
} }
@Path("/flows")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response createFlow(AuthenticationFlowModel model) {
this.auth.requireManage();
if (realm.getFlowByAlias(model.getAlias()) != null) {
return Response.status(Response.Status.CONFLICT).build();
}
realm.addAuthenticationFlow(model);
return Response.status(201).build();
}
@Path("/flows/{id}")
@GET
@NoCache
@Produces(MediaType.APPLICATION_JSON)
public AuthenticationFlowModel getFlow(@PathParam("id") String id) {
this.auth.requireView();
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
if (flow == null) {
throw new NotFoundException("Could not find flow with id");
}
return flow;
}
@Path("/flows/{id}")
@DELETE
@NoCache
public void deleteFlow(@PathParam("id") String id) {
this.auth.requireView();
AuthenticationFlowModel flow = realm.getAuthenticationFlowById(id);
if (flow == null) {
throw new NotFoundException("Could not find flow with id");
}
if (flow.isBuiltIn()) {
throw new BadRequestException("Can't delete built in flow");
}
realm.removeAuthenticationFlow(flow);
}
@Path("/flows/{flowAlias}/copy")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response copy(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
this.auth.requireManage();
String newName = data.get("newName");
if (realm.getFlowByAlias(newName) != null) {
return Response.status(Response.Status.CONFLICT).build();
}
AuthenticationFlowModel flow = realm.getFlowByAlias(flowAlias);
if (flow == null) {
logger.debug("flow not found: " + flowAlias);
return Response.status(NOT_FOUND).build();
}
AuthenticationFlowModel copy = new AuthenticationFlowModel();
copy.setAlias(newName);
copy.setDescription(flow.getDescription());
copy.setProviderId(flow.getProviderId());
copy.setBuiltIn(false);
copy.setTopLevel(flow.isTopLevel());
copy = realm.addAuthenticationFlow(copy);
copy(newName, flow, copy);
return Response.status(201).build();
}
protected void copy(String newName, AuthenticationFlowModel from, AuthenticationFlowModel to) {
for (AuthenticationExecutionModel execution : realm.getAuthenticationExecutions(from.getId())) {
if (execution.isAuthenticatorFlow()) {
AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
AuthenticationFlowModel copy = new AuthenticationFlowModel();
copy.setAlias(newName + " " + subFlow.getAlias());
copy.setDescription(subFlow.getDescription());
copy.setProviderId(subFlow.getProviderId());
copy.setBuiltIn(false);
copy.setTopLevel(false);
copy = realm.addAuthenticationFlow(copy);
execution.setFlowId(copy.getId());
copy(newName, subFlow, copy);
}
execution.setId(null);
execution.setParentFlow(to.getId());
realm.addAuthenticatorExecution(execution);
}
}
@Path("/flows/{flowAlias}/executions/flow")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void addExecutionFlow(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
this.auth.requireManage();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
throw new BadRequestException("Parent flow doesn't exists");
}
String alias = data.get("alias");
String type = data.get("type");
String provider = data.get("provider");
String description = data.get("description");
AuthenticationFlowModel newFlow = realm.getFlowByAlias(alias);
if (newFlow != null) {
throw new BadRequestException("New flow alias name already exists");
}
newFlow = new AuthenticationFlowModel();
newFlow.setAlias(alias);
newFlow.setDescription(description);
newFlow.setProviderId(type);
newFlow = realm.addAuthenticationFlow(newFlow);
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(parentFlow.getId());
execution.setFlowId(newFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
execution.setAuthenticatorFlow(true);
execution.setAuthenticator(provider);
realm.addAuthenticatorExecution(execution);
}
@Path("/flows/{flowAlias}/executions/execution")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public void addExecution(@PathParam("flowAlias") String flowAlias, Map<String, String> data) {
this.auth.requireManage();
AuthenticationFlowModel parentFlow = realm.getFlowByAlias(flowAlias);
if (parentFlow == null) {
throw new BadRequestException("Parent flow doesn't exists");
}
String provider = data.get("provider");
AuthenticationExecutionModel execution = new AuthenticationExecutionModel();
execution.setParentFlow(parentFlow.getId());
execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
execution.setAuthenticatorFlow(false);
execution.setAuthenticator(provider);
realm.addAuthenticatorExecution(execution);
}
@Path("/flows/{flowAlias}/executions") @Path("/flows/{flowAlias}/executions")
@GET @GET
@NoCache @NoCache
@ -160,12 +394,22 @@ public class AuthenticationManagementResource {
return Response.status(NOT_FOUND).build(); return Response.status(NOT_FOUND).build();
} }
List<AuthenticationExecutionRepresentation> result = new LinkedList<>(); List<AuthenticationExecutionRepresentation> result = new LinkedList<>();
List<AuthenticationExecutionModel> executions = AuthenticatorUtil.getEnabledExecutionsRecursively(realm, flow.getId());
int level = 0;
recurseExecutions(flow, result, level);
return Response.ok(result).build();
}
public void recurseExecutions(AuthenticationFlowModel flow, List<AuthenticationExecutionRepresentation> result, int level) {
int index = 0;
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(flow.getId());
for (AuthenticationExecutionModel execution : executions) { for (AuthenticationExecutionModel execution : executions) {
AuthenticationExecutionRepresentation rep = new AuthenticationExecutionRepresentation(); AuthenticationExecutionRepresentation rep = new AuthenticationExecutionRepresentation();
rep.setSubFlow(false); rep.setLevel(level);
rep.setIndex(index++);
rep.setRequirementChoices(new LinkedList<String>()); rep.setRequirementChoices(new LinkedList<String>());
if (execution.isAutheticatorFlow()) { if (execution.isAuthenticatorFlow()) {
AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getFlowId()); AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getFlowId());
if (AuthenticationFlow.BASIC_FLOW.equals(flowRef.getProviderId())) { if (AuthenticationFlow.BASIC_FLOW.equals(flowRef.getProviderId())) {
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name()); rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name());
@ -176,20 +420,20 @@ public class AuthenticationManagementResource {
rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name()); rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
rep.setProviderId(execution.getAuthenticator()); rep.setProviderId(execution.getAuthenticator());
rep.setAuthenticationConfig(execution.getAuthenticatorConfig()); rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
} }
rep.setReferenceType(flowRef.getAlias()); rep.setDisplayName(flowRef.getAlias());
rep.setConfigurable(false); rep.setConfigurable(false);
rep.setId(execution.getId()); rep.setId(execution.getId());
rep.setAuthenticationFlow(execution.isAuthenticatorFlow());
rep.setRequirement(execution.getRequirement().name()); rep.setRequirement(execution.getRequirement().name());
rep.setFlowId(execution.getFlowId());
result.add(rep); result.add(rep);
AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
recurseExecutions(subFlow, result, level + 1);
} else { } else {
if (!flow.getId().equals(execution.getParentFlow())) {
rep.setSubFlow(true);
}
String providerId = execution.getAuthenticator(); String providerId = execution.getAuthenticator();
ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId); ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId);
rep.setReferenceType(factory.getDisplayType()); rep.setDisplayName(factory.getDisplayType());
rep.setConfigurable(factory.isConfigurable()); rep.setConfigurable(factory.isConfigurable());
for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) { for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
rep.getRequirementChoices().add(choice.name()); rep.getRequirementChoices().add(choice.name());
@ -199,11 +443,8 @@ public class AuthenticationManagementResource {
rep.setProviderId(execution.getAuthenticator()); rep.setProviderId(execution.getAuthenticator());
rep.setAuthenticationConfig(execution.getAuthenticatorConfig()); rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
result.add(rep); result.add(rep);
} }
} }
return Response.ok(result).build();
} }
@Path("/flows/{flowAlias}/executions") @Path("/flows/{flowAlias}/executions")
@ -231,6 +472,134 @@ public class AuthenticationManagementResource {
} }
} }
@Path("/executions")
@POST
@NoCache
@Consumes(MediaType.APPLICATION_JSON)
public Response addExecution(AuthenticationExecutionModel model) {
this.auth.requireManage();
AuthenticationFlowModel parentFlow = getParentFlow(model);
if (parentFlow.isBuiltIn()) {
throw new BadRequestException("It is illegal to add execution to a built in flow");
}
int priority = 0;
List<AuthenticationExecutionModel> executions = getSortedExecutions(parentFlow);
for (AuthenticationExecutionModel execution : executions) {
priority = execution.getPriority();
}
if (priority > 0) priority += 10;
model.setPriority(priority);
model = realm.addAuthenticatorExecution(model);
return Response.created(uriInfo.getAbsolutePathBuilder().path(model.getId()).build()).build();
}
public AuthenticationFlowModel getParentFlow(AuthenticationExecutionModel model) {
if (model.getParentFlow() == null) {
throw new BadRequestException("parent flow not set on new execution");
}
AuthenticationFlowModel parentFlow = realm.getAuthenticationFlowById(model.getParentFlow());
if (parentFlow == null) {
throw new BadRequestException("execution parent flow does not exist");
}
return parentFlow;
}
@Path("/executions/{executionId}/raise-priority")
@POST
@NoCache
public void raisePriority(@PathParam("executionId") String execution) {
this.auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
session.getTransaction().setRollbackOnly();
throw new NotFoundException("Illegal execution");
}
AuthenticationFlowModel parentFlow = getParentFlow(model);
if (parentFlow.isBuiltIn()) {
throw new BadRequestException("It is illegal to modify execution in a built in flow");
}
List<AuthenticationExecutionModel> executions = getSortedExecutions(parentFlow);
AuthenticationExecutionModel previous = null;
for (AuthenticationExecutionModel exe : executions) {
if (exe.getId().equals(model.getId())) {
break;
}
previous = exe;
}
if (previous == null) return;
int tmp = previous.getPriority();
previous.setPriority(model.getPriority());
realm.updateAuthenticatorExecution(previous);
model.setPriority(tmp);
realm.updateAuthenticatorExecution(model);
}
public List<AuthenticationExecutionModel> getSortedExecutions(AuthenticationFlowModel parentFlow) {
List<AuthenticationExecutionModel> executions = realm.getAuthenticationExecutions(parentFlow.getId());
Collections.sort(executions, AuthenticationExecutionModel.ExecutionComparator.SINGLETON);
return executions;
}
@Path("/executions/{executionId}/lower-priority")
@POST
@NoCache
public void lowerPriority(@PathParam("executionId") String execution) {
this.auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
session.getTransaction().setRollbackOnly();
throw new NotFoundException("Illegal execution");
}
AuthenticationFlowModel parentFlow = getParentFlow(model);
if (parentFlow.isBuiltIn()) {
throw new BadRequestException("It is illegal to modify execution in a built in flow");
}
List<AuthenticationExecutionModel> executions = getSortedExecutions(parentFlow);
int i = 0;
for (i = 0; i < executions.size(); i++) {
if (executions.get(i).getId().equals(model.getId())) {
break;
}
}
if (i + 1 >= executions.size()) return;
AuthenticationExecutionModel next = executions.get(i + 1);
int tmp = model.getPriority();
model.setPriority(next.getPriority());
realm.updateAuthenticatorExecution(model);
next.setPriority(tmp);
realm.updateAuthenticatorExecution(next);
}
@Path("/executions/{executionId}")
@DELETE
@NoCache
public void removeExecution(@PathParam("executionId") String execution) {
this.auth.requireManage();
AuthenticationExecutionModel model = realm.getAuthenticationExecutionById(execution);
if (model == null) {
session.getTransaction().setRollbackOnly();
throw new NotFoundException("Illegal execution");
}
AuthenticationFlowModel parentFlow = getParentFlow(model);
if (parentFlow.isBuiltIn()) {
throw new BadRequestException("It is illegal to remove execution from a built in flow");
}
realm.removeAuthenticatorExecution(model);
}
@Path("/executions/{executionId}/config") @Path("/executions/{executionId}/config")
@POST @POST
@NoCache @NoCache