refactor client create

This commit is contained in:
Bill Burke 2015-12-21 16:36:13 -05:00
parent 4a492ce13c
commit b90409c5e4
12 changed files with 519 additions and 67 deletions

View file

@ -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',

View file

@ -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,

View file

@ -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>

View file

@ -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>

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}

View file

@ -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);
}
}

View file

@ -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());
}

View file

@ -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);

View file

@ -271,6 +271,7 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
}
}, 10, 500);
Thread.sleep(100);
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}