Merge pull request #2034 from patriot1burke/master
saml client installation formats
This commit is contained in:
commit
af554a95eb
12 changed files with 433 additions and 50 deletions
|
@ -38,6 +38,7 @@ import org.keycloak.protocol.saml.JaxrsSAML2BindingBuilder;
|
|||
import org.keycloak.saml.SAML2AuthnRequestBuilder;
|
||||
import org.keycloak.saml.SAML2LogoutRequestBuilder;
|
||||
import org.keycloak.saml.SAML2NameIDPolicyBuilder;
|
||||
import org.keycloak.saml.SPMetadataDescriptor;
|
||||
import org.keycloak.saml.SignatureAlgorithm;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
|
@ -227,31 +228,11 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider<SAMLIdentityP
|
|||
.build().toString();
|
||||
|
||||
|
||||
|
||||
String descriptor =
|
||||
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + getEntityId(uriInfo, realm) + "\">\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" +
|
||||
" <NameIDFormat>" + getConfig().getNameIDPolicyFormat() + "\n" +
|
||||
" </NameIDFormat>\n" +
|
||||
" <SingleLogoutService Binding=\"" + authnBinding + "\" Location=\"" + endpoint + "\"/>\n" +
|
||||
" <AssertionConsumerService\n" +
|
||||
" Binding=\"" + authnBinding + "\" Location=\"" + endpoint + "\"\n" +
|
||||
" index=\"1\" isDefault=\"true\" />\n";
|
||||
if (getConfig().isWantAuthnRequestsSigned()) {
|
||||
descriptor +=
|
||||
" <KeyDescriptor use=\"signing\">\n" +
|
||||
" <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
|
||||
" <dsig:X509Data>\n" +
|
||||
" <dsig:X509Certificate>\n" + realm.getCertificatePem() + "\n" +
|
||||
" </dsig:X509Certificate>\n" +
|
||||
" </dsig:X509Data>\n" +
|
||||
" </dsig:KeyInfo>\n" +
|
||||
" </KeyDescriptor>\n";
|
||||
}
|
||||
descriptor +=
|
||||
" </SPSSODescriptor>\n" +
|
||||
"</EntityDescriptor>\n";
|
||||
boolean wantAuthnRequestsSigned = getConfig().isWantAuthnRequestsSigned();
|
||||
String entityId = getEntityId(uriInfo, realm);
|
||||
String nameIDPolicyFormat = getConfig().getNameIDPolicyFormat();
|
||||
String certificatePem = realm.getCertificatePem();
|
||||
String descriptor = SPMetadataDescriptor.getSPDescriptor(authnBinding, endpoint, endpoint, wantAuthnRequestsSigned, entityId, nameIDPolicyFormat, certificatePem);
|
||||
return Response.ok(descriptor, MediaType.APPLICATION_XML_TYPE).build();
|
||||
}
|
||||
|
||||
|
|
|
@ -700,6 +700,18 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
|
|||
|
||||
$scope.changeFormat = function() {
|
||||
var url = ClientInstallation.url({ realm: $routeParams.realm, client: $routeParams.client, provider: $scope.configFormat.id });
|
||||
if ($scope.configFormat.mediaType == 'application/zip') {
|
||||
$http({
|
||||
url: url,
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
cache: false
|
||||
}).success(function(data) {
|
||||
var installation = data;
|
||||
$scope.installation = installation;
|
||||
}
|
||||
);
|
||||
} else {
|
||||
$http.get(url).success(function (data) {
|
||||
var installation = data;
|
||||
if ($scope.configFormat.mediaType == 'application/json') {
|
||||
|
@ -707,7 +719,9 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
|
|||
installation = angular.toJson(installation, true);
|
||||
}
|
||||
$scope.installation = installation;
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
};
|
||||
$scope.download = function() {
|
||||
saveAs(new Blob([$scope.installation], { type: $scope.configFormat.mediaType }), $scope.configFormat.filename);
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div class="form-group" ng-show="installation">
|
||||
<div class="col-sm-12">
|
||||
<a class="btn btn-primary btn-lg" data-ng-click="download()" type="submit" ng-show="installation">{{:: 'download' | translate}}</a>
|
||||
<textarea class="form-control" rows="20" kc-select-action="click">{{installation}}</textarea>
|
||||
<textarea class="form-control" rows="20" kc-select-action="click" data-ng-hide="configFormat.downloadOnly">{{installation}}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
|
|
35
saml/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
Executable file
35
saml/saml-core/src/main/java/org/keycloak/saml/SPMetadataDescriptor.java
Executable file
|
@ -0,0 +1,35 @@
|
|||
package org.keycloak.saml;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SPMetadataDescriptor {
|
||||
public static String getSPDescriptor(String binding, String assertionEndpoint, String logoutEndpoint, boolean wantAuthnRequestsSigned, String entityId, String nameIDPolicyFormat, String certificatePem) {
|
||||
String descriptor =
|
||||
"<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" entityID=\"" + entityId + "\">\n" +
|
||||
" <SPSSODescriptor AuthnRequestsSigned=\"" + wantAuthnRequestsSigned + "\"\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>" + nameIDPolicyFormat + "\n" +
|
||||
" </NameIDFormat>\n" +
|
||||
" <SingleLogoutService Binding=\"" + binding + "\" Location=\"" + logoutEndpoint + "\"/>\n" +
|
||||
" <AssertionConsumerService\n" +
|
||||
" Binding=\"" + binding + "\" Location=\"" + assertionEndpoint + "\"\n" +
|
||||
" index=\"1\" isDefault=\"true\" />\n";
|
||||
if (wantAuthnRequestsSigned) {
|
||||
descriptor +=
|
||||
" <KeyDescriptor use=\"signing\">\n" +
|
||||
" <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
|
||||
" <dsig:X509Data>\n" +
|
||||
" <dsig:X509Certificate>\n" + certificatePem + "\n" +
|
||||
" </dsig:X509Certificate>\n" +
|
||||
" </dsig:X509Data>\n" +
|
||||
" </dsig:KeyInfo>\n" +
|
||||
" </KeyDescriptor>\n";
|
||||
}
|
||||
descriptor +=
|
||||
" </SPSSODescriptor>\n" +
|
||||
"</EntityDescriptor>\n";
|
||||
return descriptor;
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package org.keycloak.protocol.saml;
|
|||
import org.keycloak.models.ClientConfigResolver;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.saml.SignatureAlgorithm;
|
||||
import org.keycloak.saml.common.constants.GeneralConstants;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
|
@ -37,7 +39,24 @@ public class SamlClient extends ClientConfigResolver {
|
|||
}
|
||||
|
||||
public String getNameIDFormat() {
|
||||
return resolveAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE);
|
||||
String nameIdFormat = null;
|
||||
|
||||
String configuredNameIdFormat = resolveAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE);
|
||||
if (configuredNameIdFormat != null) {
|
||||
if (configuredNameIdFormat.equals("email")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get();
|
||||
} else if (configuredNameIdFormat.equals("persistent")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
|
||||
} else if (configuredNameIdFormat.equals("transient")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get();
|
||||
} else if (configuredNameIdFormat.equals("username")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
|
||||
} else {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
|
||||
}
|
||||
}
|
||||
return nameIdFormat;
|
||||
|
||||
}
|
||||
public void setNameIDFormat(String format) {
|
||||
client.setAttribute(SamlConfigAttributes.SAML_NAME_ID_FORMAT_ATTRIBUTE, format);
|
||||
|
|
|
@ -251,17 +251,7 @@ public class SamlProtocol implements LoginProtocol {
|
|||
boolean forceFormat = samlClient.forceNameIDFormat();
|
||||
String configuredNameIdFormat = samlClient.getNameIDFormat();
|
||||
if ((nameIdFormat == null || forceFormat) && configuredNameIdFormat != null) {
|
||||
if (configuredNameIdFormat.equals("email")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_EMAIL.get();
|
||||
} else if (configuredNameIdFormat.equals("persistent")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_PERSISTENT.get();
|
||||
} else if (configuredNameIdFormat.equals("transient")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_TRANSIENT.get();
|
||||
} else if (configuredNameIdFormat.equals("username")) {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
|
||||
} else {
|
||||
nameIdFormat = JBossSAMLURIConstants.NAMEID_FORMAT_UNSPECIFIED.get();
|
||||
}
|
||||
nameIdFormat = configuredNameIdFormat;
|
||||
}
|
||||
if (nameIdFormat == null)
|
||||
return SAML_DEFAULT_NAMEID_FORMAT;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package org.keycloak.protocol.saml;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.security.PublicKey;
|
||||
|
@ -14,6 +15,7 @@ import javax.ws.rs.Produces;
|
|||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
|
@ -477,7 +479,12 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
@Path("descriptor")
|
||||
@Produces(MediaType.APPLICATION_XML)
|
||||
public String getDescriptor() throws Exception {
|
||||
InputStream is = getClass().getResourceAsStream("/idp-metadata-template.xml");
|
||||
return getIDPMetadataDescriptor(uriInfo, realm);
|
||||
|
||||
}
|
||||
|
||||
public static String getIDPMetadataDescriptor(UriInfo uriInfo, RealmModel realm) throws IOException {
|
||||
InputStream is = SamlService.class.getResourceAsStream("/idp-metadata-template.xml");
|
||||
String template = StreamUtil.readString(is);
|
||||
template = template.replace("${idp.entityID}", RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString());
|
||||
template = template.replace("${idp.sso.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
|
@ -485,7 +492,6 @@ public class SamlService extends AuthorizationEndpointBase {
|
|||
template = template.replace("${idp.sls.HTTP-POST}", RealmsResource.protocolUrl(uriInfo).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString());
|
||||
template = template.replace("${idp.signing.certificate}", realm.getCertificatePem());
|
||||
return template;
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package org.keycloak.protocol.saml.installation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.ClientInstallationProvider;
|
||||
import org.keycloak.protocol.saml.SamlClient;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class ModAuthMellonClientInstallation implements ClientInstallationProvider {
|
||||
@Override
|
||||
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ZipOutputStream zip = new ZipOutputStream(baos);
|
||||
String idpDescriptor = SamlIDPDescriptorClientInstallation.getIDPDescriptorForClient(realm, client, serverBaseUri);
|
||||
String spDescriptor = SamlSPDescriptorClientInstallation.getSPDescriptorForClient(client);
|
||||
String clientDirName = client.getClientId()
|
||||
.replace('/', '_')
|
||||
.replace(' ', '_');
|
||||
try {
|
||||
zip.putNextEntry(new ZipEntry(clientDirName + "/idp-metadata.xml"));
|
||||
zip.write(idpDescriptor.getBytes());
|
||||
zip.closeEntry();
|
||||
zip.putNextEntry(new ZipEntry(clientDirName + "/sp-metadata.xml"));
|
||||
zip.write(spDescriptor.getBytes());
|
||||
zip.closeEntry();
|
||||
if (samlClient.requiresClientSignature()) {
|
||||
if (samlClient.getClientSigningPrivateKey() != null) {
|
||||
zip.putNextEntry(new ZipEntry(clientDirName + "/client-private-key.pem"));
|
||||
zip.write(samlClient.getClientSigningPrivateKey().getBytes());
|
||||
zip.closeEntry();
|
||||
}
|
||||
if (samlClient.getClientSigningCertificate() != null) {
|
||||
zip.putNextEntry(new ZipEntry(clientDirName + "/client-cert.pem"));
|
||||
zip.write(samlClient.getClientSigningCertificate().getBytes());
|
||||
zip.closeEntry();
|
||||
}
|
||||
}
|
||||
zip.close();
|
||||
baos.close();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
|
||||
return Response.ok(baos.toByteArray(), getMediaType()).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return SamlProtocol.LOGIN_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "Mod Auth Mellon files";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "This is a zip file. It contains a SAML SP descriptor, SAML IDP descriptor, private key pem, and certificate pem that you will use to configure mod_auth_mellon for Apache. You'll use these files when crafting the main Apache configuration file. See mod_auth_mellon website for more details.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "keycloak-mod-auth-mellon-sp-config.zip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMediaType() {
|
||||
return "application/zip";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDownloadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationProvider create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "mod-auth-mellon";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package org.keycloak.protocol.saml.installation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.ClientInstallationProvider;
|
||||
import org.keycloak.protocol.saml.SamlClient;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.services.resources.RealmsResource;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.UriBuilder;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SamlIDPDescriptorClientInstallation implements ClientInstallationProvider {
|
||||
public static String getIDPDescriptorForClient(RealmModel realm, ClientModel client, URI serverBaseUri) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
String idpEntityId = RealmsResource.realmBaseUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName()).toString();
|
||||
String idp = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
|
||||
"<EntityDescriptor entityID=\"" + idpEntityId + "\"\n" +
|
||||
" xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n" +
|
||||
" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n" +
|
||||
" <IDPSSODescriptor WantAuthnRequestsSigned=\"" + Boolean.toString(samlClient.requiresClientSignature()) + "\"\n" +
|
||||
" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n";
|
||||
if (samlClient.forceNameIDFormat() && samlClient.getNameIDFormat() != null) {
|
||||
idp += " " + samlClient.getNameIDFormat();
|
||||
} else {
|
||||
idp += " <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified</NameIDFormat>\n" +
|
||||
" <NameIDFormat>urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress</NameIDFormat>\n";
|
||||
}
|
||||
String bindUrl = RealmsResource.protocolUrl(UriBuilder.fromUri(serverBaseUri)).build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString();
|
||||
idp += "\n" +
|
||||
" <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n" +
|
||||
" <SingleLogoutService\n" +
|
||||
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
|
||||
" Location=\"" + bindUrl + "\" />\n" +
|
||||
" <KeyDescriptor use=\"signing\">\n" +
|
||||
" <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
|
||||
" <dsig:X509Data>\n" +
|
||||
" <dsig:X509Certificate>\n" +
|
||||
" " + realm.getCertificatePem() + "\n" +
|
||||
" </dsig:X509Certificate>\n" +
|
||||
" </dsig:X509Data>\n" +
|
||||
" </dsig:KeyInfo>\n" +
|
||||
" </KeyDescriptor>\n" +
|
||||
" </IDPSSODescriptor>\n" +
|
||||
"</EntityDescriptor>\n";
|
||||
return idp;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri) {
|
||||
String descriptor = getIDPDescriptorForClient(realm, client, serverBaseUri);
|
||||
return Response.ok(descriptor, MediaType.TEXT_PLAIN_TYPE).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return SamlProtocol.LOGIN_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "SAML Metadata IDPSSODescriptor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "SAML Metadata IDSSODescriptor tailored for the client. This is special because not every client may require things like digital signatures";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "client-tailored-saml-idp-metadata.xml";
|
||||
}
|
||||
|
||||
public String getMediaType() {
|
||||
return MediaType.APPLICATION_XML;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDownloadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationProvider create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "saml-idp-descriptor";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
package org.keycloak.protocol.saml.installation;
|
||||
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.protocol.ClientInstallationProvider;
|
||||
import org.keycloak.protocol.saml.SamlClient;
|
||||
import org.keycloak.protocol.saml.SamlProtocol;
|
||||
import org.keycloak.saml.SPMetadataDescriptor;
|
||||
import org.keycloak.saml.common.constants.JBossSAMLURIConstants;
|
||||
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||
* @version $Revision: 1 $
|
||||
*/
|
||||
public class SamlSPDescriptorClientInstallation implements ClientInstallationProvider {
|
||||
public static String getSPDescriptorForClient(ClientModel client) {
|
||||
SamlClient samlClient = new SamlClient(client);
|
||||
String assertionUrl = client.getAttribute(SamlProtocol.SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE);
|
||||
if (assertionUrl == null) assertionUrl = client.getManagementUrl();
|
||||
String logoutUrl = client.getAttribute(SamlProtocol.SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE);
|
||||
if (logoutUrl == null) logoutUrl = client.getManagementUrl();
|
||||
String nameIdFormat = samlClient.getNameIDFormat();
|
||||
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
|
||||
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, samlClient.getClientSigningCertificate());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI serverBaseUri) {
|
||||
String descriptor = getSPDescriptorForClient(client);
|
||||
return Response.ok(descriptor, MediaType.TEXT_PLAIN_TYPE).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProtocol() {
|
||||
return SamlProtocol.LOGIN_PROTOCOL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayType() {
|
||||
return "SAML Metadata SPSSODescriptor";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHelpText() {
|
||||
return "SAML SP Metadata EntityDescriptor or rather SPSSODescriptor. This is an XML file.";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilename() {
|
||||
return "saml-sp-metadata.xml";
|
||||
}
|
||||
|
||||
public String getMediaType() {
|
||||
return MediaType.APPLICATION_XML;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDownloadOnly() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClientInstallationProvider create(KeycloakSession session) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(Config.Scope config) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return "saml-sp-descriptor";
|
||||
}
|
||||
}
|
|
@ -1 +1,4 @@
|
|||
org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation
|
||||
org.keycloak.protocol.saml.installation.SamlSPDescriptorClientInstallation
|
||||
org.keycloak.protocol.saml.installation.SamlIDPDescriptorClientInstallation
|
||||
org.keycloak.protocol.saml.installation.ModAuthMellonClientInstallation
|
||||
|
|
|
@ -46,7 +46,12 @@ public class RealmsResource {
|
|||
protected BruteForceProtector protector;
|
||||
|
||||
public static UriBuilder realmBaseUrl(UriInfo uriInfo) {
|
||||
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getRealmResource");
|
||||
UriBuilder baseUriBuilder = uriInfo.getBaseUriBuilder();
|
||||
return realmBaseUrl(baseUriBuilder);
|
||||
}
|
||||
|
||||
public static UriBuilder realmBaseUrl(UriBuilder baseUriBuilder) {
|
||||
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getRealmResource");
|
||||
}
|
||||
|
||||
public static UriBuilder accountUrl(UriBuilder base) {
|
||||
|
|
Loading…
Reference in a new issue