From b90409c5e4adcd32771e1097896e2d94cfa97ce4 Mon Sep 17 00:00:00 2001 From: Bill Burke Date: Mon, 21 Dec 2015 16:36:13 -0500 Subject: [PATCH] refactor client create --- .../theme/base/admin/resources/js/app.js | 4 +- .../admin/resources/js/controllers/clients.js | 154 +++++++++++++----- .../resources/partials/client-detail.html | 31 +--- .../resources/partials/create-client.html | 83 ++++++++++ .../keycloak/protocol/saml/SamlClient.java | 127 +++++++++++++++ .../saml/SamlClientRepresentation.java | 60 +++++++ .../protocol/saml/SamlProtocolFactory.java | 48 ++++++ .../protocol/LoginProtocolFactory.java | 10 ++ .../oidc/OIDCLoginProtocolFactory.java | 47 ++++++ .../AbstractClientRegistrationProvider.java | 2 +- .../services/managers/ClientManager.java | 19 ++- .../testsuite/rule/AbstractKeycloakRule.java | 1 + 12 files changed, 519 insertions(+), 67 deletions(-) create mode 100755 forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-client.html create mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClient.java create mode 100755 saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlClientRepresentation.java 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 5a2c7470ee..e5e1bb919b 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 ec28ef45b0..14243077af 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,28 +1041,15 @@ 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."); + }); } }; @@ -1089,6 +1062,111 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates, }; }); +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() { + $route.reload(); + }; + + $scope.cancel = function() { + $location.url("/realms/" + realm.realm + "/clients"); + }; +}); + module.controller('ClientScopeMappingCtrl', function($scope, $http, realm, client, clients, templates, Notifications, Client, ClientTemplate, ClientRealmScopeMapping, ClientClientScopeMapping, ClientRole, 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 6a2bd481df..f697c048e8 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 @@
-
- - -
- - -
- -
- - -
-
-
- +
@@ -250,14 +235,14 @@ {{:: 'valid-redirect-uris.tooltip' | translate}}
-
+
{{:: 'base-url.tooltip' | translate}}
-
+
{{:: 'idp-sso-relay-state.tooltip' | translate}}
-
+
@@ -342,11 +327,7 @@
-
- - -
-
+
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 0000000000..dd2a9626b6 --- /dev/null +++ b/forms/common-themes/src/main/resources/theme/base/admin/resources/partials/create-client.html @@ -0,0 +1,83 @@ +
+ + + + + + +
+
+ + +
+ + +
+ +
+ + +
+
+ +
+ +
+ +
+ {{:: 'client-id.tooltip' | translate}} +
+
+ +
+
+ +
+
+ {{:: 'client-protocol.tooltip' | translate}} +
+
+ +
+
+ +
+
+ Client template this client inherits configuration from +
+
+ +
+ +
+ {{:: 'root-url.tooltip' | translate}} +
+
+ +
+ +
+ {{:: 'master-saml-processing-url.tooltip' | translate}} +
+
+
+
+ + +
+
+ +
+ + \ 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 0000000000..241cc268f8 --- /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 Bill Burke + * @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 0000000000..4151d625a7 --- /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 Bill Burke + * @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 7dcc86695b..f7b4d1e97b 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 5742381cc3..a876b194b5 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 { List 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 b9211ad79f..8cd0d8fed5 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 origins = new HashSet(); + 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 0666fab32c..d011089113 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 358860f027..e823ac29f3 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 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 155bdf117f..f4ef63272a 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(); }