saml broker import/export, and module fixes

This commit is contained in:
Bill Burke 2015-03-17 19:20:46 -04:00
parent 13b22d6644
commit ce2c4188fb
10 changed files with 129 additions and 29 deletions

View file

@ -301,20 +301,20 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(); authnBinding = JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get();
} }
String assertionConsumerService = uriInfo.getBaseUriBuilder().path("realms").path(realm.getName()).path("broker").path(getConfig().getProviderId()).build().toString(); String assertionConsumerService = uriInfo.getBaseUriBuilder().path("realms").path(realm.getName()).path("broker").path(getConfig().getId()).build().toString();
String descriptor = String descriptor =
"<EntityDescriptor entityID=\"" + getEntityId(uriInfo, realm) + "\n" + "<EntityDescriptor entityID=\"" + getEntityId(uriInfo, realm) + "\">\n" +
" <SPSSODescriptor AuthnRequestsSigned=\"" + getConfig().isWantAuthnRequestsSigned() + "\n" + " <SPSSODescriptor AuthnRequestsSigned=\"" + getConfig().isWantAuthnRequestsSigned() + "\"\n" +
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" + " protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol urn:oasis:names:tc:SAML:1.1:protocol http://schemas.xmlsoap.org/ws/2003/07/secext\">\n" +
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" + " <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
" </NameIDFormat>\n" + " </NameIDFormat>\n" +
// todo single logout service description // todo single logout service description
// " <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8081/sales-metadata/\"/>\n" + // " <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\" Location=\"http://localhost:8081/sales-metadata/\"/>\n" +
" <AssertionConsumerService\n" + " <AssertionConsumerService\n" +
" Binding=\"" + authnBinding + "\" Location=\"" + assertionConsumerService + "\n" + " Binding=\"" + authnBinding + "\" Location=\"" + assertionConsumerService + "\"\n" +
" index=\"1\" isDefault=\"true\" />\n"; " index=\"1\" isDefault=\"true\" />\n";
if (getConfig().isWantAuthnRequestsSigned()) { if (getConfig().isWantAuthnRequestsSigned()) {
descriptor += descriptor +=

View file

@ -19,9 +19,12 @@ package org.keycloak.broker.saml;
import org.keycloak.broker.provider.AbstractIdentityProviderFactory; import org.keycloak.broker.provider.AbstractIdentityProviderFactory;
import org.keycloak.models.IdentityProviderModel; import org.keycloak.models.IdentityProviderModel;
import org.picketlink.common.constants.JBossSAMLConstants;
import org.picketlink.common.constants.JBossSAMLURIConstants;
import org.picketlink.common.exceptions.ParsingException; import org.picketlink.common.exceptions.ParsingException;
import org.picketlink.common.util.DocumentUtil; import org.picketlink.common.util.DocumentUtil;
import org.picketlink.identity.federation.core.parsers.saml.SAMLParser; import org.picketlink.identity.federation.core.parsers.saml.SAMLParser;
import org.picketlink.identity.federation.saml.v2.metadata.EndpointType;
import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.EntitiesDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.EntityDescriptorType;
import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType; import org.picketlink.identity.federation.saml.v2.metadata.IDPSSODescriptorType;
@ -53,7 +56,7 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
} }
@Override @Override
public Map<String, String> parseConfig(InputStream inputStream) { public Map<String, String> parseConfig(InputStream inputStream) {
try { try {
Object parsedObject = new SAMLParser().parse(inputStream); Object parsedObject = new SAMLParser().parse(inputStream);
EntityDescriptorType entityType; EntityDescriptorType entityType;
@ -76,11 +79,22 @@ public class SAMLIdentityProviderFactory extends AbstractIdentityProviderFactory
if (idpDescriptor != null) { if (idpDescriptor != null) {
SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig(); SAMLIdentityProviderConfig samlIdentityProviderConfig = new SAMLIdentityProviderConfig();
String singleSignOnServiceUrl = null;
samlIdentityProviderConfig.setSingleSignOnServiceUrl(idpDescriptor.getSingleSignOnService().get(0).getLocation().toString()); boolean postBinding = false;
for (EndpointType endpoint : idpDescriptor.getSingleSignOnService()) {
if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get())) {
singleSignOnServiceUrl = endpoint.getLocation().toString();
postBinding = true;
break;
} else if (endpoint.getBinding().toString().equals(JBossSAMLURIConstants.SAML_HTTP_REDIRECT_BINDING.get())){
singleSignOnServiceUrl = endpoint.getLocation().toString();
}
}
samlIdentityProviderConfig.setSingleSignOnServiceUrl(singleSignOnServiceUrl);
samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned()); samlIdentityProviderConfig.setWantAuthnRequestsSigned(idpDescriptor.isWantAuthnRequestsSigned());
samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned()); samlIdentityProviderConfig.setValidateSignature(idpDescriptor.isWantAuthnRequestsSigned());
samlIdentityProviderConfig.setPostBindingResponse(true); samlIdentityProviderConfig.setPostBindingResponse(postBinding);
samlIdentityProviderConfig.setPostBindingAuthnRequest(postBinding);
List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor(); List<KeyDescriptorType> keyDescriptor = idpDescriptor.getKeyDescriptor();
String defaultCertificate = null; String defaultCertificate = null;

View file

@ -5,9 +5,14 @@
<!-- Insert resources here --> <!-- Insert resources here -->
</resources> </resources>
<dependencies> <dependencies>
<module name="javax.api" />
<module name="org.keycloak.keycloak-core"/> <module name="org.keycloak.keycloak-core"/>
<module name="org.keycloak.keycloak-model-api"/> <module name="org.keycloak.keycloak-model-api"/>
<module name="org.keycloak.keycloak-broker-core"/> <module name="org.keycloak.keycloak-broker-core"/>
<module name="org.keycloak.keycloak-saml-protocol"/>
<module name="org.picketlink.common"/> <module name="org.picketlink.common"/>
<module name="org.picketlink.federation"/>
<module name="javax.ws.rs.api"/>
<module name="org.jboss.resteasy.resteasy-jaxrs"/>
</dependencies> </dependencies>
</module> </module>

View file

@ -638,7 +638,7 @@ module.controller('RealmDefaultRolesCtrl', function ($scope, Realm, realm, appli
}); });
module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications) { module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload, $http, realm, instance, providerFactory, IdentityProvider, serverInfo, $location, Notifications) {
console.log('RealmIdentityProviderCtrl'); console.log('RealmIdentityProviderCtrl');
$scope.realm = angular.copy(realm); $scope.realm = angular.copy(realm);
@ -678,6 +678,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
$scope.files = []; $scope.files = [];
$scope.importFile = false; $scope.importFile = false;
$scope.importUrl = false;
$scope.onFileSelect = function($files) { $scope.onFileSelect = function($files) {
$scope.importFile = true; $scope.importFile = true;
@ -685,6 +686,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
}; };
$scope.clearFileSelect = function() { $scope.clearFileSelect = function() {
$scope.importUrl = false;
$scope.importFile = false; $scope.importFile = false;
$scope.files = null; $scope.files = null;
} }
@ -694,7 +696,7 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
for (var i = 0; i < $scope.files.length; i++) { for (var i = 0; i < $scope.files.length; i++) {
var $file = $scope.files[i]; var $file = $scope.files[i];
$scope.upload = $upload.upload({ $scope.upload = $upload.upload({
url: authUrl + '/admin/realms/' + realm.realm + '/identity-provider/', url: authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import',
// method: POST or PUT, // method: POST or PUT,
// headers: {'headerKey': 'headerValue'}, withCredential: true, // headers: {'headerKey': 'headerValue'}, withCredential: true,
data: $scope.identityProvider, data: $scope.identityProvider,
@ -714,6 +716,24 @@ module.controller('RealmIdentityProviderCtrl', function($scope, $filter, $upload
} }
}; };
$scope.importFrom = function() {
$scope.identityProvider.fromUrl = $scope.fromUrl;
$http.post(authUrl + '/admin/realms/' + realm.realm + '/identity-provider/import', $scope.identityProvider)
.success(function(data, status, headers) {
$location.url("/realms/" + realm.realm + "/identity-provider-settings");
Notifications.success("The " + $scope.identityProvider.name + " provider has been created.");
}).error(function() {
Notifications.error("The provider can not be imported. Please verify the url.");
});
};
$scope.$watch('fromUrl', function(newVal, oldVal){
if ($scope.fromUrl && $scope.fromUrl.length > 0) {
$scope.importUrl = true;
} else{
$scope.importUrl = false;
}
});
$scope.$watch('configuredProviders', function(configuredProviders) { $scope.$watch('configuredProviders', function(configuredProviders) {
if (configuredProviders) { if (configuredProviders) {
$scope.configuredProviders = angular.copy(configuredProviders); $scope.configuredProviders = angular.copy(configuredProviders);

View file

@ -1102,7 +1102,7 @@ module.factory('PasswordPolicy', function() {
}); });
module.factory('IdentityProvider', function($resource) { module.factory('IdentityProvider', function($resource) {
return $resource(authUrl + '/admin/realms/:realm/identity-provider/:id', { return $resource(authUrl + '/admin/realms/:realm/identity-provider/instances/:id', {
realm : '@realm' realm : '@realm'
}, { }, {
create : { create : {
@ -1118,7 +1118,7 @@ module.factory('IdentityProvider', function($resource) {
}); });
module.factory('IdentityProviderExport', function($resource) { module.factory('IdentityProviderExport', function($resource) {
var url = authUrl + '/admin/realms/:realm/identity-provider/:id/export'; var url = authUrl + '/admin/realms/:realm/identity-provider/instances/:id/export';
return { return {
url : function(parameters) url : function(parameters)
{ {

View file

@ -25,8 +25,15 @@
</div> </div>
<span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="The friendly name for this identity provider." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="newIdentityProvider"> <div class="form-group" data-ng-show="newIdentityProvider && !importFile">
<label class="col-sm-2 control-label">Import IdP SAML Metadata </label> <label class="col-sm-2 control-label" for="fromUrl">Import From Url</label>
<div class="col-sm-4">
<input class="form-control" id="fromUrl" type="text" ng-model="fromUrl">
</div>
<span tooltip-placement="right" tooltip="Import metadata from a remote IDP SAML entity descriptor." class="fa fa-info-circle"></span>
</div>
<div class="form-group" data-ng-show="newIdentityProvider && !importUrl">
<label class="col-sm-2 control-label">Import From File</label>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0"> <div class="controls kc-button-input-file" data-ng-show="!files || files.length == 0">
<a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a> <a href="#" class="btn btn-default"><span class="kc-icon-upload">Icon: Upload</span>Choose a File...</a>
@ -37,14 +44,14 @@
</span> </span>
</div> </div>
</div> </div>
<div class="form-group clearfix" data-ng-show="!importFile"> <div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="singleSignOnServiceUrl">Single Sign-On Service Url<span class="required">*</span></label> <label class="col-sm-2 control-label" for="singleSignOnServiceUrl">Single Sign-On Service Url<span class="required">*</span></label>
<div class="col-sm-4"> <div class="col-sm-4">
<input class="form-control" id="singleSignOnServiceUrl" type="text" ng-model="identityProvider.config.singleSignOnServiceUrl" required> <input class="form-control" id="singleSignOnServiceUrl" type="text" ng-model="identityProvider.config.singleSignOnServiceUrl" required>
</div> </div>
<span tooltip-placement="right" tooltip="The Url that must be used to send authentication requests(SAML AuthnRequest)." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="The Url that must be used to send authentication requests(SAML AuthnRequest)." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group clearfix" data-ng-show="!importFile"> <div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label> <label class="col-sm-2 control-label" for="nameIDPolicyFormat">NameID Policy Format</label>
<div class="col-sm-4"> <div class="col-sm-4">
<select id="nameIDPolicyFormat" ng-model="identityProvider.config.nameIDPolicyFormat"> <select id="nameIDPolicyFormat" ng-model="identityProvider.config.nameIDPolicyFormat">
@ -60,42 +67,42 @@
</div> </div>
<span tooltip-placement="right" tooltip="Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Specifies the URI reference corresponding to a name identifier format. Defaults to urn:oasis:names:tc:SAML:2.0:nameid-format:persistent." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group clearfix" data-ng-show="!importFile"> <div class="form-group clearfix" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="signingCertificate">Validating X509 Certificate</label> <label class="col-sm-2 control-label" for="signingCertificate">Validating X509 Certificate</label>
<div class="col-sm-4"> <div class="col-sm-4">
<textarea class="form-control" id="signingCertificate" ng-model="identityProvider.config.signingCertificate"/> <textarea class="form-control" id="signingCertificate" ng-model="identityProvider.config.signingCertificate"/>
</div> </div>
<span tooltip-placement="right" tooltip="The certificate in PEM format that must be used to check for signatures." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="The certificate in PEM format that must be used to check for signatures." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="!importFile"> <div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label> <label class="col-sm-2 control-label" for="wantAuthnRequestsSigned">Want AuthnRequests Signed</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" onoffswitch /> <input ng-model="identityProvider.config.wantAuthnRequestsSigned" id="wantAuthnRequestsSigned" onoffswitch />
</div> </div>
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider expects signed a AuthnRequest." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip=" Indicates whether the identity provider expects signed a AuthnRequest." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="!importFile"> <div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label> <label class="col-sm-2 control-label" for="forceAuthn">Force Authentication</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" onoffswitch /> <input ng-model="identityProvider.config.forceAuthn" id="forceAuthn" onoffswitch />
</div> </div>
<span tooltip-placement="right" tooltip=" Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip=" Indicates whether the identity provider must authenticate the presenter directly rather than rely on a previous security context." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="!importFile"> <div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label> <label class="col-sm-2 control-label" for="validateSignature">Validate Signature</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitch /> <input ng-model="identityProvider.config.validateSignature" id="validateSignature" onoffswitch />
</div> </div>
<span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Enable/disable signature validation of SAML responses." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="!importFile"> <div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label> <label class="col-sm-2 control-label" for="postBindingResponse">HTTP-POST Binding Response</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitch /> <input ng-model="identityProvider.config.postBindingResponse" id="postBindingResponse" onoffswitch />
</div> </div>
<span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span> <span tooltip-placement="right" tooltip="Indicates whether the identity provider must respond to the AuthnRequest using HTTP-POST binding. If false, HTTP-REDIRECT binding will be used." class="fa fa-info-circle"></span>
</div> </div>
<div class="form-group" data-ng-show="!importFile"> <div class="form-group" data-ng-show="!importFile && !importUrl">
<label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label> <label class="col-sm-2 control-label" for="postBindingAuthnRequest">HTTP-POST Binding for AuthnRequest</label>
<div class="col-sm-4"> <div class="col-sm-4">
<input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitch /> <input ng-model="identityProvider.config.postBindingAuthnRequest" id="postBindingAuthnRequest" onoffswitch />
@ -134,9 +141,10 @@
<div class="pull-right form-actions"> <div class="pull-right form-actions">
<a class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.id}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a> <a class="btn btn-lg btn-primary" href="#/realms/{{realm.realm}}/identity-provider-settings/provider/{{identityProvider.providerId}}/{{identityProvider.id}}/export" data-ng-show="!importFile && !newIdentityProvider">Export</a>
<button kc-save data-ng-show="!importFile">Save</button> <button kc-save data-ng-show="!importFile && !importUrl">Save</button>
<button type="submit" data-ng-click="clearFileSelect()" data-ng-show="importFile" class="btn btn-lg btn-default">Cancel</button> <button type="submit" data-ng-click="clearFileSelect()" data-ng-show="importFile || importUrl" class="btn btn-lg btn-default">Cancel</button>
<button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-lg btn-primary">Import from SAML Metadata</button> <button type="submit" data-ng-click="uploadFile()" data-ng-show="importFile" class="btn btn-lg btn-primary">Import</button>
<button type="submit" data-ng-click="importFrom()" data-ng-show="importUrl" class="btn btn-lg btn-primary">Import</button>
<button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button> <button kc-delete data-ng-click="remove()" data-ng-show="!newIdentityProvider">Delete</button>
</div> </div>
</form> </form>

View file

@ -6,8 +6,13 @@ import javax.ws.rs.Consumes;
import javax.ws.rs.DELETE; import javax.ws.rs.DELETE;
import javax.ws.rs.GET; import javax.ws.rs.GET;
import javax.ws.rs.PUT; import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces; import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriInfo;
/** /**
* @author pedroigor * @author pedroigor
@ -24,4 +29,9 @@ public interface IdentityProviderResource {
@DELETE @DELETE
void remove(); void remove();
@GET
@Path("export")
public Response export(@QueryParam("format") String format);
} }

View file

@ -16,7 +16,7 @@ import java.util.List;
*/ */
public interface IdentityProvidersResource { public interface IdentityProvidersResource {
@Path("{id}") @Path("instances/{id}")
IdentityProviderResource get(@PathParam("id") String id); IdentityProviderResource get(@PathParam("id") String id);
@GET @GET

View file

@ -1,6 +1,7 @@
package org.keycloak.services.resources.admin; package org.keycloak.services.resources.admin;
import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.annotations.cache.NoCache;
import org.jboss.resteasy.client.core.executors.ApacheHttpClient4Executor;
import org.jboss.resteasy.plugins.providers.multipart.InputPart; import org.jboss.resteasy.plugins.providers.multipart.InputPart;
import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput; import org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataInput;
import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.NotFoundException;
@ -17,6 +18,7 @@ import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.RepresentationToModel; import org.keycloak.models.utils.RepresentationToModel;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.representations.idm.IdentityProviderRepresentation; import org.keycloak.representations.idm.IdentityProviderRepresentation;
import org.keycloak.services.managers.ResourceAdminManager;
import org.keycloak.services.resources.flows.Flows; import org.keycloak.services.resources.flows.Flows;
import org.keycloak.social.SocialIdentityProvider; import org.keycloak.social.SocialIdentityProvider;
@ -102,8 +104,9 @@ public class IdentityProvidersResource {
} }
@POST @POST
@Path("import")
@Consumes(MediaType.MULTIPART_FORM_DATA) @Consumes(MediaType.MULTIPART_FORM_DATA)
public Response createWithFile(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException { public Response importFrom(@Context UriInfo uriInfo, MultipartFormDataInput input) throws IOException {
this.auth.requireManage(); this.auth.requireManage();
Map<String, List<InputPart>> formDataMap = input.getFormDataMap(); Map<String, List<InputPart>> formDataMap = input.getFormDataMap();
@ -135,7 +138,47 @@ public class IdentityProvidersResource {
return create(uriInfo, representation); return create(uriInfo, representation);
} }
@Path("{id}") @POST
@Path("import")
@Consumes(MediaType.APPLICATION_JSON)
public Response importFrom(@Context UriInfo uriInfo, Map<String, Object> data) throws IOException {
this.auth.requireManage();
String id = data.get("id").toString();
String name = data.get("name").toString();
String providerId = data.get("providerId").toString();
String enabled = data.get("enabled").toString();
String updateProfileFirstLogin = data.get("updateProfileFirstLogin").toString();
String storeToken = "false";
if (data.containsKey("storeToken")) {
storeToken = data.get("storeToken").toString();
}
String from = data.get("fromUrl").toString();
ApacheHttpClient4Executor executor = ResourceAdminManager.createExecutor();
InputStream inputStream = null;
try {
inputStream = executor.createRequest(from).getTarget(InputStream.class);
} catch (Exception e) {
throw new RuntimeException(e);
}
IdentityProviderFactory providerFactory = getProviderFactorytById(providerId);
Map config = providerFactory.parseConfig(inputStream);
IdentityProviderRepresentation representation = new IdentityProviderRepresentation();
representation.setId(id);
representation.setName(name);
representation.setProviderId(providerId);
representation.setEnabled(Boolean.valueOf(enabled));
representation.setUpdateProfileFirstLogin(Boolean.valueOf(updateProfileFirstLogin));
representation.setStoreToken(Boolean.valueOf(storeToken));
representation.setConfig(config);
return create(uriInfo, representation);
}
@Path("instances/{id}")
public IdentityProviderResource getIdentityProvider(@PathParam("id") String providerId) { public IdentityProviderResource getIdentityProvider(@PathParam("id") String providerId) {
this.auth.requireView(); this.auth.requireView();
IdentityProviderModel identityProviderModel = null; IdentityProviderModel identityProviderModel = null;

View file

@ -157,7 +157,7 @@ public class AccountTest {
}); });
} }
//@Test @Ignore @Test @Ignore
public void runit() throws Exception { public void runit() throws Exception {
Thread.sleep(10000000); Thread.sleep(10000000);
} }