KEYCLOAK-1881 Client installers

This commit is contained in:
Hynek Mlnarik 2016-11-03 16:26:44 +01:00
parent 4f9e35c0a1
commit 8ae1b1740d
4 changed files with 58 additions and 51 deletions

View file

@ -18,7 +18,6 @@
package org.keycloak.protocol.saml.installation;
import org.keycloak.Config;
import org.keycloak.common.util.PemUtils;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
@ -42,14 +41,14 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
SamlClient samlClient = new SamlClient(client);
StringBuffer buffer = new StringBuffer();
StringBuilder buffer = new StringBuilder();
buffer.append("<keycloak-saml-adapter>\n");
baseXml(session, realm, client, baseUri, samlClient, buffer);
buffer.append("</keycloak-saml-adapter>\n");
return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuffer buffer) {
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder buffer) {
buffer.append(" <SP entityID=\"").append(client.getClientId()).append("\"\n");
buffer.append(" sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n");
buffer.append(" logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n");
@ -113,15 +112,6 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
buffer.append(" postBindingUrl=\"").append(bindingUrl).append("\"\n");
buffer.append(" redirectBindingUrl=\"").append(bindingUrl).append("\"");
buffer.append("/>\n");
if (samlClient.requiresRealmSignature()) {
buffer.append(" <Keys>\n");
buffer.append(" <Key signing=\"true\">\n");
buffer.append(" <CertificatePem>\n");
buffer.append(" ").append(PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate())).append("\n");
buffer.append(" </CertificatePem>\n");
buffer.append(" </Key>\n");
buffer.append(" </Keys>\n");
}
buffer.append(" </IDP>\n");
buffer.append(" </SP>\n");
}
@ -138,7 +128,7 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
@Override
public String getHelpText() {
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory if your WAR.";
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory of your WAR.";
}
@Override

View file

@ -39,7 +39,7 @@ public class KeycloakSamlSubsystemInstallation implements ClientInstallationProv
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
SamlClient samlClient = new SamlClient(client);
StringBuffer buffer = new StringBuffer();
StringBuilder buffer = new StringBuilder();
buffer.append("<secure-deployment name=\"YOUR-WAR.war\">\n");
KeycloakSamlClientInstallation.baseXml(session, realm, client, baseUri, samlClient, buffer);
buffer.append("</secure-deployment>\n");

View file

@ -32,6 +32,11 @@ import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import java.net.URI;
import java.util.Set;
import java.util.TreeSet;
import org.keycloak.dom.saml.v2.metadata.KeyTypes;
import org.keycloak.keys.KeyMetadata;
import org.keycloak.saml.SPMetadataDescriptor;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@ -41,49 +46,61 @@ public class SamlIDPDescriptorClientInstallation implements ClientInstallationPr
public static String getIDPDescriptorForClient(KeycloakSession session, 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";
StringBuilder sb = new StringBuilder();
sb.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
+ "<EntityDescriptor entityID=\"").append(idpEntityId).append("\"\n"
+ " xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\"\n"
+ " xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\"\n"
+ " xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\">\n"
+ " <IDPSSODescriptor WantAuthnRequestsSigned=\"")
.append(samlClient.requiresClientSignature())
.append("\"\n"
+ " protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\n");
if (samlClient.forceNameIDFormat() && samlClient.getNameIDFormat() != null) {
idp += " <NameIDFormat>" + samlClient.getNameIDFormat() + "</NameIDFormat>\n";
sb.append(" <NameIDFormat>").append(samlClient.getNameIDFormat()).append("</NameIDFormat>\n");
} 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";
sb.append(" <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";
if (!samlClient.forcePostBinding()) {
idp += " <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n" +
" Location=\"" + bindUrl + "\" />\n";
sb.append("\n"
+ " <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n"
+ " Location=\"").append(bindUrl).append("\" />\n");
if (! samlClient.forcePostBinding()) {
sb.append(" <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n"
+ " Location=\"").append(bindUrl).append("\" />\n");
}
idp += " <SingleLogoutService\n" +
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n" +
" Location=\"" + bindUrl + "\" />\n";
if (!samlClient.forcePostBinding()) {
idp += " <SingleLogoutService\n" +
" Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n" +
" Location=\"" + bindUrl + "\" />\n";
sb.append(" <SingleLogoutService\n"
+ " Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST\"\n"
+ " Location=\"").append(bindUrl).append("\" />\n");
if (! samlClient.forcePostBinding()) {
sb.append(" <SingleLogoutService\n"
+ " Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\"\n"
+ " Location=\"").append(bindUrl).append("\" />\n");
}
idp += " <KeyDescriptor use=\"signing\">\n" +
" <dsig:KeyInfo xmlns:dsig=\"http://www.w3.org/2000/09/xmldsig#\">\n" +
" <dsig:X509Data>\n" +
" <dsig:X509Certificate>\n" +
" " + PemUtils.encodeCertificate(session.keys().getActiveKey(realm).getCertificate()) + "\n" +
" </dsig:X509Certificate>\n" +
" </dsig:X509Data>\n" +
" </dsig:KeyInfo>\n" +
" </KeyDescriptor>\n" +
" </IDPSSODescriptor>\n" +
"</EntityDescriptor>\n";
return idp;
Set<KeyMetadata> keys = new TreeSet<>((o1, o2) -> o1.getStatus() == o2.getStatus() // Status can be only PASSIVE OR ACTIVE, push PASSIVE to end of list
? (int) (o2.getProviderPriority() - o1.getProviderPriority())
: (o1.getStatus() == KeyMetadata.Status.PASSIVE ? 1 : -1));
keys.addAll(session.keys().getKeys(realm, false));
for (KeyMetadata key : keys) {
addKeyInfo(sb, key, KeyTypes.SIGNING.value());
}
sb.append(" </IDPSSODescriptor>\n"
+ "</EntityDescriptor>\n");
return sb.toString();
}
private static void addKeyInfo(StringBuilder target, KeyMetadata key, String purpose) {
if (key == null) {
return;
}
target.append(SPMetadataDescriptor.xmlKeyInfo(" ", key.getKid(), PemUtils.encodeCertificate(key.getCertificate()), purpose, false));
}
@Override

View file

@ -46,7 +46,7 @@ public class SamlSPDescriptorClientInstallation implements ClientInstallationPro
if (logoutUrl == null) logoutUrl = client.getManagementUrl();
String nameIdFormat = samlClient.getNameIDFormat();
if (nameIdFormat == null) nameIdFormat = SamlProtocol.SAML_DEFAULT_NAMEID_FORMAT;
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
String spCertificate = SPMetadataDescriptor.xmlKeyInfo(" ", null, samlClient.getClientSigningCertificate(), KeyTypes.SIGNING.value(), true);
return SPMetadataDescriptor.getSPDescriptor(JBossSAMLURIConstants.SAML_HTTP_POST_BINDING.get(), assertionUrl, logoutUrl, samlClient.requiresClientSignature(), client.getClientId(), nameIdFormat, spCertificate);
}