2213
This commit is contained in:
parent
71b6ed80ae
commit
78fe064cf0
12 changed files with 221 additions and 38 deletions
|
@ -709,33 +709,6 @@ module.controller('ClientInstallationCtrl', function($scope, realm, client, serv
|
|||
$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() {
|
||||
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) {
|
||||
$scope.protocols = ['openid-connect',
|
||||
'saml'];//Object.keys(serverInfo.providers['login-protocol'].providers).sort();
|
||||
|
||||
$scope.create = true;
|
||||
$scope.templates = [ {name:'NONE'}];
|
||||
for (var i = 0; i < templates.length; i++) {
|
||||
var template = templates[i];
|
||||
|
|
|
@ -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] == '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>
|
||||
<kc-tooltip>{{:: 'installation.tooltip' | translate}}</kc-tooltip>
|
||||
</li>
|
||||
|
|
|
@ -36,4 +36,16 @@
|
|||
<scope>test</scope>
|
||||
</dependency>
|
||||
</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>
|
||||
|
|
|
@ -98,6 +98,8 @@ public class DeploymentBuilder {
|
|||
|
||||
}
|
||||
if (key.isEncryption()) {
|
||||
if (key.getKeystore() != null) {
|
||||
|
||||
KeyStore keyStore = loadKeystore(resourceLoader, key);
|
||||
try {
|
||||
PrivateKey privateKey = (PrivateKey) keyStore.getKey(key.getKeystore().getPrivateKeyAlias(), key.getKeystore().getPrivateKeyPassword().toCharArray());
|
||||
|
@ -105,6 +107,18 @@ public class DeploymentBuilder {
|
|||
} catch (Exception 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -116,7 +116,7 @@ public class EntityDescriptorDescriptionConverter implements ClientDescriptionCo
|
|||
attributes.put(SamlConfigAttributes.SAML_SIGNING_CERTIFICATE_ATTRIBUTE, certPem);
|
||||
} else if (keyDescriptor.getUse() == KeyTypes.ENCRYPTION) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -19,4 +19,6 @@ public interface SamlConfigAttributes {
|
|||
String SAML_ENCRYPT = "saml.encrypt";
|
||||
String SAML_CLIENT_SIGNATURE_ATTRIBUTE = "saml.client.signature";
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -70,7 +70,6 @@ public class SamlProtocol implements LoginProtocol {
|
|||
|
||||
public static final String ATTRIBUTE_TRUE_VALUE = "true";
|
||||
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_REDIRECT_ATTRIBUTE = "saml_assertion_consumer_url_redirect";
|
||||
public static final String SAML_SINGLE_LOGOUT_SERVICE_URL_POST_ATTRIBUTE = "saml_single_logout_service_url_post";
|
||||
|
|
|
@ -49,7 +49,7 @@ public class SamlProtocolUtils {
|
|||
}
|
||||
|
||||
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 {
|
||||
|
|
|
@ -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";
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation
|
|
@ -57,6 +57,10 @@ public class RealmsResource {
|
|||
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) {
|
||||
return uriInfo.getBaseUriBuilder().path(RealmsResource.class).path(RealmsResource.class, "getClientsService");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue