This commit is contained in:
Bill Burke 2016-01-07 17:25:47 -05:00
parent 71b6ed80ae
commit 78fe064cf0
12 changed files with 221 additions and 38 deletions

View file

@ -709,33 +709,6 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
$scope.installation = installation; $scope.installation = installation;
}) })
}; };
/*
$scope.changeFormat = function() {
if ($scope.configFormat == "Keycloak JSON") {
$scope.filename = 'keycloak.json';
var url = ClientInstallation.url({ realm: $routeParams.realm, client: $routeParams.client });
$http.get(url).success(function(data) {
var tmp = angular.fromJson(data);
$scope.installation = angular.toJson(tmp, true);
$scope.type = 'application/json';
})
} else if ($scope.configFormat == "Wildfly/EAP Subsystem XML") {
$scope.filename = 'keycloak.xml';
var url = ClientInstallationJBoss.url({ realm: $routeParams.realm, client: $routeParams.client });
$http.get(url).success(function(data) {
$scope.installation = data;
$scope.type = 'text/xml';
})
}
console.debug($scope.filename);
};
*/
$scope.download = function() { $scope.download = function() {
saveAs(new Blob([$scope.installation], { type: $scope.configFormat.mediaType }), $scope.configFormat.filename); saveAs(new Blob([$scope.installation], { type: $scope.configFormat.mediaType }), $scope.configFormat.filename);
} }
@ -1080,7 +1053,7 @@ module.controller('ClientDetailCtrl', function($scope, realm, client, templates,
module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) { module.controller('CreateClientCtrl', function($scope, realm, client, templates, $route, serverInfo, Client, ClientDescriptionConverter, $location, $modal, Dialog, Notifications) {
$scope.protocols = ['openid-connect', $scope.protocols = ['openid-connect',
'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort(); 'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort();
$scope.create = true;
$scope.templates = [ {name:'NONE'}]; $scope.templates = [ {name:'NONE'}];
for (var i = 0; i < templates.length; i++) { for (var i = 0; i < templates.length; i++) {
var template = templates[i]; var template = templates[i];

View file

@ -33,7 +33,7 @@
<li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li> <li ng-class="{active: path[4] == 'clustering'}" data-ng-show="!client.publicClient"><a href="#/realms/{{realm.realm}}/clients/{{client.id}}/clustering">{{:: 'clustering' | translate}}</a></li>
<li ng-class="{active: path[4] == 'installation'}" data-ng-show="client.protocol != 'saml'"> <li ng-class="{active: path[4] == 'installation'}">
<a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">{{:: 'installation' | translate}}</a> <a href="#/realms/{{realm.realm}}/clients/{{client.id}}/installation">{{:: 'installation' | translate}}</a>
<kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip> <kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip>
</li> </li>

View file

@ -36,4 +36,16 @@
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
</dependencies> </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project> </project>

View file

@ -98,6 +98,8 @@ public class DeploymentBuilder {
} }
if (key.isEncryption()) { if (key.isEncryption()) {
if (key.getKeystore() != null) {
KeyStore keyStore = loadKeystore(resourceLoader, key); KeyStore keyStore = loadKeystore(resourceLoader, key);
try { try {
PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray()); PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
@ -105,6 +107,18 @@ public class DeploymentBuilder {
} catch (Exception e) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
} else {
if (key.getPrivateKeyPem() == null) {
throw new RuntimeException("SP signing key must have a PrivateKey defined");
}
try {
PrivateKey privateKey = PemUtils.decodePrivateKey(key.getPrivateKeyPem().trim());
deployment.setDecryptionKey(privateKey);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
} }
} }
} }

View file

@ -116,7 +116,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem); attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
} else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) { } else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
attributes.put(SamlConfigAttributes.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE); attributes.put(SamlConfigAttributes.SAML_ENCRYPT, SamlProtocol.ATTRIBUTE_TRUE_VALUE);
attributes.put(SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem); attributes.put(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, certPem);
} }
} }

View file

@ -121,4 +121,21 @@ public class SamlClient extends ClientConfigResolver {
} }
public String getClientEncryptingCertificate() {
return client.getAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
}
public void setClientEncryptingCertificate(String val) {
client.setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE, val);
}
public String getClientEncryptingPrivateKey() {
return client.getAttribute(SamlConfigAttributes.SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE);
}
public void setClientEncryptingPrivateKey(String val) {
client.setAttribute(SamlConfigAttributes.SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE, val);
}
} }

View file

@ -19,4 +19,6 @@ public interface SamlConfigAttributes {
String SAML_ENCRYPT = "saml.encrypt"; String SAML_ENCRYPT = "saml.encrypt";
String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature"; String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE; String SAML_SIGNING_CERTIFICATE_ATTRIBUTE = "saml.signing." + ClientAttributeCertificateResource.X509CERTIFICATE;
String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
String SAML_ENCRYPTION_PRIVATE_KEY_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.PRIVATE_KEY;
} }

View file

@ -70,7 +70,6 @@ public class SamlProtocol implements LoginProtocol {
public static final String ATTRIBUTE_TRUE_VALUE = "true"; public static final String ATTRIBUTE_TRUE_VALUE = "true";
public static final String ATTRIBUTE_FALSE_VALUE = "false"; public static final String ATTRIBUTE_FALSE_VALUE = "false";
public static final String SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE = "saml.encryption." + ClientAttributeCertificateResource.X509CERTIFICATE;
public static final String SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE = "saml_assertion_consumer_url_post"; public static final String SAML_ASSERTION_CONSUMER_URL_POST_ATTRIBUTE = "saml_assertion_consumer_url_post";
public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect"; public static final String SAML_ASSERTION_CONSUMER_URL_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect";
public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post"; public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post";

View file

@ -49,7 +49,7 @@ public class SamlProtocolUtils {
} }
public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException { public static PublicKey getEncryptionValidationKey(ClientModel client) throws VerificationException {
return getPublicKey(client, SamlProtocol.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE); return getPublicKey(client, SamlConfigAttributes.SAML_ENCRYPTION_CERTIFICATE_ATTRIBUTE);
} }
public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException { public static PublicKey getPublicKey(ClientModel client, String attribute) throws VerificationException {

View file

@ -0,0 +1,161 @@
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;
import java.util.Map;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class KeycloakSamlClientInstallation implements ClientInstallationProvider {
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
SamlClient samlClient = new SamlClient(client);
StringBuffer buffer = new StringBuffer();
buffer.append("<keycloak-saml-adapter>\n");
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");
if (samlClient.requiresClientSignature() || samlClient.requiresEncryption()) {
buffer.append(" <Keys>\n");
if (samlClient.requiresClientSignature()) {
buffer.append(" <Key signing=\"true\">\n");
buffer.append(" <PrivateKeyPem>\n");
if (samlClient.getClientSigningPrivateKey() == null) {
buffer.append(" PRIVATE KEY NOT SET UP OR KNOWN\n");
} else {
buffer.append(" ").append(samlClient.getClientSigningPrivateKey()).append("\n");
}
buffer.append(" </PrivateKeyPem>\n");
buffer.append(" <CertificatePem>\n");
if (samlClient.getClientSigningCertificate() == null) {
buffer.append(" YOU MUST CONFIGURE YOUR CLIENT's SIGNING CERTIFICATE\n");
} else {
buffer.append(" ").append(samlClient.getClientSigningCertificate()).append("\n");
}
buffer.append(" </CertificatePem>\n");
buffer.append(" </Key>\n");
}
if (samlClient.requiresEncryption()) {
buffer.append(" <Key encryption=\"true\">\n");
buffer.append(" <PrivateKeyPem>\n");
if (samlClient.getClientEncryptingPrivateKey() == null) {
buffer.append(" PRIVATE KEY NOT SET UP OR KNOWN\n");
} else {
buffer.append(" ").append(samlClient.getClientEncryptingPrivateKey()).append("\n");
}
buffer.append(" </PrivateKeyPem>\n");
buffer.append(" </Key>\n");
}
buffer.append(" </Keys>\n");
}
buffer.append(" <IDP entityID=\"idp\"");
if (samlClient.requiresClientSignature()) {
buffer.append("\n signatureAlgorithm=\"").append(samlClient.getSignatureAlgorithm()).append("\"");
if (samlClient.getCanonicalizationMethod() != null) {
buffer.append("\n signatureCanonicalizationMethod=\"").append(samlClient.getCanonicalizationMethod()).append("\"");
}
}
buffer.append(">\n");
buffer.append(" <SingleSignOnService signRequest=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
buffer.append(" validateResponseSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
buffer.append(" requestBinding=\"POST\"\n");
UriBuilder bindingUrlBuilder = UriBuilder.fromUri(baseUri);
String bindingUrl = RealmsResource.protocolUrl(bindingUrlBuilder)
.build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString();
buffer.append(" bindingUrl=\"").append(bindingUrl).append("\"/>\n");
buffer.append(" <SingleLogoutService signRequest=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
buffer.append(" signResponse=\"").append(Boolean.toString(samlClient.requiresClientSignature())).append("\"\n");
buffer.append(" validateRequestSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
buffer.append(" validateResponseSignature=\"").append(Boolean.toString(samlClient.requiresRealmSignature())).append("\"\n");
buffer.append(" requestBinding=\"POST\"\n");
buffer.append(" responseBinding=\"POST\"\n");
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(realm.getCertificatePem()).append("\n");
buffer.append(" </CertificatePem>\n");
buffer.append(" </Key>\n");
buffer.append(" </Keys>\n");
}
buffer.append(" </IDP>\n");
buffer.append(" </SP>\n");
buffer.append("</keycloak-saml-adapter>\n");
return Response.ok(buffer.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}
@Override
public String getProtocol() {
return SamlProtocol.LOGIN_PROTOCOL;
}
@Override
public String getDisplayType() {
return "Keycloak SAML Adapter keycloak-saml.xml";
}
@Override
public String getHelpText() {
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory if your WAR.";
}
@Override
public String getFilename() {
return "keycloak-saml.xml";
}
@Override
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 "keycloak-saml";
}
}

View file

@ -0,0 +1 @@
org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation

View file

@ -57,6 +57,10 @@ public class RealmsResource {
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol"); return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
} }
public static UriBuilder protocolUrl(UriBuilder builder) {
return builder.path(RealmsResource.class).path(RealmsResource.class, "getProtocol");
}
public static UriBuilder clientRegistrationUrl(UriInfo uriInfo) { public static UriBuilder clientRegistrationUrl(UriInfo uriInfo) {
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getClientsService"); return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getClientsService");
} }