KEYCLOAK-10898 WildFly Adapter CLI based installation scripts

This commit is contained in:
vramik 2020-02-04 11:59:06 +01:00 committed by Hynek Mlnařík
parent 8061aa5217
commit 7c91e36e43
7 changed files with 341 additions and 6 deletions

View file

@ -0,0 +1,127 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.protocol.oidc.installation;
import static org.keycloak.protocol.util.ClientCliInstallationUtil.quote;
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.oidc.OIDCLoginProtocol;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import java.util.Map;
public class KeycloakOIDCJbossSubsystemClientCliInstallation implements ClientInstallationProvider {
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
String deploymentName = "WAR MODULE NAME.war";
StringBuilder builder = new StringBuilder();
builder
.append("/subsystem=keycloak/secure-deployment=").append(quote(deploymentName)).append("/:add( \\\n")
.append(" realm=").append(quote(realm.getName())).append(", \\\n")
.append(" resource=").append(quote(client.getClientId())).append(", \\\n")
.append(" auth-server-url=").append(baseUri).append(", \\\n");
if (client.isBearerOnly()){
builder.append(" bearer-only=true, \\\n");
} else if (client.isPublicClient()) {
builder.append(" public-client=true, \\\n");
}
if (KeycloakOIDCClientInstallation.showVerifyTokenAudience(client)) {
builder.append(" verify-token-audience=true, \\\n");
}
if (client.getRoles().size() > 0) {
builder.append(" use-resource-role-mappings=true, \\\n");
}
builder.append(" ssl-required=").append(realm.getSslRequired().name()).append(")\n\n");
if (KeycloakOIDCClientInstallation.showClientCredentialsAdapterConfig(client)) {
Map<String, Object> adapterConfig = KeycloakOIDCClientInstallation.getClientCredentialsAdapterConfig(session, client);
for (Map.Entry<String, Object> entry : adapterConfig.entrySet()) {
builder.append("/subsystem=keycloak/secure-deployment=").append(quote(deploymentName)).append("/")
.append("credential=").append(entry.getKey()).append(":add(value=").append(entry.getValue())
.append(")\n");
}
}
return Response.ok(builder.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}
@Override
public String getProtocol() {
return OIDCLoginProtocol.LOGIN_PROTOCOL;
}
@Override
public String getDisplayType() {
return "Keycloak OIDC JBoss Subsystem CLI";
}
@Override
public String getHelpText() {
return "CLI script you must edit and apply to your client app server. This type of configuration is useful when you can't or don't want to crack open your WAR file.";
}
@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-oidc-jboss-subsystem-cli";
}
@Override
public boolean isDownloadOnly() {
return false;
}
@Override
public String getFilename() {
return "keycloak-oidc-subsystem.cli";
}
@Override
public String getMediaType() {
return MediaType.TEXT_PLAIN;
}
}

View file

@ -49,7 +49,7 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
} }
public static void baseXml(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri, SamlClient samlClient, StringBuilder 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(" <SP entityID=\"").append(client.getBaseUrl() == null ? "SPECIFY YOUR entityID!" : client.getBaseUrl()).append("\"\n");
buffer.append(" sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n"); buffer.append(" sslPolicy=\"").append(realm.getSslRequired().name()).append("\"\n");
buffer.append(" logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n"); buffer.append(" logoutPage=\"SPECIFY YOUR LOGOUT PAGE!\">\n");
if (samlClient.requiresClientSignature() || samlClient.requiresEncryption()) { if (samlClient.requiresClientSignature() || samlClient.requiresEncryption()) {
@ -128,7 +128,7 @@ public class KeycloakSamlClientInstallation implements ClientInstallationProvide
@Override @Override
public String getHelpText() { public String getHelpText() {
return "Keycloak SAML adapter configuration file. Put this in WEB-INF directory of your WAR."; return "Keycloak SAML adapter configuration file you must edit. Put this in WEB-INF directory of your WAR.";
} }
@Override @Override

View file

@ -0,0 +1,167 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.protocol.saml.installation;
import static org.keycloak.protocol.util.ClientCliInstallationUtil.quote;
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.MediaType;
import javax.ws.rs.core.Response;
import java.net.URI;
import javax.ws.rs.core.UriBuilder;
import org.keycloak.services.resources.RealmsResource;
public class KeycloakSamlSubsystemCliInstallation implements ClientInstallationProvider {
@Override
public Response generateInstallation(KeycloakSession session, RealmModel realm, ClientModel client, URI baseUri) {
SamlClient samlClient = new SamlClient(client);
StringBuilder builder = new StringBuilder();
String entityId = client.getBaseUrl() == null ? "SPECIFY YOUR entityID!" : client.getBaseUrl();
String bindingUrl = RealmsResource.protocolUrl(UriBuilder.fromUri(baseUri))
.build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL).toString();
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/:add\n\n")
.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/:add(sslPolicy=")
.append(realm.getSslRequired().name())
.append(",logoutPage=")
.append(quote("SPECIFY YOUR LOGOUT PAGE!"))
.append("\n\n");
if (samlClient.requiresClientSignature()) {
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/Key=KEY1:add(signing=true, \\\nPrivateKeyPem=")
.append(quote(samlClient.getClientSigningPrivateKey() == null ? "PRIVATE KEY NOT SET UP OR KNOWN" : samlClient.getClientSigningPrivateKey()))
.append(", \\\nCertificatePem=")
.append(quote(samlClient.getClientSigningCertificate() == null ? "YOU MUST CONFIGURE YOUR_CLIENT's SIGNING CERTIFICATE" : samlClient.getClientSigningCertificate()))
.append(")\n\n");
}
if (samlClient.requiresEncryption()) {
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/Key=KEY2:add(encryption=true,PrivateKeyPem=")
.append(quote(samlClient.getClientEncryptingPrivateKey() == null ? "PRIVATE KEY NOT SET UP OR KNOWN" : samlClient.getClientEncryptingPrivateKey()))
.append(")\n\n");
}
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/IDP=idp/:add( \\\n SingleSignOnService={ \\\n signRequest=")
.append(Boolean.toString(samlClient.requiresClientSignature()))
.append(", \\\n validateResponseSignature=")
.append(Boolean.toString(samlClient.requiresRealmSignature()))
.append(", \\\n validateAssertionSignature=")
.append(Boolean.toString(samlClient.requiresAssertionSignature()))
.append(", \\\n requestBinding=POST, \\\n bindingUrl=")
.append(bindingUrl)
.append("}, \\\n SingleLogoutService={ \\\n signRequest=")
.append(Boolean.toString(samlClient.requiresClientSignature()))
.append(", \\\n signResponse=")
.append(Boolean.toString(samlClient.requiresClientSignature()))
.append(", \\\n validateRequestSignature=")
.append(Boolean.toString(samlClient.requiresRealmSignature()))
.append(", \\\n validateResponseSignature=")
.append(Boolean.toString(samlClient.requiresRealmSignature()))
.append(", \\\n requestBinding=POST, \\\n responseBinding=POST, \\\n postBindingUrl=")
.append(bindingUrl)
.append(", \\\n redirectBindingUrl=")
.append(bindingUrl)
.append("} \\\n)\n\n");
if (samlClient.requiresClientSignature()) {
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/IDP=idp/:write-attribute(name=signatureAlgorithm,value=")
.append(samlClient.getSignatureAlgorithm())
.append(")\n\n");
if (samlClient.getCanonicalizationMethod() != null) {
builder.append("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/SP=")
.append(quote(entityId))
.append("/IDP=idp/:write-attribute(name=signatureCanonicalizationMethod,value=")
.append(samlClient.getCanonicalizationMethod())
.append(")\n");
}
}
return Response.ok(builder.toString(), MediaType.TEXT_PLAIN_TYPE).build();
}
@Override
public String getProtocol() {
return SamlProtocol.LOGIN_PROTOCOL;
}
@Override
public String getDisplayType() {
return "Keycloak SAML JBoss Subsystem CLI";
}
@Override
public String getHelpText() {
return "CLI script you must edit and apply to your client app server. This type of configuration is useful when you can't or don't want to crack open your WAR file.";
}
@Override
public String getFilename() {
return "keycloak-saml-subsystem.cli";
}
@Override
public String getMediaType() {
return MediaType.TEXT_PLAIN;
}
@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-subsystem-cli";
}
}

View file

@ -53,12 +53,12 @@ public class KeycloakSamlSubsystemInstallation implements ClientInstallationProv
@Override @Override
public String getDisplayType() { public String getDisplayType() {
return "Keycloak SAML Wildfly/JBoss Subsystem"; return "Keycloak SAML JBoss Subsystem XML";
} }
@Override @Override
public String getHelpText() { public String getHelpText() {
return "Keycloak SAML adapter Wildfly/JBoss subsystem xml. Put this <subsystem xmlns=\"urn:jboss:domain:keycloak-saml:1.2\"> element of your standalone.xml file."; return "Keycloak SAML adapter JBoss subsystem xml you must edit. Put this into <subsystem xmlns=\"urn:jboss:domain:keycloak-saml:1.2\"> element of your standalone.xml file.";
} }
@Override @Override

View file

@ -0,0 +1,23 @@
/*
* Copyright 2020 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.protocol.util;
public class ClientCliInstallationUtil {
public static String quote(String value) {
return "\"" + value.replaceAll("\\\\", "\\\\\\\\").replaceAll("\"", "\\\\\"") + "\"";
}
}

View file

@ -16,10 +16,12 @@
# #
org.keycloak.protocol.oidc.installation.KeycloakOIDCClientInstallation org.keycloak.protocol.oidc.installation.KeycloakOIDCClientInstallation
org.keycloak.protocol.oidc.installation.KeycloakOIDCJbossSubsystemClientCliInstallation
org.keycloak.protocol.oidc.installation.KeycloakOIDCJbossSubsystemClientInstallation org.keycloak.protocol.oidc.installation.KeycloakOIDCJbossSubsystemClientInstallation
org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation org.keycloak.protocol.saml.installation.KeycloakSamlClientInstallation
org.keycloak.protocol.saml.installation.SamlSPDescriptorClientInstallation org.keycloak.protocol.saml.installation.SamlSPDescriptorClientInstallation
org.keycloak.protocol.saml.installation.ModAuthMellonClientInstallation org.keycloak.protocol.saml.installation.ModAuthMellonClientInstallation
org.keycloak.protocol.saml.installation.KeycloakSamlSubsystemCliInstallation
org.keycloak.protocol.saml.installation.KeycloakSamlSubsystemInstallation org.keycloak.protocol.saml.installation.KeycloakSamlSubsystemInstallation
org.keycloak.protocol.docker.installation.DockerVariableOverrideInstallationProvider org.keycloak.protocol.docker.installation.DockerVariableOverrideInstallationProvider
org.keycloak.protocol.docker.installation.DockerRegistryConfigFileInstallationProvider org.keycloak.protocol.docker.installation.DockerRegistryConfigFileInstallationProvider

View file

@ -109,6 +109,13 @@ public class InstallationTest extends AbstractClientTest {
assertOidcInstallationConfig(json); assertOidcInstallationConfig(json);
} }
@Test
public void testOidcJBossCli() {
String cli = oidcClient.getInstallationProvider("keycloak-oidc-jboss-subsystem-cli");
assertOidcInstallationConfig(cli);
assertThat(cli, containsString("/subsystem=keycloak/secure-deployment=\"WAR MODULE NAME.war\""));
}
@Test @Test
public void testOidcBearerOnlyJson() { public void testOidcBearerOnlyJson() {
String json = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json"); String json = oidcBearerOnlyClient.getInstallationProvider("keycloak-oidc-keycloak-json");
@ -167,11 +174,20 @@ public class InstallationTest extends AbstractClientTest {
public void testSamlAdapterXml() { public void testSamlAdapterXml() {
String xml = samlClient.getInstallationProvider("keycloak-saml"); String xml = samlClient.getInstallationProvider("keycloak-saml");
assertThat(xml, containsString("<keycloak-saml-adapter>")); assertThat(xml, containsString("<keycloak-saml-adapter>"));
assertThat(xml, containsString(SAML_NAME)); assertThat(xml, containsString("SPECIFY YOUR entityID!"));
assertThat(xml, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate()))); assertThat(xml, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate())));
assertThat(xml, containsString(samlUrl())); assertThat(xml, containsString(samlUrl()));
} }
@Test
public void testSamlAdapterCli() {
String cli = samlClient.getInstallationProvider("keycloak-saml-subsystem-cli");
assertThat(cli, containsString("/subsystem=keycloak-saml/secure-deployment=YOUR-WAR.war/"));
assertThat(cli, containsString("SPECIFY YOUR entityID!"));
assertThat(cli, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate())));
assertThat(cli, containsString(samlUrl()));
}
@Test @Test
public void testSamlMetadataSpDescriptor() { public void testSamlMetadataSpDescriptor() {
String xml = samlClient.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR); String xml = samlClient.getInstallationProvider(SamlSPDescriptorClientInstallation.SAML_CLIENT_INSTALATION_SP_DESCRIPTOR);
@ -184,7 +200,7 @@ public class InstallationTest extends AbstractClientTest {
public void testSamlJBossXml() { public void testSamlJBossXml() {
String xml = samlClient.getInstallationProvider("keycloak-saml-subsystem"); String xml = samlClient.getInstallationProvider("keycloak-saml-subsystem");
assertThat(xml, containsString("<secure-deployment")); assertThat(xml, containsString("<secure-deployment"));
assertThat(xml, containsString(SAML_NAME)); assertThat(xml, containsString("SPECIFY YOUR entityID!"));
assertThat(xml, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate()))); assertThat(xml, not(containsString(ApiUtil.findActiveKey(testRealmResource()).getCertificate())));
assertThat(xml, containsString(samlUrl())); assertThat(xml, containsString(samlUrl()));
} }