keycloak-aplcache

refactor client create

12/21/2015 7:36:13 PM

Details

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 5a2c747..e5e1bb9 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
@@ -1132,7 +1132,7 @@ module.config([ '$routeProvider', function($routeProvider) {
             controller : 'UserRoleMappingCtrl'
         })
         .when('/create/client/:realm', {
-            templateUrl : resourceUrl + '/partials/client-detail.html',
+            templateUrl : resourceUrl + '/partials/create-client.html',
             resolve : {
                 realm : function(RealmLoader) {
                     return RealmLoader();
@@ -1150,7 +1150,7 @@ module.config([ '$routeProvider', function($routeProvider) {
                     return ServerInfoLoader();
                 }
             },
-            controller : 'ClientDetailCtrl'
+            controller : 'CreateClientCtrl'
         })
         .when('/realms/:realm/clients/:client', {
             templateUrl : resourceUrl + '/partials/client-detail.html',
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
index ec28ef4..1424307 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/js/controllers/clients.js
@@ -736,7 +736,8 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         "bearer-only"
     ];
 
-    $scope.protocols = Object.keys(serverInfo.providers['login-protocol'].providers).sort();
+    $scope.protocols = ['openid-connect',
+                        'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort();
 
     $scope.templates = [ {name:'NONE'}];
     for (var i = 0; i < templates.length; i++) {
@@ -765,7 +766,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
     ];
 
     $scope.realm = realm;
-    $scope.create = !client.clientId;
     $scope.samlAuthnStatement = false;
     $scope.samlMultiValuedRoles = false;
     $scope.samlServerSignature = false;
@@ -870,20 +870,6 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
     if (!$scope.create) {
         $scope.client = angular.copy(client);
         updateProperties();
-    } else {
-        $scope.client = {
-            enabled: true,
-            standardFlowEnabled: true,
-            attributes: {}
-        };
-        $scope.client.attributes['saml_signature_canonicalization_method'] = $scope.canonicalization[0].value;
-        $scope.client.redirectUris = [];
-        $scope.accessType = $scope.accessTypes[0];
-        $scope.protocol = $scope.protocols[0];
-        $scope.signatureAlgorithm = $scope.signatureAlgorithms[1];
-        $scope.nameIdFormat = $scope.nameIdFormats[0];
-        $scope.samlAuthnStatement = true;
-        $scope.samlForceNameIdFormat = false;
     }
 
 
@@ -1055,29 +1041,121 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
         if ($scope.client.protocol != 'saml' && !$scope.client.bearerOnly && ($scope.client.standardFlowEnabled || $scope.client.implicitFlowEnabled) && (!$scope.client.redirectUris || $scope.client.redirectUris.length == 0)) {
             Notifications.error("You must specify at least one redirect uri");
         } else {
-            if ($scope.create) {
-                Client.save({
-                    realm: realm.realm,
-                    client: ''
-                }, $scope.client, function (data, headers) {
-                    $scope.changed = false;
-                    var l = headers().location;
-                    var id = l.substring(l.lastIndexOf("/") + 1);
-                    $location.url("/realms/" + realm.realm + "/clients/" + id);
-                    Notifications.success("The client has been created.");
-                });
-            } else {
-                Client.update({
-                    realm : realm.realm,
-                    client : client.id
-                }, $scope.client, function() {
-                    $scope.changed = false;
-                    client = angular.copy($scope.client);
-                    $location.url("/realms/" + realm.realm + "/clients/" + client.id);
-                    Notifications.success("Your changes have been saved to the client.");
-                });
+            Client.update({
+                realm : realm.realm,
+                client : client.id
+            }, $scope.client, function() {
+                $scope.changed = false;
+                client = angular.copy($scope.client);
+                $location.url("/realms/" + realm.realm + "/clients/" + client.id);
+                Notifications.success("Your changes have been saved to the client.");
+            });
+        }
+    };
+
+    $scope.reset = function() {
+        $route.reload();
+    };
+
+    $scope.cancel = function() {
+        $location.url("/realms/" + realm.realm + "/clients");
+    };
+});
+
+module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
+    $scope.protocols = ['openid-connect',
+        'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort();
+
+    $scope.templates = [ {name:'NONE'}];
+    for (var i = 0; i < templates.length; i++) {
+        var template = templates[i];
+        $scope.templates.push(template);
+    }
+
+    $scope.realm = realm;
+
+    $scope.client = {
+        enabled: true,
+        attributes: {}
+    };
+    $scope.client.redirectUris = [];
+    $scope.protocol = $scope.protocols[0];
+
+
+    $scope.importFile = function(fileContent){
+        console.debug(fileContent);
+        ClientDescriptionConverter.save({
+            realm: realm.realm
+        }, fileContent, function (data) {
+            $scope.client = data;
+            $scope.importing = true;
+        });
+    };
+
+    $scope.viewImportDetails = function() {
+        $modal.open({
+            templateUrl: resourceUrl + '/partials/modal/view-object.html',
+            controller: 'ObjectModalCtrl',
+            resolve: {
+                object: function () {
+                    return $scope.client;
+                }
             }
+        })
+    };
+
+    $scope.switchChange = function() {
+        $scope.changed = true;
+    }
+
+    $scope.changeProtocol = function() {
+        if ($scope.protocol == "openid-connect") {
+            $scope.client.protocol = "openid-connect";
+        } else if ($scope.protocol == "saml") {
+            $scope.client.protocol = "saml";
+        }
+    };
+
+    $scope.$watch(function() {
+        return $location.path();
+    }, function() {
+        $scope.path = $location.path().substring(1).split("/");
+    });
+
+    function isChanged() {
+        if (!angular.equals($scope.client, client)) {
+            return true;
         }
+        return false;
+    }
+
+    $scope.$watch('client', function() {
+        $scope.changed = isChanged();
+    }, true);
+
+
+    $scope.save = function() {
+
+        $scope.client.protocol = $scope.protocol;
+
+        if ($scope.client.protocol == 'openid-connect' && !$scope.client.rootUrl) {
+            Notifications.error("You must specify the root URL of application");
+        }
+
+        if ($scope.client.protocol == 'saml' && !$scope.client.adminUrl) {
+            Notifications.error("You must specify the SAML Endpoint URL");
+        }
+
+        Client.save({
+            realm: realm.realm,
+            client: ''
+        }, $scope.client, function (data, headers) {
+            $scope.changed = false;
+            var l = headers().location;
+            var id = l.substring(l.lastIndexOf("/") + 1);
+            $location.url("/realms/" + realm.realm + "/clients/" + id);
+            Notifications.success("The client has been created.");
+        });
     };
 
     $scope.reset = function() {
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
index 6a2bd48..f697c04 100755
--- a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html
@@ -2,30 +2,15 @@
 
     <ol class="breadcrumb">
         <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
-        <li data-ng-show="create">{{:: 'add-client' | translate}}</li>
-        <li data-ng-hide="create">{{client.clientId}}</li>
+        <li>{{client.clientId}}</li>
     </ol>
 
     <kc-tabs-client></kc-tabs-client>
 
     <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
         <fieldset class="border-top">
-            <div class="form-group" data-ng-show="create">
-                <label for="name" class="col-sm-2 control-label">{{:: 'import' | translate}}</label>
-
-                <div class="col-md-6" data-ng-hide="importing">
-                    <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
-                    <input id="import-file" type="file" class="hidden" kc-on-read-file="importFile($fileContent)">
-                </div>
-
-                <div class="col-md-6" data-ng-show="importing">
-                    <button class="btn btn-default" data-ng-click="viewImportDetails()">{{:: 'view-details' | translate}}</button>
-                    <button class="btn btn-default" data-ng-click="reset()">{{:: 'clear-import' | translate}}</button>
-                </div>
-            </div>
-
             <div class="form-group">
-                <label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}} <span class="required" data-ng-show="create">*</span></label>
+                <label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}}</label>
                 <div class="col-sm-6">
                     <input class="form-control" type="text" id="clientId" name="clientId" data-ng-model="client.clientId" autofocus required>
                 </div>
@@ -250,14 +235,14 @@
                 <kc-tooltip>{{:: 'valid-redirect-uris.tooltip' | translate}}</kc-tooltip>
             </div>
 
-            <div class="form-group" data-ng-show="!client.bearerOnly && !create">
+            <div class="form-group" data-ng-show="!client.bearerOnly">
                 <label class="col-md-2 control-label" for="baseUrl">{{:: 'base-url' | translate}}</label>
                 <div class="col-sm-6">
                     <input class="form-control" type="text" name="baseUrl" id="baseUrl" data-ng-model="client.baseUrl">
                 </div>
                 <kc-tooltip>{{:: 'base-url.tooltip' | translate}}</kc-tooltip>
             </div>
-            <div class="form-group" data-ng-hide="create || protocol == 'saml'">
+            <div class="form-group" data-ng-hide="protocol == 'saml'">
                 <label class="col-md-2 control-label" for="adminUrl">{{:: 'admin-url' | translate}}</label>
                 <div class="col-sm-6">
                     <input class="form-control" type="text" name="adminUrl" id="adminUrl"
@@ -287,7 +272,7 @@
                 </div>
                 <kc-tooltip>{{:: 'idp-sso-relay-state.tooltip' | translate}}</kc-tooltip>
             </div>
-            <div class="form-group" data-ng-show="!client.bearerOnly && !create && protocol == 'openid-connect' && (client.standardFlowEnabled || client.implicitFlowEnabled)">
+            <div class="form-group" data-ng-show="!client.bearerOnly && protocol == 'openid-connect' && (client.standardFlowEnabled || client.implicitFlowEnabled)">
                 <label class="col-md-2 control-label" for="newWebOrigin">{{:: 'web-origins' | translate}}</label>
 
                 <div class="col-sm-6">
@@ -342,11 +327,7 @@
         </fieldset>
 
         <div class="form-group">
-            <div class="col-md-10 col-md-offset-2" data-ng-show="create && access.manageClients">
-                <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
-                <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
-            </div>
-            <div class="col-md-10 col-md-offset-2" data-ng-show="!create && access.manageClients">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
                 <button kc-save  data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
                 <button kc-reset data-ng-disabled="!changed">{{:: 'cancel' | translate}}</button>
             </div>
diff --git a/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-client.html b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-client.html
new file mode 100755
index 0000000..dd2a962
--- /dev/null
+++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-client.html
@@ -0,0 +1,83 @@
+<div class="col-sm-9 col-md-10 col-sm-push-3 col-md-push-2">
+
+    <ol class="breadcrumb">
+        <li><a href="#/realms/{{realm.realm}}/clients">{{:: 'clients' | translate}}</a></li>
+        <li>{{:: 'add-client' | translate}}</li>
+    </ol>
+
+    <kc-tabs-client></kc-tabs-client>
+
+    <form class="form-horizontal" name="clientForm" novalidate kc-read-only="!access.manageClients">
+        <fieldset class="border-top">
+            <div class="form-group">
+                <label class="col-sm-2 control-label">{{:: 'import' | translate}}</label>
+
+                <div class="col-md-6" data-ng-hide="importing">
+                    <label for="import-file" class="btn btn-default">{{:: 'select-file' | translate}} <i class="pficon pficon-import"></i></label>
+                    <input id="import-file" type="file" class="hidden" kc-on-read-file="importFile($fileContent)">
+                </div>
+
+                <div class="col-md-6" data-ng-show="importing">
+                    <button class="btn btn-default" data-ng-click="viewImportDetails()">{{:: 'view-details' | translate}}</button>
+                    <button class="btn btn-default" data-ng-click="reset()">{{:: 'clear-import' | translate}}</button>
+                </div>
+            </div>
+
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="clientId">{{:: 'client-id' | translate}} <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" id="clientId" name="clientId" data-ng-model="client.clientId" autofocus required>
+                </div>
+                <kc-tooltip>{{:: 'client-id.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="protocol">{{:: 'client-protocol' | translate}}</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="protocol"
+                                ng-change="changeProtocol()"
+                                ng-model="protocol"
+                                ng-options="aProtocol for aProtocol in protocols">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>{{:: 'client-protocol.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group">
+                <label class="col-md-2 control-label" for="protocol">Client Template</label>
+                <div class="col-sm-6">
+                    <div>
+                        <select class="form-control" id="template"
+                                ng-model="client.clientTemplate"
+                                ng-options="template.name as template.name for template in templates">
+                        </select>
+                    </div>
+                </div>
+                <kc-tooltip>Client template this client inherits configuration from</kc-tooltip>
+            </div>
+            <div class="form-group" data-ng-hide="protocol == 'saml'">
+                <label class="col-md-2 control-label" for="rootUrl">{{:: 'root-url' | translate}} <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" name="rootUrl" id="rootUrl" data-ng-model="client.rootUrl">
+                </div>
+                <kc-tooltip>{{:: 'root-url.tooltip' | translate}}</kc-tooltip>
+            </div>
+            <div class="form-group" data-ng-show="protocol == 'saml'">
+                <label class="col-md-2 control-label" for="masterSamlUrl">Client SAML Endpoint <span class="required">*</span></label>
+                <div class="col-sm-6">
+                    <input class="form-control" type="text" name="masterSamlUrl" id="masterSamlUrl"
+                           data-ng-model="client.adminUrl">
+                </div>
+                <kc-tooltip>{{:: 'master-saml-processing-url.tooltip' | translate}}</kc-tooltip>
+            </div>
+        </fieldset>
+        <div class="form-group">
+            <div class="col-md-10 col-md-offset-2" data-ng-show="access.manageClients">
+                <button kc-save data-ng-disabled="!changed">{{:: 'save' | translate}}</button>
+                <button kc-cancel data-ng-click="cancel()">{{:: 'cancel' | translate}}</button>
+            </div>
+        </div>
+    </form>
+</div>
+
+<kc-menu></kc-menu>
\ No newline at end of file
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
new file mode 100755
index 0000000..241cc26
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java
@@ -0,0 +1,127 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.saml.SignatureAlgorithm;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlClient {
+    public static final String SAML_SIGNING_PRIVATE_KEY = "saml.signing.private.key";
+    protected ClientModel client;
+
+    public SamlClient(ClientModel client) {
+        this.client = client;
+    }
+
+    public String getCanonicalizationMethod() {
+        return client.getAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+    }
+
+    public void setCanonicalizationMethod(String value) {
+        client.setAttribute(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE, value);
+    }
+
+    public SignatureAlgorithm getSignatureAlgorithm() {
+        String alg = client.getAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM);
+        if (alg != null) {
+            SignatureAlgorithm algorithm = SignatureAlgorithm.valueOf(alg);
+            if (algorithm != null)
+                return algorithm;
+        }
+        return SignatureAlgorithm.RSA_SHA256;
+    }
+
+    public void setSignatureAlgorithm(SignatureAlgorithm algorithm) {
+        client.setAttribute(SamlProtocol.SAML_SIGNATURE_ALGORITHM, algorithm.name());
+    }
+
+    public String getNameIDFormat() {
+        return client.getAttributes().get(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE);
+    }
+    public void setNameIDFormat(String format) {
+        client.setAttribute(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE, format);
+    }
+
+    public boolean includeAuthnStatement() {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_AUTHNSTATEMENT));
+    }
+
+    public void setIncludeAuthnStatement(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_AUTHNSTATEMENT, Boolean.toString(val));
+    }
+
+    public boolean forceNameIDFormat() {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE));
+
+    }
+    public void setForceNameIDFormat(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE, Boolean.toString(val));
+    }
+
+    public boolean requiresRealmSignature(ClientModel client) {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_SERVER_SIGNATURE));
+    }
+
+    public void setRequiresRealmSignature(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_SERVER_SIGNATURE, Boolean.toString(val));
+
+    }
+
+    public boolean forcePostBinding(ClientModel client) {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_FORCE_POST_BINDING));
+    }
+
+    public void setForcePostBinding(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_FORCE_POST_BINDING, Boolean.toString(val));
+
+    }
+    public boolean samlAssertionSignature(ClientModel client) {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE));
+    }
+
+    public void setAssertionSignature(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_ASSERTION_SIGNATURE   , Boolean.toString(val));
+
+    }
+    public boolean requiresEncryption(ClientModel client) {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_ENCRYPT));
+    }
+
+
+    public void setRequiresEncryption(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_ENCRYPT, Boolean.toString(val));
+
+    }
+
+    public boolean requiresClientSignature(ClientModel client) {
+        return "true".equals(client.getAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE));
+    }
+
+    public void setRequiresClientSignature(boolean val) {
+        client.setAttribute(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE   , Boolean.toString(val));
+
+    }
+
+    public String getClientSigningCertificate() {
+        return client.getAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE);
+    }
+
+    public void setClientSigningCertificate(String val) {
+        client.setAttribute(SamlProtocol.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, val);
+
+    }
+
+    public String getClientSigningPrivateKey() {
+        return client.getAttribute(SAML_SIGNING_PRIVATE_KEY);
+    }
+
+    public void setClientSigningPrivateKey(String val) {
+        client.setAttribute(SAML_SIGNING_PRIVATE_KEY, val);
+
+    }
+
+
+
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java
new file mode 100755
index 0000000..4151d62
--- /dev/null
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java
@@ -0,0 +1,60 @@
+package org.keycloak.protocol.saml;
+
+import org.keycloak.models.ClientModel;
+import org.keycloak.representations.idm.ClientRepresentation;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class SamlClientRepresentation {
+    protected ClientRepresentation rep;
+
+    public SamlClientRepresentation(ClientRepresentation rep) {
+        this.rep = rep;
+    }
+
+    public String getCanonicalizationMethod() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_CANONICALIZATION_METHOD_ATTRIBUTE);
+    }
+
+    public String getSignatureAlgorithm() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_SIGNATURE_ALGORITHM);
+    }
+
+    public String getNameIDFormat() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_NAME_ID_FORMAT_ATTRIBUTE);
+
+    }
+
+    public String getIncludeAuthnStatement() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_AUTHNSTATEMENT);
+
+    }
+
+    public String getForceNameIDFormat() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_FORCE_NAME_ID_FORMAT_ATTRIBUTE);
+    }
+
+    public String getSamlServerSignature() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_SERVER_SIGNATURE);
+
+    }
+
+    public String getForcePostBinding() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_FORCE_POST_BINDING);
+
+    }
+    public String getClientSignature() {
+        if (rep.getAttributes() == null) return null;
+        return rep.getAttributes().get(SamlProtocol.SAML_CLIENT_SIGNATURE_ATTRIBUTE);
+
+    }
+}
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
index 7dcc866..f7b4d1e 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
@@ -6,15 +6,20 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
+import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.protocol.AbstractLoginProtocolFactory;
 import org.keycloak.protocol.LoginProtocol;
 import org.keycloak.protocol.saml.mappers.AttributeStatementHelper;
 import org.keycloak.protocol.saml.mappers.RoleListMapper;
 import org.keycloak.protocol.saml.mappers.UserPropertyAttributeStatementMapper;
+import org.keycloak.representations.idm.CertificateRepresentation;
+import org.keycloak.representations.idm.ClientRepresentation;
+import org.keycloak.saml.SignatureAlgorithm;
 import org.keycloak.services.managers.AuthenticationManager;
 import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
 import org.keycloak.saml.processing.core.saml.v2.constants.X500SAMLProfileConstants;
 
+import javax.xml.crypto.dsig.CanonicalizationMethod;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -95,4 +100,47 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
             client.addProtocolMapper(model);
         }
     }
+
+    @Override
+    public void setupClientDefaults(ClientRepresentation clientRep, ClientModel newClient) {
+        SamlClientRepresentation rep = new SamlClientRepresentation(clientRep);
+        SamlClient client = new SamlClient(newClient);
+        if (clientRep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true);
+        if (rep.getCanonicalizationMethod() == null) {
+            client.setCanonicalizationMethod(CanonicalizationMethod.EXCLUSIVE);
+        }
+        if (rep.getSignatureAlgorithm() == null) {
+            client.setSignatureAlgorithm(SignatureAlgorithm.RSA_SHA256);
+        }
+
+        if (rep.getNameIDFormat() == null) {
+            client.setNameIDFormat("username");
+        }
+
+        if (rep.getIncludeAuthnStatement() == null) {
+            client.setIncludeAuthnStatement(true);
+        }
+
+        if (rep.getForceNameIDFormat() == null) {
+            client.setForceNameIDFormat(false);
+        }
+
+        if (rep.getSamlServerSignature() == null) {
+            client.setRequiresRealmSignature(true);
+        }
+        if (rep.getForcePostBinding() == null) {
+            client.setForcePostBinding(true);
+        }
+
+        if (rep.getClientSignature() == null) {
+            client.setRequiresClientSignature(true);
+            CertificateRepresentation info = KeycloakModelUtils.generateKeyPairCertificate(newClient.getClientId());
+            client.setClientSigningCertificate(info.getCertificate());
+            client.setClientSigningPrivateKey(info.getPrivateKey());
+        }
+
+        if (clientRep.isFrontchannelLogout() == null) {
+            newClient.setFrontchannelLogout(true);
+        }
+    }
 }
diff --git a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
index 5742381..a876b19 100755
--- a/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/LoginProtocolFactory.java
@@ -1,9 +1,11 @@
 package org.keycloak.protocol;
 
 import org.keycloak.events.EventBuilder;
+import org.keycloak.models.ClientModel;
 import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.provider.ProviderFactory;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 
 import java.util.List;
@@ -27,4 +29,12 @@ public interface LoginProtocolFactory extends ProviderFactory<LoginProtocol> {
     List<ProtocolMapperModel> getDefaultBuiltinMappers();
 
     Object createProtocolEndpoint(RealmModel realm, EventBuilder event, AuthenticationManager authManager);
+
+    /**
+     * Setup default values for new clients.
+     *
+     * @param rep
+     * @param newClient
+     */
+    void setupClientDefaults(ClientRepresentation rep, ClientModel newClient);
 }
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index b9211ad..8cd0d8f 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -16,7 +16,9 @@
  */
 package org.keycloak.protocol.oidc;
 
+import org.jboss.logging.Logger;
 import org.keycloak.common.constants.KerberosConstants;
+import org.keycloak.common.util.UriUtils;
 import org.keycloak.events.EventBuilder;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
@@ -29,12 +31,16 @@ import org.keycloak.protocol.oidc.mappers.FullNameMapper;
 import org.keycloak.protocol.oidc.mappers.OIDCAttributeMapperHelper;
 import org.keycloak.protocol.oidc.mappers.UserPropertyMapper;
 import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
+import org.keycloak.representations.idm.ClientRepresentation;
 import org.keycloak.services.managers.AuthenticationManager;
 
 import java.util.ArrayList;
 import java.util.HashMap;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
+
 import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
 
 /**
@@ -42,6 +48,7 @@ import org.keycloak.protocol.oidc.mappers.UserAttributeMapper;
  * @version $Revision: 1 $
  */
 public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
+    private static Logger logger = Logger.getLogger(OIDCLoginProtocolFactory.class);
 
     public static final String USERNAME = "username";
     public static final String EMAIL = "email";
@@ -159,4 +166,44 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
     public String getId() {
         return "openid-connect";
     }
+
+    @Override
+    public void setupClientDefaults(ClientRepresentation rep, ClientModel newClient) {
+        if (rep.getRootUrl() != null && (rep.getRedirectUris() == null || rep.getRedirectUris().isEmpty())) {
+            String root = rep.getRootUrl();
+            if (root.endsWith("/")) root = root + "*";
+            else root = root + "/*";
+            newClient.addRedirectUri(root);
+
+            Set<String> origins = new HashSet<String>();
+            String origin = UriUtils.getOrigin(root);
+            logger.debugv("adding default client origin: {0}" , origin);
+            origins.add(origin);
+            newClient.setWebOrigins(origins);
+        }
+        if (rep.isBearerOnly() == null
+                && rep.isPublicClient() == null) {
+            newClient.setPublicClient(true);
+        }
+        if (rep.isBearerOnly() == null) newClient.setBearerOnly(false);
+        if (rep.getAdminUrl() == null && rep.getRootUrl() != null) {
+            newClient.setManagementUrl(rep.getRootUrl());
+        }
+
+
+        // Backwards compatibility only
+        if (rep.isDirectGrantsOnly() != null) {
+            logger.warn("Using deprecated 'directGrantsOnly' configuration in JSON representation. It will be removed in future versions");
+            newClient.setStandardFlowEnabled(!rep.isDirectGrantsOnly());
+            newClient.setDirectAccessGrantsEnabled(rep.isDirectGrantsOnly());
+        } else {
+            if (rep.isStandardFlowEnabled() == null) newClient.setStandardFlowEnabled(true);
+            if (rep.isDirectAccessGrantsEnabled() == null) newClient.setDirectAccessGrantsEnabled(true);
+
+        }
+
+        if (rep.isImplicitFlowEnabled() == null) newClient.setImplicitFlowEnabled(false);
+        if (rep.isPublicClient() == null) newClient.setPublicClient(true);
+        if (rep.isFrontchannelLogout() == null) newClient.setFrontchannelLogout(false);
+    }
 }
diff --git a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
index 0666fab..d011089 100755
--- a/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
+++ b/services/src/main/java/org/keycloak/services/clientregistration/AbstractClientRegistrationProvider.java
@@ -34,7 +34,7 @@ public abstract class AbstractClientRegistrationProvider implements ClientRegist
         auth.requireCreate();
 
         try {
-            ClientModel clientModel = ClientManager.createClient(session, session.getContext().getRealm(), client, true);
+            ClientModel clientModel = RepresentationToModel.createClient(session, session.getContext().getRealm(), client, true);
             if (client.getClientId() == null) {
                 clientModel.setClientId(clientModel.getId());
             }
diff --git a/services/src/main/java/org/keycloak/services/managers/ClientManager.java b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
index 358860f..e823ac2 100755
--- a/services/src/main/java/org/keycloak/services/managers/ClientManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/ClientManager.java
@@ -15,6 +15,8 @@ import org.keycloak.models.UserSessionProvider;
 import org.keycloak.models.session.UserSessionPersisterProvider;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.models.utils.RepresentationToModel;
+import org.keycloak.protocol.LoginProtocol;
+import org.keycloak.protocol.LoginProtocolFactory;
 import org.keycloak.protocol.oidc.OIDCLoginProtocol;
 import org.keycloak.protocol.oidc.mappers.UserSessionNoteMapper;
 import org.keycloak.representations.adapters.config.BaseRealmConfig;
@@ -45,10 +47,25 @@ public class ClientManager {
     public ClientManager() {
     }
 
+    /**
+     * Should not be called from an import.  This really expects that the client is created from the admin console.
+     *
+     * @param session
+     * @param realm
+     * @param rep
+     * @param addDefaultRoles
+     * @return
+     */
     public static ClientModel createClient(KeycloakSession session, RealmModel realm, ClientRepresentation rep, boolean addDefaultRoles) {
         ClientModel client = RepresentationToModel.createClient(session, realm, rep, addDefaultRoles);
 
-        // remove default mappers
+        if (rep.getProtocol() != null) {
+            LoginProtocolFactory providerFactory = (LoginProtocolFactory) session.getKeycloakSessionFactory().getProviderFactory(LoginProtocol.class, rep.getProtocol());
+            providerFactory.setupClientDefaults(rep, client);
+        }
+
+
+        // remove default mappers if there is a template
         if (rep.getProtocolMappers() == null && rep.getClientTemplate() != null) {
             Set<ProtocolMapperModel> mappers = client.getProtocolMappers();
             for (ProtocolMapperModel mapper : mappers) client.removeProtocolMapper(mapper);
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
index 155bdf1..f4ef632 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java
@@ -271,6 +271,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
                 }
 
             }, 10, 500);
+            Thread.sleep(100);
         } catch (InterruptedException ie) {
             Thread.currentThread().interrupt();
         }