keycloak-memoizeit

Changes

Details

diff --git a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
index dbcc833..c1f5843 100755
--- a/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
+++ b/connections/jpa/src/main/java/org/keycloak/connections/jpa/updater/JpaUpdaterProvider.java
@@ -12,7 +12,7 @@ public interface JpaUpdaterProvider extends Provider {
 
     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);
 
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml
new file mode 100755
index 0000000..4f33cf7
--- /dev/null
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-1.5.0.xml
@@ -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>
diff --git a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
index efba42c..ca5d0e9 100755
--- a/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
+++ b/connections/jpa-liquibase/src/main/resources/META-INF/jpa-changelog-master.xml
@@ -8,4 +8,5 @@
     <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.4.0.xml"/>
+    <include file="META-INF/jpa-changelog-1.5.0.xml"/>
 </databaseChangeLog>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
index 5e01598..fbaefd1 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/app.js
@@ -1080,10 +1080,70 @@ module.config([ '$routeProvider', function($routeProvider) {
                 },
                 flows : function(AuthenticationFlowsLoader) {
                     return AuthenticationFlowsLoader();
+                },
+                selectedFlow : function() {
+                    return null;
+                }
+            },
+            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', {
             templateUrl : resourceUrl + '/partials/required-actions.html',
             resolve : {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
index 15a8fa9..d17505c 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/realm.js
@@ -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.flows = flows;
     if (flows.length > 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() {
-        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.flowmax = 0;
+            $scope.choicesmax = 0;
+            $scope.levelmax = 0;
             for (var i = 0; i < $scope.executions.length; i++ ) {
-                execution = $scope.executions[i];
-                if (execution.requirementChoices.length > $scope.flowmax) {
-                    $scope.flowmax = execution.requirementChoices.length;
+                var execution = $scope.executions[i];
+                if (execution.requirementChoices.length > $scope.choicesmax) {
+                    $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++ ) {
-                execution = $scope.executions[i];
+                var execution = $scope.executions[i];
                 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.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) {
         var copy = angular.copy(execution);
         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");
             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;
 
     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');
     $scope.realm = realm;
     $scope.requiredActions = [];
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
index c347759..2c30dd3 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/loaders.js
@@ -354,11 +354,45 @@ module.factory('IdentityProviderMapperLoader', function(Loader, IdentityProvider
 module.factory('AuthenticationFlowsLoader', function(Loader, AuthenticationFlows, $route, $q) {
     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 {
+            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) {
     return Loader.get(AuthenticationConfigDescription, function () {
         return {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
index 04929ef..64a5ff5 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/services.js
@@ -94,6 +94,34 @@ module.service('Dialog', function($modal) {
 	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) {
 	// time (in ms) the notifications are shown
 	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', {
         realm : '@realm',
         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) {
-    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'
     });
 });
+
+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) {
     return $resource(authUrl + '/admin/realms/:realm/authentication/config-description/:provider', {
         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) {
     var dialog = {};
 
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
index 50f01b9..4f2794b 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/authentication-flows.html
@@ -6,43 +6,35 @@
     <table class="table table-striped table-bordered">
         <thead>
         <tr>
-            <th colspan="6" class="kc-table-actions">
+            <th colspan="{{levelmax + 1 + choicesmax + 4}}" class="kc-table-actions">
                 <div class="dropdown pull-left">
                     <select class="form-control" ng-model="flow"
                             ng-options="(flow.alias|capitalize) for flow in flows"
                             data-ng-change="setupForm()">
                     </select>
                 </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>
-        </tr>
+         </tr>
         <tr data-ng-hide="executions.length == 0">
-            <th colspan="2">Auth Type</th>
-            <th colspan="{{flowmax}}">Requirement</th>
+            <th colspan="{{levelmax + 1}}">Auth Type</th>
+            <th colspan="{{choicesmax}}">Requirement</th>
             <th></th>
         </tr>
         </thead>
         <tbody>
         <tr ng-repeat="execution in executions" data-ng-show="executions.length > 0">
-            <td ng-show="execution.subFlow"></td>
-            <td>{{execution.referenceType|capitalize}}</td>
-            <td ng-hide="execution.subFlow"></td>
+            <td ng-repeat="lev in execution.preLevels"></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-repeat="lev in execution.postLevels"></td>
             <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 >
                 <input type="radio" ng-model="execution.requirement" ng-value="choice" ng-change="updateExecution(execution)">
                 {{choice}}
@@ -51,8 +43,20 @@
             </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>
+                <ul class="nav navbar-nav navbar-utility" data-ng-hide="flow.builtIn && !execution.configurable">
+                    <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>
          </tr>
         <tr data-ng-show="executions.length == 0">
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
new file mode 100755
index 0000000..53c82d1
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-execution.html
@@ -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>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html
new file mode 100755
index 0000000..a298b85
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow.html
@@ -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>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html
new file mode 100755
index 0000000..c88d76b
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-flow-execution.html
@@ -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>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html
index 55c5430..866ecf0 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/modal/role-selector.html
@@ -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">
-    <h2>Role Selector</h2>
     <form>
         <div data-ng-show="realmRoles.length > 0">
         <label class="control-label" for="available">Realm Roles</label>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-copy.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-copy.html
new file mode 100755
index 0000000..e074432
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/templates/kc-copy.html
@@ -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>
\ No newline at end of file
diff --git a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
index f390d1f..acb3975 100755
--- a/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
+++ b/model/api/src/main/java/org/keycloak/models/AuthenticationExecutionModel.java
@@ -23,9 +23,8 @@ public class AuthenticationExecutionModel implements Serializable {
     private String authenticatorConfig;
     private String authenticator;
     private String flowId;
-    private boolean autheticatorFlow;
+    private boolean authenticatorFlow;
     private Requirement requirement;
-    private boolean userSetupAllowed;
     private int priority;
     private String parentFlow;
 
@@ -69,14 +68,6 @@ public class AuthenticationExecutionModel implements Serializable {
         this.priority = priority;
     }
 
-    public boolean isUserSetupAllowed() {
-        return userSetupAllowed;
-    }
-
-    public void setUserSetupAllowed(boolean userSetupAllowed) {
-        this.userSetupAllowed = userSetupAllowed;
-    }
-
     public String getParentFlow() {
         return parentFlow;
     }
@@ -103,12 +94,12 @@ public class AuthenticationExecutionModel implements Serializable {
      *
      * @return
      */
-    public boolean isAutheticatorFlow() {
-        return autheticatorFlow;
+    public boolean isAuthenticatorFlow() {
+        return authenticatorFlow;
     }
 
-    public void setAutheticatorFlow(boolean autheticatorFlow) {
-        this.autheticatorFlow = autheticatorFlow;
+    public void setAuthenticatorFlow(boolean authenticatorFlow) {
+        this.authenticatorFlow = authenticatorFlow;
     }
 
     public enum Requirement {
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 1733c06..7995887 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -2,13 +2,9 @@ package org.keycloak.models.utils;
 
 import org.keycloak.models.AuthenticationExecutionModel;
 import org.keycloak.models.AuthenticationFlowModel;
-import org.keycloak.models.AuthenticatorConfigModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RequiredCredentialModel;
 
-import java.util.HashMap;
-import java.util.Map;
-
 /**
  * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
  * @version $Revision: 1 $
@@ -59,8 +55,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("registration-page-form");
         execution.setPriority(10);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(true);
+        execution.setAuthenticatorFlow(true);
         execution.setFlowId(registrationFormFlow.getId());
         realm.addAuthenticatorExecution(execution);
 
@@ -69,8 +64,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("registration-user-creation");
         execution.setPriority(20);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         execution = new AuthenticationExecutionModel();
@@ -78,8 +72,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("registration-profile-action");
         execution.setPriority(40);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         execution = new AuthenticationExecutionModel();
@@ -87,8 +80,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("registration-password-action");
         execution.setPriority(50);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         //AuthenticatorConfigModel captchaConfig = new AuthenticatorConfigModel();
@@ -103,8 +95,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.DISABLED);
         execution.setAuthenticator("registration-recaptcha-action");
         execution.setPriority(60);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         //execution.setAuthenticatorConfig(captchaConfig.getId());
         realm.addAuthenticatorExecution(execution);
 
@@ -141,8 +132,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("direct-grant-validate-username");
         execution.setPriority(10);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         // password
@@ -154,8 +144,7 @@ public class DefaultAuthenticationFlows {
         }
         execution.setAuthenticator("direct-grant-validate-password");
         execution.setPriority(20);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         // otp
@@ -167,8 +156,7 @@ public class DefaultAuthenticationFlows {
         }
         execution.setAuthenticator("direct-grant-validate-otp");
         execution.setPriority(30);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
 
@@ -188,8 +176,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
         execution.setAuthenticator("auth-cookie");
         execution.setPriority(10);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
         execution = new AuthenticationExecutionModel();
         execution.setParentFlow(browser.getId());
@@ -200,8 +187,7 @@ public class DefaultAuthenticationFlows {
         }
         execution.setAuthenticator("auth-spnego");
         execution.setPriority(20);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
 
@@ -217,8 +203,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.ALTERNATIVE);
         execution.setFlowId(forms.getId());
         execution.setPriority(30);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(true);
+        execution.setAuthenticatorFlow(true);
         realm.addAuthenticatorExecution(execution);
 
         // forms
@@ -228,8 +213,7 @@ public class DefaultAuthenticationFlows {
         execution.setRequirement(AuthenticationExecutionModel.Requirement.REQUIRED);
         execution.setAuthenticator("auth-username-password-form");
         execution.setPriority(10);
-        execution.setUserSetupAllowed(false);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
 
         // otp processing
@@ -243,8 +227,7 @@ public class DefaultAuthenticationFlows {
 
         execution.setAuthenticator("auth-otp-form");
         execution.setPriority(20);
-        execution.setUserSetupAllowed(true);
-        execution.setAutheticatorFlow(false);
+        execution.setAuthenticatorFlow(false);
         realm.addAuthenticatorExecution(execution);
     }
 }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index c2b14cd..3fcde28 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -462,13 +462,12 @@ public class ModelToRepresentation {
             rep.setAuthenticatorConfig(config.getAlias());
         }
         rep.setAuthenticator(model.getAuthenticator());
-        rep.setAutheticatorFlow(model.isAutheticatorFlow());
+        rep.setAutheticatorFlow(model.isAuthenticatorFlow());
         if (model.getFlowId() != null) {
             AuthenticationFlowModel flow = realm.getAuthenticationFlowById(model.getFlowId());
             rep.setFlowAlias(flow.getAlias());
        }
         rep.setPriority(model.getPriority());
-        rep.setUserSetupAllowed(model.isUserSetupAllowed());
         rep.setRequirement(model.getRequirement().name());
         return rep;
     }
diff --git a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index 83c8273..92060b0 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1077,13 +1077,12 @@ public class RepresentationToModel {
             model.setAuthenticatorConfig(config.getId());
         }
         model.setAuthenticator(rep.getAuthenticator());
-        model.setAutheticatorFlow(rep.isAutheticatorFlow());
+        model.setAuthenticatorFlow(rep.isAutheticatorFlow());
         if (rep.getFlowAlias() != null) {
             AuthenticationFlowModel flow = realm.getFlowByAlias(rep.getFlowAlias());
             model.setFlowId(flow.getId());
         }
         model.setPriority(rep.getPriority());
-        model.setUserSetupAllowed(rep.isUserSetupAllowed());
         model.setRequirement(AuthenticationExecutionModel.Requirement.valueOf(rep.getRequirement()));
         return model;
     }
diff --git a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
index 09d4304..c2d85d3 100755
--- a/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
+++ b/model/file/src/main/java/org/keycloak/models/file/adapter/RealmAdapter.java
@@ -1308,13 +1308,12 @@ public class RealmAdapter implements RealmModel {
     public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
         AuthenticationExecutionModel model = new AuthenticationExecutionModel();
         model.setId(entity.getId());
-        model.setUserSetupAllowed(entity.isUserSetupAllowed());
         model.setRequirement(entity.getRequirement());
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
         model.setParentFlow(entity.getParentFlow());
         model.setFlowId(entity.getFlowId());
-        model.setAutheticatorFlow(entity.isAuthenticatorFlow());
+        model.setAuthenticatorFlow(entity.isAuthenticatorFlow());
         model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
         return model;
     }
@@ -1345,8 +1344,7 @@ public class RealmAdapter implements RealmModel {
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
-        entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
         entity.setFlowId(model.getFlowId());
         entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
         AuthenticationFlowEntity flow = getFlowEntity(model.getId());
@@ -1366,12 +1364,11 @@ public class RealmAdapter implements RealmModel {
             }
         }
         if (entity == null) return;
-        entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
         entity.setFlowId(model.getFlowId());
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
         entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
     }
 
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
index dd6e2e7..f4b1e35 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/entities/CachedRealm.java
@@ -197,6 +197,7 @@ public class CachedRealm implements Serializable {
         defaultLocale = model.getDefaultLocale();
         for (AuthenticationFlowModel flow : model.getAuthenticationFlows()) {
             authenticationFlows.put(flow.getId(), flow);
+            authenticationExecutions.put(flow.getId(), new LinkedList<AuthenticationExecutionModel>());
             for (AuthenticationExecutionModel execution : model.getAuthenticationExecutions(flow.getId())) {
                 authenticationExecutions.add(flow.getId(), execution);
                 executionsById.put(execution.getId(), execution);
diff --git a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
index fe2ded8..84938bf 100755
--- a/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
+++ b/model/invalidation-cache/model-adapters/src/main/java/org/keycloak/models/cache/RealmAdapter.java
@@ -1075,7 +1075,6 @@ public class RealmAdapter implements RealmModel {
     @Override
     public List<AuthenticationExecutionModel> getAuthenticationExecutions(String flowId) {
         if (updated != null) return updated.getAuthenticationExecutions(flowId);
-        List<AuthenticationExecutionModel> models = new ArrayList<>();
         return cached.getAuthenticationExecutions().get(flowId);
     }
 
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
index 60dfccd..574849f 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/AuthenticationExecutionEntity.java
@@ -51,9 +51,6 @@ public class AuthenticationExecutionEntity {
     @Column(name="PRIORITY")
     protected int priority;
 
-    @Column(name="USER_SETUP_ALLOWED")
-    private boolean userSetupAllowed;
-
     @Column(name="AUTHENTICATOR_FLOW")
     private boolean autheticatorFlow;
 
@@ -97,14 +94,6 @@ public class AuthenticationExecutionEntity {
         this.priority = priority;
     }
 
-    public boolean isUserSetupAllowed() {
-        return userSetupAllowed;
-    }
-
-    public void setUserSetupAllowed(boolean userSetupAllowed) {
-        this.userSetupAllowed = userSetupAllowed;
-    }
-
     public boolean isAutheticatorFlow() {
         return autheticatorFlow;
     }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
index 51148f0..c7dcf5a 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/RealmAdapter.java
@@ -1622,13 +1622,12 @@ public class RealmAdapter implements RealmModel {
     public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
         AuthenticationExecutionModel model = new AuthenticationExecutionModel();
         model.setId(entity.getId());
-        model.setUserSetupAllowed(entity.isUserSetupAllowed());
         model.setRequirement(entity.getRequirement());
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
         model.setFlowId(entity.getFlowId());
         model.setParentFlow(entity.getParentFlow().getId());
-        model.setAutheticatorFlow(entity.isAutheticatorFlow());
+        model.setAuthenticatorFlow(entity.isAutheticatorFlow());
         model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
         return model;
     }
@@ -1654,8 +1653,7 @@ public class RealmAdapter implements RealmModel {
         entity.setParentFlow(flow);
         flow.getExecutions().add(entity);
         entity.setRealm(realm);
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
-        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        entity.setAutheticatorFlow(model.isAuthenticatorFlow());
         em.persist(entity);
         em.flush();
         model.setId(entity.getId());
@@ -1667,11 +1665,10 @@ public class RealmAdapter implements RealmModel {
     public void updateAuthenticatorExecution(AuthenticationExecutionModel model) {
         AuthenticationExecutionEntity entity = em.find(AuthenticationExecutionEntity.class, model.getId());
         if (entity == null) return;
-        entity.setAutheticatorFlow(model.isAutheticatorFlow());
+        entity.setAutheticatorFlow(model.isAuthenticatorFlow());
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
         entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
         entity.setFlowId(model.getFlowId());
         em.flush();
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
index 823a51b..795ccbf 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/RealmAdapter.java
@@ -1384,13 +1384,12 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
     public AuthenticationExecutionModel entityToModel(AuthenticationExecutionEntity entity) {
         AuthenticationExecutionModel model = new AuthenticationExecutionModel();
         model.setId(entity.getId());
-        model.setUserSetupAllowed(entity.isUserSetupAllowed());
         model.setRequirement(entity.getRequirement());
         model.setPriority(entity.getPriority());
         model.setAuthenticator(entity.getAuthenticator());
         model.setFlowId(entity.getFlowId());
         model.setParentFlow(entity.getParentFlow());
-        model.setAutheticatorFlow(entity.isAuthenticatorFlow());
+        model.setAuthenticatorFlow(entity.isAuthenticatorFlow());
         model.setAuthenticatorConfig(entity.getAuthenticatorConfig());
         return model;
     }
@@ -1421,8 +1420,7 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
-        entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
         entity.setFlowId(model.getFlowId());
         entity.setParentFlow(model.getParentFlow());
         entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
@@ -1444,12 +1442,11 @@ public class RealmAdapter extends AbstractMongoAdapter<MongoRealmEntity> impleme
             }
         }
         if (entity == null) return;
-        entity.setAuthenticatorFlow(model.isAutheticatorFlow());
+        entity.setAuthenticatorFlow(model.isAuthenticatorFlow());
         entity.setAuthenticator(model.getAuthenticator());
         entity.setPriority(model.getPriority());
         entity.setRequirement(model.getRequirement());
         entity.setFlowId(model.getFlowId());
-        entity.setUserSetupAllowed(model.isUserSetupAllowed());
         entity.setAuthenticatorConfig(model.getAuthenticatorConfig());
         updateMongoEntity();
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
index da9209d..4845b12 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/CookieAuthenticatorFactory.java
@@ -78,4 +78,10 @@ public class CookieAuthenticatorFactory implements AuthenticatorFactory {
     public List<ProviderConfigProperty> getConfigProperties() {
         return null;
     }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
index 29b1c63..1a01e5a 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/OTPFormAuthenticatorFactory.java
@@ -59,6 +59,11 @@ public class OTPFormAuthenticatorFactory implements AuthenticatorFactory {
         return false;
     }
 
+    @Override
+    public boolean isUserSetupAllowed() {
+        return true;
+    }
+
     public static final AuthenticationExecutionModel.Requirement[] REQUIREMENT_CHOICES = {
             AuthenticationExecutionModel.Requirement.REQUIRED,
             AuthenticationExecutionModel.Requirement.OPTIONAL,
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
index 4629675..ecfa068 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/SpnegoAuthenticatorFactory.java
@@ -84,4 +84,10 @@ public class SpnegoAuthenticatorFactory implements AuthenticatorFactory {
     public List<ProviderConfigProperty> getConfigProperties() {
         return null;
     }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
index adc2815..392a02a 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/browser/UsernamePasswordFormFactory.java
@@ -81,4 +81,10 @@ public class UsernamePasswordFormFactory implements AuthenticatorFactory {
     public List<ProviderConfigProperty> getConfigProperties() {
         return null;
     }
+
+    @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
 }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
index 8a71f52..7d4da65 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateOTP.java
@@ -83,6 +83,12 @@ public class ValidateOTP extends AbstractDirectGrantAuthenticator {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public String getDisplayType() {
         return "OTP";
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
index 55ba2f8..792fd5e 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidatePassword.java
@@ -74,6 +74,12 @@ public class ValidatePassword extends AbstractDirectGrantAuthenticator {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public String getDisplayType() {
         return "Password";
     }
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
index 330860b..5a9cd80 100755
--- a/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/directgrant/ValidateUsername.java
@@ -104,6 +104,12 @@ public class ValidateUsername extends AbstractDirectGrantAuthenticator {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public String getDisplayType() {
         return "Username Validation";
     }
diff --git a/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java b/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
index 45e8c3a..aa8a3a8 100755
--- a/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
+++ b/services/src/main/java/org/keycloak/authentication/AuthenticatorUtil.java
@@ -1,7 +1,6 @@
 package org.keycloak.authentication;
 
 import org.keycloak.models.AuthenticationExecutionModel;
-import org.keycloak.models.AuthenticatorConfigModel;
 import org.keycloak.models.RealmModel;
 
 import java.util.LinkedList;
@@ -21,9 +20,11 @@ public class AuthenticatorUtil {
     }
 
     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);
-            if (model.isAutheticatorFlow() && model.isEnabled()) {
+            if (model.isAuthenticatorFlow() && model.isEnabled()) {
                 recurseExecutions(realm, model.getFlowId(), executions);
             }
         }
@@ -31,7 +32,7 @@ public class AuthenticatorUtil {
 
     public static AuthenticationExecutionModel findExecutionByAuthenticator(RealmModel realm, String flowId, String authProviderId) {
         for (AuthenticationExecutionModel model : realm.getAuthenticationExecutions(flowId)) {
-            if (model.isAutheticatorFlow()) {
+            if (model.isAuthenticatorFlow()) {
                 AuthenticationExecutionModel recurse = findExecutionByAuthenticator(realm, model.getFlowId(), authProviderId);
                 if (recurse != null) return recurse;
 
diff --git a/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
index 9e8705a..ab807ca 100755
--- a/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
+++ b/services/src/main/java/org/keycloak/authentication/ConfigurableAuthenticatorFactory.java
@@ -25,4 +25,14 @@ public interface ConfigurableAuthenticatorFactory extends ConfiguredProvider {
      * @return
      */
     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();
 }
diff --git a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
index 524d7a9..a84131a 100755
--- a/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/DefaultAuthenticationFlow.java
@@ -49,7 +49,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                     alternativeSuccessful = true;
                 continue;
             }
-            if (model.isAutheticatorFlow()) {
+            if (model.isAuthenticatorFlow()) {
                 AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
                 return authenticationFlow.processAction(actionExecution);
             } else if (model.getId().equals(actionExecution)) {
@@ -79,7 +79,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                 processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
                 continue;
             }
-            if (model.isAutheticatorFlow()) {
+            if (model.isAuthenticatorFlow()) {
                 AuthenticationFlow authenticationFlow = processor.createFlowExecution(model.getFlowId(), model);
                 Response flowChallenge = authenticationFlow.processFlow();
                 if (flowChallenge == null) {
@@ -124,7 +124,7 @@ public class DefaultAuthenticationFlow implements AuthenticationFlow {
                 configuredFor = authenticator.configuredFor(processor.getSession(), processor.getRealm(), authUser);
                 if (!configuredFor) {
                     if (model.isRequired()) {
-                        if (model.isUserSetupAllowed()) {
+                        if (factory.isUserSetupAllowed()) {
                             AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", factory.getId());
                             processor.getClientSession().setExecutionStatus(model.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
                             authenticator.setRequiredActions(processor.getSession(), processor.getRealm(), processor.getClientSession().getAuthenticatedUser());
diff --git a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
index 782c862..d244740 100755
--- a/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
+++ b/services/src/main/java/org/keycloak/authentication/FormAuthenticationFlow.java
@@ -152,7 +152,8 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
                 executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SKIPPED);
                 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();
             if (action.requiresUser() && authUser == null) {
@@ -163,7 +164,7 @@ public class FormAuthenticationFlow implements AuthenticationFlow {
                 configuredFor = action.configuredFor(processor.getSession(), processor.getRealm(), authUser);
                 if (!configuredFor) {
                     if (formActionExecution.isRequired()) {
-                        if (formActionExecution.isUserSetupAllowed()) {
+                        if (factory.isUserSetupAllowed()) {
                             AuthenticationProcessor.logger.debugv("authenticator SETUP_REQUIRED: {0}", formExecution.getAuthenticator());
                             executionStatus.put(formActionExecution.getId(), ClientSessionModel.ExecutionStatus.SETUP_REQUIRED);
                             requiredActions.add(action);
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
index 37c0d23..9a0e995 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPage.java
@@ -45,7 +45,7 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
 
     @Override
     public String getHelpText() {
-        return null;
+        return "This is the controller for the registration page";
     }
 
     @Override
@@ -83,6 +83,11 @@ public class RegistrationPage implements FormAuthenticator, FormAuthenticatorFac
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+    @Override
     public void postInit(KeycloakSessionFactory factory) {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
index 92c87f6..ade4e00 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationPassword.java
@@ -34,7 +34,7 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
 
     @Override
     public String getHelpText() {
-        return null;
+        return "Validates that password matches password confirmation field.  It also will store password in user's credential store.";
     }
 
     @Override
@@ -106,6 +106,11 @@ public class RegistrationPassword implements FormAction, FormActionFactory {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+    @Override
     public void close() {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
index 46c4bda..2fd3c85 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationProfile.java
@@ -31,7 +31,7 @@ public class RegistrationProfile implements FormAction, FormActionFactory {
 
     @Override
     public String getHelpText() {
-        return null;
+        return "Validates email, first name, and last name attributes and stores them in user data.";
     }
 
     @Override
@@ -111,6 +111,12 @@ public class RegistrationProfile implements FormAction, FormActionFactory {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public void close() {
 
     }
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
index 8f143ed..df75e4f 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationRecaptcha.java
@@ -159,6 +159,12 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public void close() {
 
     }
@@ -185,7 +191,7 @@ public class RegistrationRecaptcha implements FormAction, FormActionFactory, Con
 
     @Override
     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>();
diff --git a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
index 9865957..40d2fb0 100755
--- a/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
+++ b/services/src/main/java/org/keycloak/authentication/forms/RegistrationUserCreation.java
@@ -35,7 +35,7 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
 
     @Override
     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
@@ -150,6 +150,12 @@ public class RegistrationUserCreation implements FormAction, FormActionFactory {
     }
 
     @Override
+    public boolean isUserSetupAllowed() {
+        return false;
+    }
+
+
+    @Override
     public void close() {
 
     }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
index c4618ea..3ce1e19 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/RoleNameMapper.java
@@ -35,7 +35,7 @@ public class RoleNameMapper extends AbstractOIDCProtocolMapper implements OIDCAc
         property.setName(ROLE_CONFIG);
         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.setType(ProviderConfigProperty.STRING_TYPE);
+        property.setType(ProviderConfigProperty.ROLE_TYPE);
         configProperties.add(property);
         property = new ProviderConfigProperty();
         property.setName(NEW_ROLE_NAME);
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
index 5f55c76..b792213 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/AuthenticationManagementResource.java
@@ -2,17 +2,25 @@ package org.keycloak.services.resources.admin;
 
 import org.jboss.logging.Logger;
 import org.jboss.resteasy.annotations.cache.NoCache;
+import org.jboss.resteasy.spi.BadRequestException;
 import org.jboss.resteasy.spi.NotFoundException;
 import org.keycloak.authentication.AuthenticationFlow;
+import org.keycloak.authentication.Authenticator;
 import org.keycloak.authentication.AuthenticatorUtil;
 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.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.ConfiguredProvider;
 import org.keycloak.provider.ProviderConfigProperty;
+import org.keycloak.provider.ProviderFactory;
 import org.keycloak.representations.idm.ConfigPropertyRepresentation;
 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.Response;
 import javax.ws.rs.core.UriInfo;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -59,13 +68,16 @@ public class AuthenticationManagementResource {
 
     public static class AuthenticationExecutionRepresentation {
         protected String id;
-        protected String referenceType;
         protected String requirement;
+        protected String displayName;
         protected List<String> requirementChoices;
         protected Boolean configurable;
-        protected Boolean subFlow;
+        protected Boolean authenticationFlow;
         protected String providerId;
         protected String authenticationConfig;
+        protected String flowId;
+        protected int level;
+        protected int index;
 
         public String getId() {
             return id;
@@ -75,12 +87,12 @@ public class AuthenticationManagementResource {
             this.id = execution;
         }
 
-        public String getReferenceType() {
-            return referenceType;
+        public String getDisplayName() {
+            return displayName;
         }
 
-        public void setReferenceType(String referenceType) {
-            this.referenceType = referenceType;
+        public void setDisplayName(String displayName) {
+            this.displayName = displayName;
         }
 
         public String getRequirement() {
@@ -107,14 +119,6 @@ public class AuthenticationManagementResource {
             this.configurable = configurable;
         }
 
-        public Boolean getSubFlow() {
-            return subFlow;
-        }
-
-        public void setSubFlow(Boolean subFlow) {
-            this.subFlow = subFlow;
-        }
-
         public String getProviderId() {
             return providerId;
         }
@@ -130,8 +134,83 @@ public class AuthenticationManagementResource {
         public void setAuthenticationConfig(String 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")
     @GET
     @NoCache
@@ -147,6 +226,161 @@ public class AuthenticationManagementResource {
         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")
     @GET
     @NoCache
@@ -160,12 +394,22 @@ public class AuthenticationManagementResource {
             return Response.status(NOT_FOUND).build();
         }
         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) {
             AuthenticationExecutionRepresentation rep = new AuthenticationExecutionRepresentation();
-            rep.setSubFlow(false);
+            rep.setLevel(level);
+            rep.setIndex(index++);
             rep.setRequirementChoices(new LinkedList<String>());
-            if (execution.isAutheticatorFlow()) {
+            if (execution.isAuthenticatorFlow()) {
                 AuthenticationFlowModel flowRef = realm.getAuthenticationFlowById(execution.getFlowId());
                 if (AuthenticationFlow.BASIC_FLOW.equals(flowRef.getProviderId())) {
                     rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.ALTERNATIVE.name());
@@ -176,20 +420,20 @@ public class AuthenticationManagementResource {
                     rep.getRequirementChoices().add(AuthenticationExecutionModel.Requirement.DISABLED.name());
                     rep.setProviderId(execution.getAuthenticator());
                     rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
-
                 }
-                rep.setReferenceType(flowRef.getAlias());
+                rep.setDisplayName(flowRef.getAlias());
                 rep.setConfigurable(false);
                 rep.setId(execution.getId());
+                rep.setAuthenticationFlow(execution.isAuthenticatorFlow());
                 rep.setRequirement(execution.getRequirement().name());
+                rep.setFlowId(execution.getFlowId());
                 result.add(rep);
+                AuthenticationFlowModel subFlow = realm.getAuthenticationFlowById(execution.getFlowId());
+                recurseExecutions(subFlow, result, level + 1);
             } else {
-                if (!flow.getId().equals(execution.getParentFlow())) {
-                    rep.setSubFlow(true);
-                }
                 String providerId = execution.getAuthenticator();
                 ConfigurableAuthenticatorFactory factory = CredentialHelper.getConfigurableAuthenticatorFactory(session, providerId);
-                rep.setReferenceType(factory.getDisplayType());
+                rep.setDisplayName(factory.getDisplayType());
                 rep.setConfigurable(factory.isConfigurable());
                 for (AuthenticationExecutionModel.Requirement choice : factory.getRequirementChoices()) {
                     rep.getRequirementChoices().add(choice.name());
@@ -199,11 +443,8 @@ public class AuthenticationManagementResource {
                 rep.setProviderId(execution.getAuthenticator());
                 rep.setAuthenticationConfig(execution.getAuthenticatorConfig());
                 result.add(rep);
-
             }
-
         }
-        return Response.ok(result).build();
     }
 
     @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")
     @POST
     @NoCache