KEYCLOAK-1924 SAML adapter full subsystem EAP/Wildfly

This commit is contained in:
Marko Strukelj 2015-11-09 15:12:01 +01:00
parent 426a2b46fc
commit 38d160fab2
89 changed files with 5174 additions and 236 deletions

View file

@ -40,7 +40,7 @@
</excludes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/unpacked/keycloak-saml-wf9-adapter-${project.version}</directory>
<directory>${project.build.directory}/unpacked/keycloak-saml-wildfly-adapter-${project.version}</directory>
<outputDirectory>keycloak</outputDirectory>
<excludes>
<exclude>standalone/configuration/standalone-keycloak.xml</exclude>

View file

@ -26,7 +26,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>zip</type>
</dependency>
<dependency>
@ -116,9 +116,9 @@
<artifactItems>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked/keycloak-saml-wf9-adapter-${project.version}</outputDirectory>
<outputDirectory>${project.build.directory}/unpacked/keycloak-saml-wildfly-adapter-${project.version}</outputDirectory>
</artifactItem>
</artifactItems>
</configuration>

View file

@ -44,7 +44,7 @@
<web-context>auth</web-context>
</subsystem>
<subsystem xmlns="urn:jboss:domain:keycloak:1.1"/>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6"/>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
</xsl:copy>
</xsl:template>

View file

@ -338,12 +338,12 @@
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>zip</type>
</artifactItem>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>tar.gz</type>
</artifactItem>
</artifactItems>

View file

@ -15,7 +15,7 @@
<packaging>pom</packaging>
<modules>
<module>wf9-adapter</module>
<module>wildfly-adapter</module>
<module>tomcat6-adapter-zip</module>
<module>tomcat7-adapter-zip</module>
<module>tomcat8-adapter-zip</module>

View file

@ -6,15 +6,15 @@
<version>1.7.0.Final-SNAPSHOT</version>
<relativePath>../../../pom.xml</relativePath>
</parent>
<name>Keycloak Wildfly 9 SAML Adapter</name>
<name>Keycloak Wildfly SAML Adapter</name>
<description/>
<modelVersion>4.0.0</modelVersion>
<artifactId>keycloak-saml-wf9-adapter-dist-pom</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist-pom</artifactId>
<packaging>pom</packaging>
<modules>
<module>wf9-modules</module>
<module>wf9-adapter-zip</module>
<module>wildfly-modules</module>
<module>wildfly-adapter-zip</module>
</modules>
</project>

View file

@ -18,7 +18,7 @@
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-saml-undertow-adapter/**</include>
<include>org/keycloak/keycloak-saml-wildfly-adapter/**</include>
<include>org/keycloak/keycloak-saml-wf9-subsystem/**</include>
<include>org/keycloak/keycloak-saml-wildfly-subsystem/**</include>
<include>org/keycloak/keycloak-saml-adapter-subsystem/**</include>
</includes>
<excludes>

View file

@ -8,15 +8,15 @@
<relativePath>../../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<packaging>pom</packaging>
<name>Keycloak SAML Wildfly 9 Adapter Distro</name>
<name>Keycloak SAML Wildfly Adapter Distro</name>
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-modules</artifactId>
<artifactId>keycloak-saml-wildfly-modules</artifactId>
<type>zip</type>
</dependency>
</dependencies>
@ -37,7 +37,7 @@
<artifactItems>
<artifactItem>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-modules</artifactId>
<artifactId>keycloak-saml-wildfly-modules</artifactId>
<type>zip</type>
<outputDirectory>${project.build.directory}/unpacked</outputDirectory>
</artifactItem>

View file

@ -69,8 +69,8 @@
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-adapter"/>
</module-def>
<module-def name="org.keycloak.keycloak-saml-wf9-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-saml-wf9-subsystem"/>
<module-def name="org.keycloak.keycloak-saml-wildfly-subsystem">
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-subsystem"/>
</module-def>
</target>

View file

@ -12,9 +12,9 @@
<relativePath>../../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-saml-wf9-modules</artifactId>
<artifactId>keycloak-saml-wildfly-modules</artifactId>
<name>Keycloak SAML Wildfly 9 Modules</name>
<name>Keycloak SAML Wildfly Modules</name>
<packaging>pom</packaging>
<dependencies>
<dependency>
@ -51,7 +51,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-subsystem</artifactId>
<artifactId>keycloak-saml-wildfly-subsystem</artifactId>
</dependency>
</dependencies>

View file

@ -30,6 +30,6 @@
</resources>
<dependencies>
<module name="org.keycloak.keycloak-saml-wf9-subsystem" export="true" services="export"/>
<module name="org.keycloak.keycloak-saml-wildfly-subsystem" export="true" services="export"/>
</dependencies>
</module>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
-->
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-wf9-subsystem">
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-wildfly-subsystem">
<resources>
<resource-root path="."/>

View file

@ -3,7 +3,7 @@
<para>
To be able to secure WAR apps deployed on JBoss EAP 6.x or Wildfly, you must install and
configure the Keycloak SAML Adapter Subsystem. You then provide a keycloak
config, <literal>/WEB-INF/keycloak-saml</literal> file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml.
config, <literal>/WEB-INF/keycloak-saml.xml</literal> file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml.
Both methods are described in this section.
</para>
<section id="jboss-adapter-installation">
@ -13,10 +13,10 @@
the Keycloak download site. They are also available as a maven artifact.
</para>
<para>
Install on Wildfly 9:
Install on Wildfly 9 or 10:
<programlisting>
$ cd $WILDFLY_HOME
$ unzip keycloak-saml-wf9-adapter-dist.zip
$ unzip keycloak-saml-wildfly-adapter-dist.zip
</programlisting>
</para>
<para>
@ -52,7 +52,7 @@ $ jboss-cli.sh -c --file=adapter-install.cli
</extensions>
<profile>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6"/>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
...
</profile>
]]>
@ -185,4 +185,92 @@ public class CustomerService {
</programlisting>
</para>
</section>
<section>
<title>Securing WARs via Keycloak SAML Subsystem</title>
<para>
You do not have to crack open a WAR to secure it with Keycloak. Alternatively, you can externally secure
it via the Keycloak SAML Adapter Subsystem. While you don't have to specify KEYCLOAK-SAML as an <literal>auth-method</literal>,
you still have to define the <literal>security-constraints</literal> in <literal>web.xml</literal>. You do
not, however, have to create a <literal>WEB-INF/keycloak-saml.xml</literal> file. This metadata is instead defined
within XML in your server's <literal>domain.xml</literal> or <literal>standalone.xml</literal> subsystem
configuration section.
</para>
<para>
<programlisting><![CDATA[
<extensions>
<extension module="org.keycloak.keycloak-saml-adapter-subsystem"/>
</extensions>
<profile>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
<secure-deployment name="WAR MODULE NAME.war">
<SP entityID="APPLICATION URL">
...
</SP>
</secure-deployment>
</subsystem>
</profile>
]]>
</programlisting>
</para>
<para>
The <literal>secure-deployment</literal> <literal>name</literal> attribute identifies the WAR you want
to secure. Its value is the <literal>module-name</literal> defined in <literal>web.xml</literal> with
<literal>.war</literal> appended. The rest of the configuration uses the same XML syntax as
<literal>keycloak-saml.xml</literal> configuration defined in <link linkend='adapter-config'>general adapter configuration</link>.
</para>
<para>
An example configuration:
</para>
<para>
<programlisting><![CDATA[
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
<secure-deployment name="saml-post-encryption.war">
<SP entityID="http://localhost:8080/sales-post-enc/"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
logoutPage="/logout.jsp"
forceAuthentication="false">
<Keys>
<Key signing="true" encryption="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
</KeyStore>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
<RoleIdentifiers>
<Attribute name="Role"/>
</RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<Keys>
<Key signing="true" >
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="saml-demo"/>
</KeyStore>
</Key>
</Keys>
</IDP>
</SP>
</secure-deployment>
</subsystem>
]]>
</programlisting>
</para>
</section>
</chapter>

View file

@ -1048,7 +1048,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-subsystem</artifactId>
<artifactId>keycloak-saml-wildfly-subsystem</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
@ -1143,7 +1143,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-modules</artifactId>
<artifactId>keycloak-saml-wildfly-modules</artifactId>
<version>${project.version}</version>
<type>zip</type>
</dependency>
@ -1155,7 +1155,7 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId>
<artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<version>${project.version}</version>
<type>zip</type>
</dependency>

View file

@ -30,10 +30,6 @@
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-adapter-core</artifactId>
</dependency>
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-jdk15on</artifactId>

View file

@ -101,9 +101,9 @@ projects that depend on this project.-->
</dependency>
<dependency>
<groupId>org.jboss.msc</groupId>
<artifactId>jboss-msc</artifactId>
<version>1.0.2.GA</version>
<groupId>org.jboss.as</groupId>
<artifactId>jboss-as-controller</artifactId>
<version>${jboss.version}</version>
</dependency>
<dependency>

View file

@ -0,0 +1,60 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class Configuration {
static final Configuration INSTANCE = new Configuration();
private ModelNode config = new ModelNode();
private Configuration() {
}
void updateModel(ModelNode operation, ModelNode model) {
ModelNode node = config;
ModelNode addr = operation.get("address");
for (Property item : addr.asPropertyList()) {
node = getNodeForAddressElement(node, item);
}
node.set(model);
}
private ModelNode getNodeForAddressElement(ModelNode node, Property item) {
String key = item.getValue().asString();
ModelNode keymodel = node.get(item.getName());
return keymodel.get(key);
}
public ModelNode getSecureDeployment(String name) {
ModelNode secureDeployment = config.get("subsystem").get("keycloak-saml").get(Constants.Model.SECURE_DEPLOYMENT);
if (secureDeployment.hasDefined(name)) {
return secureDeployment.get(name);
}
return null;
}
public boolean isSecureDeployment(String name) {
return getSecureDeployment(name) != null;
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class Constants {
static class Model {
static final String SECURE_DEPLOYMENT = "secure-deployment";
static final String SERVICE_PROVIDER = "service-provider";
static final String SSL_POLICY = "ssl-policy";
static final String NAME_ID_POLICY_FORMAT = "name-id-policy-format";
static final String LOGOUT_PAGE = "logout-page";
static final String FORCE_AUTHENTICATION = "force-authentication";
static final String ROLE_ATTRIBUTES = "role-attributes";
static final String SIGNING = "signing";
static final String ENCRYPTION = "encryption";
static final String KEY = "key";
static final String RESOURCE = "resource";
static final String PASSWORD = "password";
static final String PRIVATE_KEY_ALIAS = "private-key-alias";
static final String PRIVATE_KEY_PASSWORD = "private-key-password";
static final String CERTIFICATE_ALIAS = "certificate-alias";
static final String KEY_STORE = "key-store";
static final String SIGN_REQUEST = "sign-request";
static final String VALIDATE_RESPONSE_SIGNATURE = "validate-response-signature";
static final String REQUEST_BINDING = "request-binding";
static final String BINDING_URL = "binding-url";
static final String VALIDATE_REQUEST_SIGNATURE = "validate-request-signature";
static final String SIGN_RESPONSE = "sign-response";
static final String RESPONSE_BINDING = "response-binding";
static final String POST_BINDING_URL = "post-binding-url";
static final String REDIRECT_BINDING_URL = "redirect-binding-url";
static final String SINGLE_SIGN_ON = "single-sign-on";
static final String SINGLE_LOGOUT = "single-logout";
static final String IDENTITY_PROVIDER = "identity-provider";
static final String PRINCIPAL_NAME_MAPPING_POLICY = "principal-name-mapping-policy";
static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "principal-name-mapping-attribute-name";
static final String SIGNATURE_ALGORITHM = "signature-algorithm";
static final String SIGNATURE_CANONICALIZATION_METHOD = "signature-canonicalization-method";
static final String PRIVATE_KEY_PEM = "private-key-pem";
static final String PUBLIC_KEY_PEM = "public-key-pem";
static final String CERTIFICATE_PEM = "certificate-pem";
static final String TYPE = "type";
static final String ALIAS = "alias";
static final String FILE = "file";
static final String SIGNATURES_REQUIRED = "signatures-required";
}
static class XML {
static final String SECURE_DEPLOYMENT = "secure-deployment";
static final String SERVICE_PROVIDER = "SP";
static final String NAME = "name";
static final String ENTITY_ID = "entityID";
static final String SSL_POLICY = "sslPolicy";
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
static final String LOGOUT_PAGE = "logoutPage";
static final String FORCE_AUTHENTICATION = "forceAuthentication";
static final String ROLE_IDENTIFIERS = "RoleIdentifiers";
static final String SIGNING = "signing";
static final String ENCRYPTION = "encryption";
static final String KEYS = "Keys";
static final String KEY = "Key";
static final String RESOURCE = "resource";
static final String PASSWORD = "password";
static final String KEY_STORE = "KeyStore";
static final String PRIVATE_KEY = "PrivateKey";
static final String CERTIFICATE = "Certificate";
static final String PRIVATE_KEY_ALIAS = "alias";
static final String PRIVATE_KEY_PASSWORD = "password";
static final String CERTIFICATE_ALIAS = "alias";
static final String SIGN_REQUEST = "signRequest";
static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature";
static final String REQUEST_BINDING = "requestBinding";
static final String BINDING_URL = "bindingUrl";
static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature";
static final String SIGN_RESPONSE = "signResponse";
static final String RESPONSE_BINDING = "responseBinding";
static final String POST_BINDING_URL = "postBindingUrl";
static final String REDIRECT_BINDING_URL = "redirectBindingUrl";
static final String SINGLE_SIGN_ON = "SingleSignOnService";
static final String SINGLE_LOGOUT = "SingleLogoutService";
static final String IDENTITY_PROVIDER = "IDP";
static final String PRINCIPAL_NAME_MAPPING = "PrincipalNameMapping";
static final String PRINCIPAL_NAME_MAPPING_POLICY = "policy";
static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "attribute";
static final String ATTRIBUTE = "Attribute";
static final String SIGNATURE_ALGORITHM = "signatureAlgorithm";
static final String SIGNATURE_CANONICALIZATION_METHOD = "signatureCanonicalizationMethod";
static final String PRIVATE_KEY_PEM = "PrivateKeyPem";
static final String PUBLIC_KEY_PEM = "PublicKeyPem";
static final String CERTIFICATE_PEM = "CertificatePem";
static final String TYPE = "type";
static final String ALIAS = "alias";
static final String FILE = "file";
static final String SIGNATURES_REQUIRED = "signaturesRequired";
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class IdentityProviderAddHandler extends AbstractAddStepHandler {
IdentityProviderAddHandler() {
super(IdentityProviderDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class IdentityProviderDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SIGNATURES_REQUIRED =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURES_REQUIRED, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGNATURES_REQUIRED)
.build();
static final SimpleAttributeDefinition SIGNATURE_ALGORITHM =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_ALGORITHM, ModelType.STRING, true)
.setXmlName(Constants.XML.SIGNATURE_ALGORITHM)
.build();
static final SimpleAttributeDefinition SIGNATURE_CANONICALIZATION_METHOD =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_CANONICALIZATION_METHOD, ModelType.STRING, true)
.setXmlName(Constants.XML.SIGNATURE_CANONICALIZATION_METHOD)
.build();
static final ObjectTypeAttributeDefinition SINGLE_SIGN_ON =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_SIGN_ON,
SingleSignOnDefinition.ATTRIBUTES)
.setAllowNull(false)
.build();
static final ObjectTypeAttributeDefinition SINGLE_LOGOUT =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_LOGOUT,
SingleLogoutDefinition.ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD};
static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD, SINGLE_SIGN_ON, SINGLE_LOGOUT};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static final IdentityProviderDefinition INSTANCE = new IdentityProviderDefinition();
private IdentityProviderDefinition() {
super(PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.IDENTITY_PROVIDER),
new IdentityProviderAddHandler(),
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,41 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class KeyAddHandler extends AbstractAddStepHandler {
KeyAddHandler() {
super(KeyDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SIGNING =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNING, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGNING)
.build();
static final SimpleAttributeDefinition ENCRYPTION =
new SimpleAttributeDefinitionBuilder(Constants.Model.ENCRYPTION, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.ENCRYPTION)
.build();
static final SimpleAttributeDefinition PRIVATE_KEY_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_PEM)
.build();
static final SimpleAttributeDefinition PUBLIC_KEY_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.PUBLIC_KEY_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.PUBLIC_KEY_PEM)
.build();
static final SimpleAttributeDefinition CERTIFICATE_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.CERTIFICATE_PEM)
.build();
static final ObjectTypeAttributeDefinition KEY_STORE =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.KEY_STORE,
KeyStoreDefinition.ALL_ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNING, ENCRYPTION};
static final SimpleAttributeDefinition[] ELEMENTS = {PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM};
static final AttributeDefinition[] ALL_ATTRIBUTES = {SIGNING, ENCRYPTION, PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM, KEY_STORE};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static final HashMap<String, SimpleAttributeDefinition> ELEMENT_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ELEMENTS) {
ELEMENT_MAP.put(def.getXmlName(), def);
}
}
static final KeyDefinition INSTANCE = new KeyDefinition();
private KeyDefinition() {
super(PathElement.pathElement(Constants.Model.KEY),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.KEY),
new KeyAddHandler(),
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
static SimpleAttributeDefinition lookupElement(String xmlName) {
return ELEMENT_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyStoreCertificateDefinition {
static final SimpleAttributeDefinition CERTIFICATE_ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.CERTIFICATE_ALIAS)
.build();
static SimpleAttributeDefinition lookup(String xmlName) {
return Constants.XML.CERTIFICATE_ALIAS.equals(xmlName) ? CERTIFICATE_ALIAS : null;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class KeyStoreDefinition {
static final SimpleAttributeDefinition RESOURCE =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESOURCE, ModelType.STRING, true)
.setXmlName(Constants.XML.RESOURCE)
.build();
static final SimpleAttributeDefinition PASSWORD =
new SimpleAttributeDefinitionBuilder(Constants.Model.PASSWORD, ModelType.STRING, true)
.setXmlName(Constants.XML.PASSWORD)
.build();
static final SimpleAttributeDefinition FILE =
new SimpleAttributeDefinitionBuilder(Constants.Model.FILE, ModelType.STRING, true)
.setXmlName(Constants.XML.FILE)
.build();
static final SimpleAttributeDefinition TYPE =
new SimpleAttributeDefinitionBuilder(Constants.Model.TYPE, ModelType.STRING, true)
.setXmlName(Constants.XML.TYPE)
.build();
static final SimpleAttributeDefinition ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.ALIAS)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS};
static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS,
KeyStorePrivateKeyDefinition.PRIVATE_KEY_ALIAS,
KeyStorePrivateKeyDefinition.PRIVATE_KEY_PASSWORD,
KeyStoreCertificateDefinition.CERTIFICATE_ALIAS
};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyStorePrivateKeyDefinition {
static final SimpleAttributeDefinition PRIVATE_KEY_ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_ALIAS)
.build();
static final SimpleAttributeDefinition PRIVATE_KEY_PASSWORD =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PASSWORD, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_PASSWORD)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASSWORD};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -21,13 +21,22 @@ import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.web.deployment.WarMetaData;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.jboss.ValveMetaData;
import org.jboss.metadata.web.spec.LoginConfigMetaData;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import org.keycloak.adapters.saml.AdapterConstants;
import org.keycloak.adapters.saml.jbossweb.SamlAuthenticatorValve;
import org.keycloak.subsystem.saml.as7.logging.KeycloakLogger;
import org.keycloak.subsystem.saml.as7.xml.FormattingXMLStreamWriter;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@ -39,22 +48,16 @@ import java.util.List;
public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class);
// This param name is defined again in Keycloak Undertow Integration class
// org.keycloak.adapters.undertow.KeycloakServletExtension. We have this value in
// two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.saml.adapterConfig";
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
// if it's not a web-app there's nothing to secure
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
return;
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
@ -64,14 +67,68 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// otherwise
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod());
try {
boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod());
boolean hasSubsystemConfig = Configuration.INSTANCE.isSecureDeployment(deploymentName);
if (hasSubsystemConfig || webRequiresKC) {
log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName);
if (webRequiresKC) {
log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName);
addValve(webMetaData);
// if secure-deployment configuration exists for web app, we force KEYCLOAK-SAML auth method on it
if (hasSubsystemConfig) {
addXMLData(getXML(deploymentName), warMetaData);
if (loginConfig != null) {
loginConfig.setAuthMethod("KEYCLOAK-SAML");
//loginConfig.setRealmName(service.getRealmName(deploymentName));
} else {
log.warn("Failed to set up KEYCLOAK-SAML auth method for WAR: " + deploymentName + " (loginConfig == null)");
}
}
addValve(webMetaData);
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
} catch (Exception e) {
throw new DeploymentUnitProcessingException("Failed to configure KeycloakSamlExtension from subsystem model", e);
}
}
private String getXML(String deploymentName) throws XMLStreamException {
ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentName);
if (node != null) {
KeycloakSubsystemParser writer = new KeycloakSubsystemParser();
ByteArrayOutputStream output = new ByteArrayOutputStream();
XMLExtendedStreamWriter streamWriter = new FormattingXMLStreamWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(output));
try {
streamWriter.writeStartElement("keycloak-saml-adapter");
writer.writeSps(streamWriter, node);
streamWriter.writeEndElement();
} finally {
streamWriter.close();
}
return new String(output.toByteArray(), Charset.forName("utf-8"));
}
return null;
}
private void addXMLData(String xml, WarMetaData warMetaData) {
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
warMetaData.setMergedJBossWebMetaData(webMetaData);
}
List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
if (contextParams == null) {
contextParams = new ArrayList<>();
}
ParamValueMetaData param = new ParamValueMetaData();
param.setParamName(AdapterConstants.AUTH_DATA_PARAM_NAME);
param.setParamValue(xml);
contextParams.add(param);
webMetaData.setContextParams(contextParams);
}
private void addValve(JBossWebMetaData webMetaData) {
List<ValveMetaData> valves = webMetaData.getValves();
if (valves == null) {
@ -89,5 +146,4 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
public void undeploy(DeploymentUnit du) {
}
}

View file

@ -18,6 +18,7 @@ package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.Extension;
import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ResourceDefinition;
import org.jboss.as.controller.SubsystemRegistration;
@ -36,15 +37,12 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUB
public class KeycloakSamlExtension implements Extension {
public static final String SUBSYSTEM_NAME = "keycloak-saml";
public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6";
public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.1";
private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions";
private static final int MGMT_API_VERSION_MAJOR = 1;
private static final int MGMT_API_VERSION_MINOR = 1;
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1, 1, 0);
static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@ -67,10 +65,15 @@ public class KeycloakSamlExtension implements Extension {
*/
@Override
public void initialize(final ExtensionContext context) {
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION_MAJOR, MGMT_API_VERSION_MINOR);
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME,
MGMT_API_VERSION.getMajor(), MGMT_API_VERSION.getMinor(), MGMT_API_VERSION.getMicro());
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KeycloakSubsystemDefinition.INSTANCE);
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SecureDeploymentDefinition.INSTANCE);
ManagementResourceRegistration serviceProviderRegistration = secureDeploymentRegistration.registerSubModel(ServiceProviderDefinition.INSTANCE);
serviceProviderRegistration.registerSubModel(KeyDefinition.INSTANCE);
ManagementResourceRegistration idpRegistration = serviceProviderRegistration.registerSubModel(IdentityProviderDefinition.INSTANCE);
idpRegistration.registerSubModel(KeyDefinition.INSTANCE);
subsystem.registerXMLElementWriter(PARSER);
}
}

View file

@ -16,13 +16,12 @@
*/
package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.as.server.AbstractDeploymentChainStep;
import org.jboss.as.server.DeploymentProcessorTarget;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.server.deployment.Phase;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
@ -43,17 +42,20 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
context.addStep(new AbstractDeploymentChainStep() {
@Override
protected void execute(DeploymentProcessorTarget processorTarget) {
processorTarget.addDeploymentProcessor(Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessorAS7());
processorTarget.addDeploymentProcessor(
processorTarget.addDeploymentProcessor(KeycloakSamlExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 0, chooseDependencyProcessor());
processorTarget.addDeploymentProcessor(KeycloakSamlExtension.SUBSYSTEM_NAME,
Phase.POST_MODULE, // PHASE
Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
new KeycloakAdapterConfigDeploymentProcessor());
chooseConfigDeploymentProcessor());
}
}, OperationContext.Stage.RUNTIME);
}
@Override
protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
model.setEmptyObject();
private DeploymentUnitProcessor chooseDependencyProcessor() {
return new KeycloakDependencyProcessorAS7();
}
private DeploymentUnitProcessor chooseConfigDeploymentProcessor() {
return new KeycloakAdapterConfigDeploymentProcessor();
}
}

View file

@ -14,23 +14,23 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
/**
* Definition of subsystem=keycloak.
* Definition of subsystem=keycloak-saml.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
protected KeycloakSubsystemDefinition() {
static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition();
private KeycloakSubsystemDefinition() {
super(KeycloakSamlExtension.SUBSYSTEM_PATH,
KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE,
@ -41,7 +41,6 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(ModelDescriptionConstants.DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
}

View file

@ -1,5 +1,5 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
@ -17,9 +17,14 @@
package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
@ -27,6 +32,10 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
/**
@ -41,10 +50,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
// Require no attributes
ParseUtils.requireNoAttributes(reader);
ModelNode addKeycloakSub = org.jboss.as.controller.operations.common.Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM));
ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM));
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (reader.getLocalName().equals(Constants.XML.SECURE_DEPLOYMENT)) {
readSecureDeployment(reader, list);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
@ -53,6 +67,319 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
return reader.nextTag();
}
void readSecureDeployment(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
String name = readRequiredAttribute(reader, Constants.XML.NAME);
PathAddress addr = PathAddress.pathAddress(
PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakSamlExtension.SUBSYSTEM_NAME),
PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT, name));
ModelNode addSecureDeployment = Util.createAddOperation(addr);
list.add(addSecureDeployment);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (tagName.equals(Constants.XML.SERVICE_PROVIDER)) {
readServiceProvider(reader, list, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readServiceProvider(XMLExtendedStreamReader reader, List<ModelNode> list, PathAddress parentAddr) throws XMLStreamException {
String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID);
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.SERVICE_PROVIDER, entityId));
ModelNode addServiceProvider = Util.createAddOperation(addr);
list.add(addServiceProvider);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
if (Constants.XML.ENTITY_ID.equals(name)) {
continue;
}
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = ServiceProviderDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addServiceProvider, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.KEYS.equals(tagName)) {
readKeys(list, reader, addr);
} else if (Constants.XML.PRINCIPAL_NAME_MAPPING.equals(tagName)) {
readPrincipalNameMapping(addServiceProvider, reader);
} else if (Constants.XML.ROLE_IDENTIFIERS.equals(tagName)) {
readRoleIdentifiers(addServiceProvider, reader);
} else if (Constants.XML.IDENTITY_PROVIDER.equals(tagName)) {
readIdentityProvider(list, reader, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readIdentityProvider(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID);
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER, entityId));
ModelNode addIdentityProvider = Util.createAddOperation(addr);
list.add(addIdentityProvider);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
if (Constants.XML.ENTITY_ID.equals(name)
// don't break if encountering this noop attr from client-adapter/core keycloak_saml_adapter_1_6.xsd
|| "encryption".equals(name)) {
continue;
}
SimpleAttributeDefinition attr = IdentityProviderDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addIdentityProvider, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.SINGLE_SIGN_ON.equals(tagName)) {
readSingleSignOn(addIdentityProvider, reader);
} else if (Constants.XML.SINGLE_LOGOUT.equals(tagName)) {
readSingleLogout(addIdentityProvider, reader);
} else if (Constants.XML.KEYS.equals(tagName)) {
readKeys(list, reader, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readSingleSignOn(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode sso = addIdentityProvider.get(Constants.Model.SINGLE_SIGN_ON);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = SingleSignOnDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, sso, reader);
}
ParseUtils.requireNoContent(reader);
}
void readSingleLogout(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode slo = addIdentityProvider.get(Constants.Model.SINGLE_LOGOUT);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = SingleLogoutDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, slo, reader);
}
ParseUtils.requireNoContent(reader);
}
void readKeys(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
List<ModelNode> keyList = new LinkedList<>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (!Constants.XML.KEY.equals(tagName)) {
throw ParseUtils.unexpectedElement(reader);
}
readKey(keyList, reader, parentAddr);
}
list.addAll(keyList);
}
void readKey(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.KEY, "key-" + list.size()));
ModelNode addKey = Util.createAddOperation(addr);
list.add(addKey);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKey, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.KEY_STORE.equals(tagName)) {
readKeyStore(addKey, reader);
} else if (Constants.XML.PRIVATE_KEY_PEM.equals(tagName)
|| Constants.XML.PUBLIC_KEY_PEM.equals(tagName)
|| Constants.XML.CERTIFICATE_PEM.equals(tagName)) {
readNoAttrElementContent(KeyDefinition.lookupElement(tagName), addKey, reader);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readNoAttrElementContent(SimpleAttributeDefinition attr, ModelNode model, XMLExtendedStreamReader reader) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
String value = reader.getElementText();
attr.parseAndSetParameter(value, model, reader);
}
void readKeyStore(ModelNode addKey, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode addKeyStore = addKey.get(Constants.Model.KEY_STORE);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStoreDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.FILE) && !addKeyStore.hasDefined(Constants.Model.RESOURCE)) {
throw new XMLStreamException("KeyStore element must have 'file' or 'resource' attribute set", reader.getLocation());
}
if (!addKeyStore.hasDefined(Constants.Model.PASSWORD)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PASSWORD);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.PRIVATE_KEY.equals(tagName)) {
readPrivateKey(reader, addKeyStore);
} else if (Constants.XML.CERTIFICATE.equals(tagName)) {
readCertificate(reader, addKeyStore);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readPrivateKey(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStorePrivateKeyDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_ALIAS)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_ALIAS);
}
if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_PASSWORD)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_PASSWORD);
}
ParseUtils.requireNoContent(reader);
}
void readCertificate(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.CERTIFICATE_ALIAS)) {
throw ParseUtils.missingRequired(reader, Constants.XML.CERTIFICATE_ALIAS);
}
ParseUtils.requireNoContent(reader);
}
void readRoleIdentifiers(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (!Constants.XML.ATTRIBUTE.equals(tagName)) {
throw ParseUtils.unexpectedElement(reader);
}
ParseUtils.requireSingleAttribute(reader, Constants.XML.NAME);
String name = ParseUtils.readStringAttributeElement(reader, Constants.XML.NAME);
ServiceProviderDefinition.ROLE_ATTRIBUTES.parseAndAddParameterElement(name, addServiceProvider, reader);
}
}
void readPrincipalNameMapping(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
boolean policySet = false;
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
if (Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY.equals(name)) {
policySet = true;
ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_POLICY.parseAndSetParameter(value, addServiceProvider, reader);
} else if (Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.equals(name)) {
ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.parseAndSetParameter(value, addServiceProvider, reader);
} else {
throw ParseUtils.unexpectedAttribute(reader, i);
}
}
if (!policySet) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY);
}
ParseUtils.requireNoContent(reader);
}
/**
* Read an attribute, and throw exception if attribute is not present
*/
String readRequiredAttribute(XMLExtendedStreamReader reader, String attrName) throws XMLStreamException {
String value = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
String attr = reader.getAttributeLocalName(i);
if (attr.equals(attrName)) {
value = reader.getAttributeValue(i);
break;
}
}
if (value == null) {
throw ParseUtils.missingRequired(reader, Collections.singleton(attrName));
}
return value;
}
/**
* {@inheritDoc}
@ -60,8 +387,183 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
@Override
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false);
writeSecureDeployment(writer, context.getModelNode());
writer.writeEndElement();
}
public void writeSecureDeployment(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.get(Constants.Model.SECURE_DEPLOYMENT).isDefined()) {
return;
}
for (Property sp : model.get(Constants.Model.SECURE_DEPLOYMENT).asPropertyList()) {
writer.writeStartElement(Constants.XML.SECURE_DEPLOYMENT);
writer.writeAttribute(Constants.XML.NAME, sp.getName());
writeSps(writer, sp.getValue());
writer.writeEndElement();
}
}
void writeSps(final XMLExtendedStreamWriter writer, final ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
for (Property sp : model.get(Constants.Model.SERVICE_PROVIDER).asPropertyList()) {
writer.writeStartElement(Constants.XML.SERVICE_PROVIDER);
writer.writeAttribute(Constants.XML.ENTITY_ID, sp.getName());
ModelNode spAttributes = sp.getValue();
for (SimpleAttributeDefinition attr : ServiceProviderDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, spAttributes, false, writer);
}
writeKeys(writer, spAttributes.get(Constants.Model.KEY));
writePrincipalNameMapping(writer, spAttributes);
writeRoleIdentifiers(writer, spAttributes);
writeIdentityProvider(writer, spAttributes.get(Constants.Model.IDENTITY_PROVIDER));
writer.writeEndElement();
}
}
void writeIdentityProvider(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
for (Property idp : model.asPropertyList()) {
writer.writeStartElement(Constants.XML.IDENTITY_PROVIDER);
writer.writeAttribute(Constants.XML.ENTITY_ID, idp.getName());
ModelNode idpAttributes = idp.getValue();
for (SimpleAttributeDefinition attr : IdentityProviderDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, idpAttributes, false, writer);
}
writeSingleSignOn(writer, idpAttributes.get(Constants.Model.SINGLE_SIGN_ON));
writeSingleLogout(writer, idpAttributes.get(Constants.Model.SINGLE_LOGOUT));
writeKeys(writer, idpAttributes.get(Constants.Model.KEY));
}
writer.writeEndElement();
}
void writeSingleSignOn(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.SINGLE_SIGN_ON);
for (SimpleAttributeDefinition attr : SingleSignOnDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeSingleLogout(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.SINGLE_LOGOUT);
for (SimpleAttributeDefinition attr : SingleLogoutDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeKeys(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
boolean contains = false;
for (Property key : model.asPropertyList()) {
if (!contains) {
writer.writeStartElement(Constants.XML.KEYS);
contains = true;
}
writer.writeStartElement(Constants.XML.KEY);
ModelNode keyAttributes = key.getValue();
for (SimpleAttributeDefinition attr : KeyDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, keyAttributes, false, writer);
}
for (SimpleAttributeDefinition attr : KeyDefinition.ELEMENTS) {
attr.getAttributeMarshaller().marshallAsElement(attr, keyAttributes, false, writer);
}
writeKeyStore(writer, keyAttributes.get(Constants.Model.KEY_STORE));
writer.writeEndElement();
}
if (contains) {
writer.writeEndElement();
}
}
void writeKeyStore(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.KEY_STORE);
for (SimpleAttributeDefinition attr : KeyStoreDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writePrivateKey(writer, model);
writeCertificate(writer, model);
writer.writeEndElement();
}
void writeCertificate(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode value = model.get(Constants.Model.CERTIFICATE_ALIAS);
if (!value.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.CERTIFICATE);
SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.CERTIFICATE_ALIAS;
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
writer.writeEndElement();
}
void writePrivateKey(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode pk_alias = model.get(Constants.Model.PRIVATE_KEY_ALIAS);
ModelNode pk_password = model.get(Constants.Model.PRIVATE_KEY_PASSWORD);
if (!pk_alias.isDefined() && !pk_password.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.PRIVATE_KEY);
for (SimpleAttributeDefinition attr : KeyStorePrivateKeyDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeRoleIdentifiers(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode value = model.get(Constants.Model.ROLE_ATTRIBUTES);
if (!value.isDefined()) {
return;
}
List<ModelNode> items = value.asList();
if (items.size() == 0) {
return;
}
writer.writeStartElement(Constants.XML.ROLE_IDENTIFIERS);
for (ModelNode item : items) {
writer.writeStartElement(Constants.XML.ATTRIBUTE);
writer.writeAttribute("name", item.asString());
writer.writeEndElement();
}
writer.writeEndElement();
}
void writePrincipalNameMapping(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
writer.writeStartElement(Constants.XML.PRINCIPAL_NAME_MAPPING);
ModelNode value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY);
if (value.isDefined()) {
writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY, value.asString());
}
value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME);
if (value.isDefined()) {
writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, value.asString());
}
writer.writeEndElement();
}
}

View file

@ -0,0 +1,42 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class SecureDeploymentAddHandler extends AbstractAddStepHandler {
static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
private SecureDeploymentAddHandler() {
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
/**
* Defines attributes and operations for a secure-deployment.
*/
public class SecureDeploymentDefinition extends SimpleResourceDefinition {
static final SecureDeploymentDefinition INSTANCE = new SecureDeploymentDefinition();
private SecureDeploymentDefinition() {
super(PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SECURE_DEPLOYMENT),
SecureDeploymentAddHandler.INSTANCE,
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.ServiceVerificationHandler;
import org.jboss.dmr.ModelNode;
import org.jboss.msc.service.ServiceController;
import java.util.List;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class ServiceProviderAddHandler extends AbstractAddStepHandler {
static final ServiceProviderAddHandler INSTANCE = new ServiceProviderAddHandler();
ServiceProviderAddHandler() {
super(ServiceProviderDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List<ServiceController<?>> newControllers) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ListAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.StringListAttributeDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class ServiceProviderDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SSL_POLICY =
new SimpleAttributeDefinitionBuilder(Constants.Model.SSL_POLICY, ModelType.STRING, true)
.setXmlName(Constants.XML.SSL_POLICY)
.build();
static final SimpleAttributeDefinition NAME_ID_POLICY_FORMAT =
new SimpleAttributeDefinitionBuilder(Constants.Model.NAME_ID_POLICY_FORMAT, ModelType.STRING, true)
.setXmlName(Constants.XML.NAME_ID_POLICY_FORMAT)
.build();
static final SimpleAttributeDefinition LOGOUT_PAGE =
new SimpleAttributeDefinitionBuilder(Constants.Model.LOGOUT_PAGE, ModelType.STRING, true)
.setXmlName(Constants.XML.LOGOUT_PAGE)
.build();
static final SimpleAttributeDefinition FORCE_AUTHENTICATION =
new SimpleAttributeDefinitionBuilder(Constants.Model.FORCE_AUTHENTICATION, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.FORCE_AUTHENTICATION)
.build();
static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_POLICY =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY, ModelType.STRING, true)
.setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY)
.build();
static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ModelType.STRING, true)
.setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME)
.build();
static final ListAttributeDefinition ROLE_ATTRIBUTES =
new StringListAttributeDefinition.Builder(Constants.Model.ROLE_ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION};
static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static final HashMap<String, AttributeDefinition> ALL_MAP = new HashMap<>();
static final Collection<AttributeDefinition> ALL_ATTRIBUTES;
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
ALL_MAP.putAll(ATTRIBUTE_MAP);
for (AttributeDefinition def : ELEMENTS) {
ALL_MAP.put(def.getXmlName(), def);
}
ALL_ATTRIBUTES = Collections.unmodifiableCollection(ALL_MAP.values());
}
static final ServiceProviderDefinition INSTANCE = new ServiceProviderDefinition();
private ServiceProviderDefinition() {
super(PathElement.pathElement(Constants.Model.SERVICE_PROVIDER),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SERVICE_PROVIDER),
ServiceProviderAddHandler.INSTANCE,
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class SingleLogoutDefinition {
static final SimpleAttributeDefinition VALIDATE_REQUEST_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_REQUEST_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_REQUEST_SIGNATURE)
.build();
static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE)
.build();
static final SimpleAttributeDefinition SIGN_REQUEST =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_REQUEST)
.build();
static final SimpleAttributeDefinition SIGN_RESPONSE =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_RESPONSE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_RESPONSE)
.build();
static final SimpleAttributeDefinition REQUEST_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.REQUEST_BINDING)
.build();
static final SimpleAttributeDefinition RESPONSE_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.RESPONSE_BINDING)
.build();
static final SimpleAttributeDefinition POST_BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.POST_BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.POST_BINDING_URL)
.build();
static final SimpleAttributeDefinition REDIRECT_BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.REDIRECT_BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.REDIRECT_BINDING_URL)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {VALIDATE_REQUEST_SIGNATURE, VALIDATE_RESPONSE_SIGNATURE,
SIGN_REQUEST, SIGN_RESPONSE, REQUEST_BINDING, RESPONSE_BINDING, POST_BINDING_URL, REDIRECT_BINDING_URL};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class SingleSignOnDefinition {
static final SimpleAttributeDefinition SIGN_REQUEST =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_REQUEST)
.build();
static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE)
.build();
static final SimpleAttributeDefinition REQUEST_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.REQUEST_BINDING)
.build();
static final SimpleAttributeDefinition RESPONSE_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.RESPONSE_BINDING)
.build();
static final SimpleAttributeDefinition BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.BINDING_URL)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,49 @@
/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7.logging;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.LogMessage;
import org.jboss.logging.Logger;
import org.jboss.logging.Message;
import org.jboss.logging.MessageLogger;
import static org.jboss.logging.Logger.Level.DEBUG;
import static org.jboss.logging.Logger.Level.INFO;
/**
* This interface to be fleshed out later when error messages are fully externalized.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
@MessageLogger(projectCode = "KEYCLOAK")
public interface KeycloakLogger extends BasicLogger {
/**
* A logger with a category of the package name.
*/
KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak");
@LogMessage(level = INFO)
@Message(value = "Keycloak SAML subsystem override for deployment %s")
void deploymentSecured(String deployment);
@LogMessage(level = DEBUG)
@Message(value = "Keycloak SAML has overriden and secured deployment %s")
void warSecured(String deployment);
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.saml.as7.logging;
import org.jboss.logging.MessageBundle;
import org.jboss.logging.Messages;
/**
* This interface to be fleshed out later when error messages are fully externalized.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc.
*/
@MessageBundle(projectCode = "TLIP")
public interface KeycloakMessages {
/**
* The messages
*/
KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class);
}

View file

@ -0,0 +1,534 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.subsystem.saml.as7.xml;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayDeque;
import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamWriter;
/**
* An XML stream writer which nicely formats the XML for configuration files.
*
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter, XMLStreamConstants {
private static final String NO_NAMESPACE = new String();
private final XMLStreamWriter delegate;
private final ArrayDeque<ArgRunnable> attrQueue = new ArrayDeque<ArgRunnable>();
private int level;
private int state = START_DOCUMENT;
private boolean indentEndElement = false;
private ArrayDeque<String> unspecifiedNamespaces = new ArrayDeque<String>();
public FormattingXMLStreamWriter(final XMLStreamWriter delegate) {
this.delegate = delegate;
unspecifiedNamespaces.push(NO_NAMESPACE);
}
private void nl() throws XMLStreamException {
delegate.writeCharacters("\n");
}
private void indent() throws XMLStreamException {
int level = this.level;
final XMLStreamWriter delegate = this.delegate;
for (int i = 0; i < level; i ++) {
delegate.writeCharacters(" ");
}
}
private interface ArgRunnable {
public void run(int arg) throws XMLStreamException;
}
@Override
public void setUnspecifiedElementNamespace(final String namespace) {
ArrayDeque<String> namespaces = this.unspecifiedNamespaces;
namespaces.pop();
namespaces.push(namespace == null ? NO_NAMESPACE : namespace);
}
private String nestUnspecifiedNamespace() {
ArrayDeque<String> namespaces = unspecifiedNamespaces;
String clone = namespaces.getFirst();
namespaces.push(clone);
return clone;
}
@Override
public void writeStartElement(final String localName) throws XMLStreamException {
ArrayDeque<String> namespaces = unspecifiedNamespaces;
String namespace = namespaces.getFirst();
if (namespace != NO_NAMESPACE) {
writeStartElement(namespace, localName);
return;
}
unspecifiedNamespaces.push(namespace);
// If this is a nested element flush the outer
runAttrQueue();
nl();
indent();
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
if (arg == 0) {
delegate.writeStartElement(localName);
} else {
delegate.writeEmptyElement(localName);
}
}
});
level++;
state = START_ELEMENT;
indentEndElement = false;
}
@Override
public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException {
nestUnspecifiedNamespace();
// If this is a nested element flush the outer
runAttrQueue();
nl();
indent();
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
if (arg == 0) {
delegate.writeStartElement(namespaceURI, localName);
} else {
delegate.writeEmptyElement(namespaceURI, localName);
}
}
});
level++;
state = START_ELEMENT;
indentEndElement = false;
}
@Override
public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
nestUnspecifiedNamespace();
// If this is a nested element flush the outer
runAttrQueue();
nl();
indent();
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
if (arg == 0) {
delegate.writeStartElement(prefix, namespaceURI, localName);
} else {
delegate.writeEmptyElement(prefix, namespaceURI, localName);
}
}
});
level++;
state = START_ELEMENT;
indentEndElement = false;
}
@Override
public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException {
runAttrQueue();
nl();
indent();
delegate.writeEmptyElement(namespaceURI, localName);
state = END_ELEMENT;
}
@Override
public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException {
runAttrQueue();
nl();
indent();
delegate.writeEmptyElement(prefix, namespaceURI, localName);
state = END_ELEMENT;
}
@Override
public void writeEmptyElement(final String localName) throws XMLStreamException {
String namespace = unspecifiedNamespaces.getFirst();
if (namespace != NO_NAMESPACE) {
writeEmptyElement(namespace, localName);
return;
}
runAttrQueue();
nl();
indent();
delegate.writeEmptyElement(localName);
state = END_ELEMENT;
}
@Override
public void writeEndElement() throws XMLStreamException {
level--;
if (state != START_ELEMENT) {
runAttrQueue();
if (state != CHARACTERS || indentEndElement) {
nl();
indent();
indentEndElement = false;
}
delegate.writeEndElement();
} else {
// Change the start element to an empty element
ArgRunnable start = attrQueue.poll();
if (start == null) {
delegate.writeEndElement();
} else {
start.run(1);
// Write everything else
runAttrQueue();
}
}
unspecifiedNamespaces.pop();
state = END_ELEMENT;
}
private void runAttrQueue() throws XMLStreamException {
ArgRunnable attr;
while ((attr = attrQueue.poll()) != null) {
attr.run(0);
}
}
@Override
public void writeEndDocument() throws XMLStreamException {
delegate.writeEndDocument();
state = END_DOCUMENT;
}
@Override
public void close() throws XMLStreamException {
delegate.close();
state = END_DOCUMENT;
}
@Override
public void flush() throws XMLStreamException {
delegate.flush();
}
@Override
public void writeAttribute(final String localName, final String value) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
try {
delegate.writeAttribute(localName, value);
} catch (XMLStreamException e) {
throw new UndeclaredThrowableException(e);
}
}
});
}
@Override
public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(prefix, namespaceURI, localName, value);
}
});
}
@Override
public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(namespaceURI, localName, value);
}
});
}
@Override
public void writeAttribute(final String localName, final String[] values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(localName, join(values));
}
});
}
@Override
public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String[] values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(prefix, namespaceURI, localName, join(values));
}
});
}
@Override
public void writeAttribute(final String namespaceURI, final String localName, final String[] values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(namespaceURI, localName, join(values));
}
});
}
@Override
public void writeAttribute(final String localName, final Iterable<String> values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(localName, join(values));
}
});
}
@Override
public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final Iterable<String> values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(prefix, namespaceURI, localName, join(values));
}
});
}
@Override
public void writeAttribute(final String namespaceURI, final String localName, final Iterable<String> values) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeAttribute(namespaceURI, localName, join(values));
}
});
}
@Override
public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeNamespace(prefix, namespaceURI);
}
});
}
@Override
public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException {
attrQueue.add(new ArgRunnable() {
public void run(int arg) throws XMLStreamException {
delegate.writeDefaultNamespace(namespaceURI);
}
});
}
@Override
public void writeComment(final String data) throws XMLStreamException {
runAttrQueue();
nl();
nl();
indent();
final StringBuilder b = new StringBuilder(data.length());
final Iterator<String> i = Spliterator.over(data, '\n');
if (! i.hasNext()) {
return;
} else {
final String first = i.next();
if (! i.hasNext()) {
delegate.writeComment(" " + first + " ");
state = COMMENT;
return;
} else {
b.append('\n');
for (int q = 0; q < level; q++) {
b.append(" ");
}
b.append(" ~ ");
b.append(first);
do {
b.append('\n');
for (int q = 0; q < level; q++) {
b.append(" ");
}
b.append(" ~ ");
b.append(i.next());
} while (i.hasNext());
}
b.append('\n');
for (int q = 0; q < level; q ++) {
b.append(" ");
}
b.append(" ");
delegate.writeComment(b.toString());
state = COMMENT;
}
}
@Override
public void writeProcessingInstruction(final String target) throws XMLStreamException {
runAttrQueue();
nl();
indent();
delegate.writeProcessingInstruction(target);
state = PROCESSING_INSTRUCTION;
}
@Override
public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException {
runAttrQueue();
nl();
indent();
delegate.writeProcessingInstruction(target, data);
state = PROCESSING_INSTRUCTION;
}
@Override
public void writeCData(final String data) throws XMLStreamException {
runAttrQueue();
delegate.writeCData(data);
state = CDATA;
}
@Override
public void writeDTD(final String dtd) throws XMLStreamException {
nl();
indent();
delegate.writeDTD(dtd);
state = DTD;
}
@Override
public void writeEntityRef(final String name) throws XMLStreamException {
runAttrQueue();
delegate.writeEntityRef(name);
state = ENTITY_REFERENCE;
}
@Override
public void writeStartDocument() throws XMLStreamException {
delegate.writeStartDocument();
nl();
state = START_DOCUMENT;
}
@Override
public void writeStartDocument(final String version) throws XMLStreamException {
delegate.writeStartDocument(version);
nl();
state = START_DOCUMENT;
}
@Override
public void writeStartDocument(final String encoding, final String version) throws XMLStreamException {
delegate.writeStartDocument(encoding, version);
nl();
state = START_DOCUMENT;
}
@Override
public void writeCharacters(final String text) throws XMLStreamException {
runAttrQueue();
if (state != CHARACTERS) {
nl();
indent();
}
final Iterator<String> iterator = Spliterator.over(text, '\n');
while (iterator.hasNext()) {
final String t = iterator.next();
delegate.writeCharacters(t);
if (iterator.hasNext()) {
nl();
indent();
}
}
state = CHARACTERS;
indentEndElement = true;
}
@Override
public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException {
runAttrQueue();
delegate.writeCharacters(text, start, len);
state = CHARACTERS;
}
@Override
public String getPrefix(final String uri) throws XMLStreamException {
return delegate.getPrefix(uri);
}
@Override
public void setPrefix(final String prefix, final String uri) throws XMLStreamException {
delegate.setPrefix(prefix, uri);
}
@Override
public void setDefaultNamespace(final String uri) throws XMLStreamException {
runAttrQueue();
delegate.setDefaultNamespace(uri);
}
@Override
public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException {
delegate.setNamespaceContext(context);
}
@Override
public NamespaceContext getNamespaceContext() {
return delegate.getNamespaceContext();
}
@Override
public Object getProperty(final String name) throws IllegalArgumentException {
return delegate.getProperty(name);
}
private static String join(final String[] values) {
final StringBuilder b = new StringBuilder();
for (int i = 0, valuesLength = values.length; i < valuesLength; i++) {
final String s = values[i];
if (s != null) {
if (i > 0) {
b.append(' ');
}
b.append(s);
}
}
return b.toString();
}
private static String join(final Iterable<String> values) {
final StringBuilder b = new StringBuilder();
Iterator<String> iterator = values.iterator();
while (iterator.hasNext()) {
final String s = iterator.next();
if (s != null) {
b.append(s);
if (iterator.hasNext()) b.append(' ');
}
}
return b.toString();
}
}

View file

@ -0,0 +1,66 @@
/*
* JBoss, Home of Professional Open Source.
* Copyright 2010, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.subsystem.saml.as7.xml;
import java.util.Iterator;
import java.util.NoSuchElementException;
/**
* @author <a href="mailto:david.lloyd@redhat.com">David M. Lloyd</a>
*/
final class Spliterator implements Iterator<String> {
private final String subject;
private final char delimiter;
private int i;
Spliterator(final String subject, final char delimiter) {
this.subject = subject;
this.delimiter = delimiter;
i = 0;
}
static Spliterator over(String subject, char delimiter) {
return new Spliterator(subject, delimiter);
}
public boolean hasNext() {
return i != -1;
}
public String next() {
final int i = this.i;
if (i == -1) {
throw new NoSuchElementException();
}
int n = subject.indexOf(delimiter, i);
try {
return n == -1 ? subject.substring(i) : subject.substring(i, n);
} finally {
this.i = n == -1 ? -1 : n + 1;
}
}
public void remove() {
throw new UnsupportedOperationException();
}
}

View file

@ -0,0 +1,63 @@
keycloak-saml.subsystem=Keycloak adapter subsystem
keycloak-saml.subsystem.add=Operation Adds Keycloak adapter subsystem
keycloak-saml.subsystem.remove=Operation removes Keycloak adapter subsystem
keycloak-saml.subsystem.secure-deployment=A deployment secured by Keycloak.
keycloak-saml.secure-deployment=A deployment secured by Keycloak
keycloak-saml.secure-deployment.add=Add a deployment to be secured by Keycloak
keycloak-saml.secure-deployment.remove=Remove a deployment to be secured by Keycloak
keycloak-saml.secure-deployment.service-provider=A security provider configuration for secure deployment
keycloak-saml.service-provider=A security provider configuration for secure deployment
keycloak-saml.service-provider.add=Add a security provider configuration to deployment secured by Keycloak SAML
keycloak-saml.service-provider.remove=Remove a security provider definition from deployment secured by Keycloak SAML
keycloak-saml.service-provider.ssl-policy=SSL Policy to use
keycloak-saml.service-provider.name-id-policy-format=Name ID policy format URN
keycloak-saml.service-provider.logout-page=URI to a logout page
keycloak-saml.service-provider.force-authentication=Redirected unauthenticated request to a login page
keycloak-saml.service-provider.role-attributes=Role identifiers
keycloak-saml.service-provider.principal-name-mapping-policy=Principal name mapping policy
keycloak-saml.service-provider.principal-name-mapping-attribute-name=Principal name mapping attribute name
keycloak-saml.service-provider.key=A key definition
keycloak-saml.service-provider.identity-provider=Identity provider definition
keycloak-saml.key=A key configuration for service provider or identity provider
keycloak-saml.key.add=Add a key definition
keycloak-saml.key.remove=Remove a key definition
keycloak-saml.key.signing=Key can be used for signing
keycloak-saml.key.encryption=Key can be used for encryption
keycloak-saml.key.private-key-pem=Private key string in pem format
keycloak-saml.key.public-key-pem=Public key string in pem format
keycloak-saml.key.certificate-pem=Certificate key string in pem format
keycloak-saml.key.key-store=Key store definition
keycloak-saml.key.key-store.file=Key store filesystem path
keycloak-saml.key.key-store.resource=Key store resource URI
keycloak-saml.key.key-store.password=Key store password
keycloak-saml.key.key-store.type=Key store format
keycloak-saml.key.key-store.alias=Key alias
keycloak-saml.key.key-store.private-key-alias=Private key alias
keycloak-saml.key.key-store.private-key-password=Private key password
keycloak-saml.key.key-store.certificate-alias=Certificate alias
keycloak-saml.identity-provider=An identity provider configuration
keycloak-saml.identity-provider.add=Add an identity provider
keycloak-saml.identity-provider.remove=Remove an identity provider
keycloak-saml.identity-provider.signatures-required=Require signatures for single-sign-on and single-logout
keycloak-saml.identity-provider.signature-algorithm=Signature algorithm
keycloak-saml.identity-provider.signature-canonicalization-method=Signature canonicalization method
keycloak-saml.identity-provider.single-sign-on=Single sign-on configuration
keycloak-saml.identity-provider.single-sign-on.sign-request=Sign SSO requests
keycloak-saml.identity-provider.single-sign-on.validate-response-signature=Validate an SSO response signature
keycloak-saml.identity-provider.single-sign-on.request-binding=HTTP method to use for requests
keycloak-saml.identity-provider.single-sign-on.response-binding=HTTP method to use for responses
keycloak-saml.identity-provider.single-sign-on.binding-url=SSO endpoint URL
keycloak-saml.identity-provider.single-logout=Single logout configuration
keycloak-saml.identity-provider.single-logout.validate-request-signature=Validate a single-logout request signature
keycloak-saml.identity-provider.single-logout.validate-response-signature=Validate a single-logout response signature
keycloak-saml.identity-provider.single-logout.sign-request=Sign single-logout requests
keycloak-saml.identity-provider.single-logout.sign-response=Sign single-logout responses
keycloak-saml.identity-provider.single-logout.request-binding=HTTP method to use for request
keycloak-saml.identity-provider.single-logout.response-binding=HTTP method to use for response
keycloak-saml.identity-provider.single-logout.post-binding-url=Endpoint URL for posting
keycloak-saml.identity-provider.single-logout.redirect-binding-url=Endpoint URL for redirects
keycloak-saml.identity-provider.key=Key definition for identity provider

View file

@ -0,0 +1,268 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:jboss:domain:keycloak-saml:1.1"
xmlns="urn:jboss:domain:keycloak-saml:1.1"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.0">
<!-- The subsystem root element -->
<xs:element name="subsystem" type="subsystem-type"/>
<xs:complexType name="subsystem-type">
<xs:annotation>
<xs:documentation>
<![CDATA[
The Keycloak SAML adapter subsystem, used to register deployments managed by Keycloak SAML adapter
]]>
</xs:documentation>
</xs:annotation>
<xs:all>
<xs:element name="secure-deployment" minOccurs="0" type="secure-deployment-type"/>
</xs:all>
</xs:complexType>
<xs:complexType name="secure-deployment-type">
<xs:all>
<xs:element name="SP" minOccurs="1" maxOccurs="1" type="sp-type"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The name of the realm.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="sp-type">
<xs:all>
<xs:element name="Keys" minOccurs="0" maxOccurs="1" type="keys-type"/>
<xs:element name="PrincipalNameMapping" minOccurs="0" maxOccurs="1" type="principal-name-mapping-type"/>
<xs:element name="RoleIdentifiers" minOccurs="0" maxOccurs="1" type="role-identifiers-type"/>
<xs:element name="IDP" minOccurs="1" maxOccurs="1" type="identity-provider-type"/>
</xs:all>
<xs:attribute name="entityID" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The entity ID for SAML service provider</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="sslPolicy" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The ssl policy</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="nameIDPolicyFormat" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Name ID policy format URN</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="logoutPage" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>URI to a logout page</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="forceAuthentication" type="xs:boolean" use="required">
<xs:annotation>
<xs:documentation>Redirected unauthenticated request to a login page</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="identity-provider-type">
<xs:all minOccurs="1" maxOccurs="1">
<xs:element name="SingleSignOnService" minOccurs="1" maxOccurs="1" type="single-signon-type"/>
<xs:element name="SingleLogoutService" minOccurs="0" maxOccurs="1" type="single-logout-type"/>
<xs:element name="Keys" minOccurs="0" maxOccurs="1" type="keys-type"/>
</xs:all>
<xs:attribute name="entityID" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The entity ID for SAML service provider</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signaturesRequired" type="xs:boolean" use="required">
<xs:annotation>
<xs:documentation>Require signatures for single-sign-on and single-logout</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signatureAlgorithm" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Algorithm used for signatures</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Canonicalization method used for signatures</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="single-signon-type">
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign the SSO requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate the SSO response signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="requestBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="responseBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for response</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="bindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>SSO endpoint URL</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="single-logout-type">
<xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate a single-logout request signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate a single-logout response signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign single-logout requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signResponse" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign single-logout responses</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="requestBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for request</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="responseBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for response</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="postBindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Endpoint URL for posting</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="redirectBindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Endpoint URL for redirects</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="keys-type">
<xs:sequence>
<xs:element name="Key" minOccurs="1" maxOccurs="2" type="key-type"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="key-type">
<xs:all>
<xs:element name="KeyStore" minOccurs="0" maxOccurs="1" type="keystore-type"/>
<xs:element name="PrivateKeyPem" minOccurs="0" maxOccurs="1" type="xs:string"/>
<xs:element name="PublicKeyPem" minOccurs="0" maxOccurs="1" type="xs:string"/>
<xs:element name="CertificatePem" minOccurs="0" maxOccurs="1" type="xs:string"/>
</xs:all>
<xs:attribute name="signing" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Key can be used for signing</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="encryption" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Key can be used for encryption</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="keystore-type">
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element name="PrivateKey" minOccurs="0" maxOccurs="1" type="privatekey-type"/>
<xs:element name="Certificate" minOccurs="0" maxOccurs="1" type="certificate-type"/>
</xs:sequence>
<xs:attribute name="file" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store filesystem path</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="resource" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store resource URI</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Key store password</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="type" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store format</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="alias" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key alias</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="privatekey-type">
<xs:attribute name="alias" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Private key alias</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Private key password</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="certificate-type">
<xs:attribute name="alias" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Certificate alias</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="principal-name-mapping-type">
<xs:attribute name="policy" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Principal name mapping policy. Possible values: FROM_NAME_ID</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="attribute" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Name of the attribute to use for principal name mapping</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="role-identifiers-type">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Attribute" minOccurs="0" maxOccurs="unbounded" type="attribute-type"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="attribute-type">
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Role attribute</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:schema>

View file

@ -0,0 +1,49 @@
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
<secure-deployment name="my-app.war">
<SP entityID="http://localhost:8080/sales-post-enc/"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
logoutPage="/logout.jsp"
forceAuthentication="false">
<Keys>
<Key encryption="true" signing="true">
<PrivateKeyPem>my_key.pem</PrivateKeyPem>
<PublicKeyPem>my_key.pub</PublicKeyPem>
<CertificatePem>cert.cer</CertificatePem>
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
</KeyStore>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID"/>
<RoleIdentifiers>
<Attribute name="Role"/>
<Attribute name="Role2"/>
</RoleIdentifiers>
<IDP entityID="idp">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<Keys>
<Key signing="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="saml-demo"/>
</KeyStore>
</Key>
</Keys>
</IDP>
</SP>
</secure-deployment>
</subsystem>

View file

@ -5,5 +5,5 @@ package org.keycloak.adapters.saml;
* @version $Revision: 1 $
*/
public class AdapterConstants {
public static final String AUTH_DATA_PARAM_NAME="org.keycloak.auth.deployment.data";
public static final String AUTH_DATA_PARAM_NAME="org.keycloak.saml.xml.adapterConfig";
}

View file

@ -28,6 +28,7 @@ import io.undertow.servlet.api.DeploymentInfo;
import io.undertow.servlet.api.LoginConfig;
import io.undertow.servlet.api.ServletSessionConfig;
import org.jboss.logging.Logger;
import org.keycloak.adapters.saml.AdapterConstants;
import org.keycloak.adapters.saml.DefaultSamlDeployment;
import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment;
@ -38,6 +39,7 @@ import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import org.keycloak.saml.common.exceptions.ParsingException;
import javax.servlet.ServletContext;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
@ -64,8 +66,16 @@ public class SamlServletExtension implements ServletExtension {
return false;
}
private static InputStream getXMLFromServletContext(ServletContext servletContext) {
String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
if (json == null) {
return null;
}
return new ByteArrayInputStream(json.getBytes());
}
private static InputStream getConfigInputStream(ServletContext context) {
InputStream is = null;
InputStream is = getXMLFromServletContext(context);
if (is == null) {
String path = context.getInitParameter("keycloak.config.file");
if (path == null) {

View file

@ -15,6 +15,6 @@
<modules>
<module>wildfly-adapter</module>
<module>wildfly9-subsystem</module>
<module>wildfly-subsystem</module>
</modules>
</project>

View file

@ -24,8 +24,8 @@
<relativePath>../../../../pom.xml</relativePath>
</parent>
<artifactId>keycloak-saml-wf9-subsystem</artifactId>
<name>Keycloak Wildfly 9 SAML Adapter Subsystem</name>
<artifactId>keycloak-saml-wildfly-subsystem</artifactId>
<name>Keycloak Wildfly SAML Adapter Subsystem</name>
<description/>
<packaging>jar</packaging>

View file

@ -0,0 +1,56 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class Configuration {
static Configuration INSTANCE = new Configuration();
private ModelNode config = new ModelNode();
private Configuration() {
}
void updateModel(ModelNode operation, ModelNode model) {
ModelNode node = config;
ModelNode addr = operation.get("address");
for (Property item : addr.asPropertyList()) {
node = getNodeForAddressElement(node, item);
}
node.set(model);
}
private ModelNode getNodeForAddressElement(ModelNode node, Property item) {
String key = item.getValue().asString();
ModelNode keymodel = node.get(item.getName());
return keymodel.get(key);
}
public ModelNode getSecureDeployment(String name) {
ModelNode secureDeployment = config.get("subsystem").get("keycloak-saml").get(Constants.Model.SECURE_DEPLOYMENT);
if (secureDeployment.hasDefined(name)) {
return secureDeployment.get(name);
}
return null;
}
}

View file

@ -0,0 +1,120 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class Constants {
static class Model {
static final String SECURE_DEPLOYMENT = "secure-deployment";
static final String SERVICE_PROVIDER = "service-provider";
static final String SSL_POLICY = "ssl-policy";
static final String NAME_ID_POLICY_FORMAT = "name-id-policy-format";
static final String LOGOUT_PAGE = "logout-page";
static final String FORCE_AUTHENTICATION = "force-authentication";
static final String ROLE_ATTRIBUTES = "role-attributes";
static final String SIGNING = "signing";
static final String ENCRYPTION = "encryption";
static final String KEY = "key";
static final String RESOURCE = "resource";
static final String PASSWORD = "password";
static final String PRIVATE_KEY_ALIAS = "private-key-alias";
static final String PRIVATE_KEY_PASSWORD = "private-key-password";
static final String CERTIFICATE_ALIAS = "certificate-alias";
static final String KEY_STORE = "key-store";
static final String SIGN_REQUEST = "sign-request";
static final String VALIDATE_RESPONSE_SIGNATURE = "validate-response-signature";
static final String REQUEST_BINDING = "request-binding";
static final String BINDING_URL = "binding-url";
static final String VALIDATE_REQUEST_SIGNATURE = "validate-request-signature";
static final String SIGN_RESPONSE = "sign-response";
static final String RESPONSE_BINDING = "response-binding";
static final String POST_BINDING_URL = "post-binding-url";
static final String REDIRECT_BINDING_URL = "redirect-binding-url";
static final String SINGLE_SIGN_ON = "single-sign-on";
static final String SINGLE_LOGOUT = "single-logout";
static final String IDENTITY_PROVIDER = "identity-provider";
static final String PRINCIPAL_NAME_MAPPING_POLICY = "principal-name-mapping-policy";
static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "principal-name-mapping-attribute-name";
static final String SIGNATURE_ALGORITHM = "signature-algorithm";
static final String SIGNATURE_CANONICALIZATION_METHOD = "signature-canonicalization-method";
static final String PRIVATE_KEY_PEM = "private-key-pem";
static final String PUBLIC_KEY_PEM = "public-key-pem";
static final String CERTIFICATE_PEM = "certificate-pem";
static final String TYPE = "type";
static final String ALIAS = "alias";
static final String FILE = "file";
static final String SIGNATURES_REQUIRED = "signatures-required";
}
static class XML {
static final String SECURE_DEPLOYMENT = "secure-deployment";
static final String SERVICE_PROVIDER = "SP";
static final String NAME = "name";
static final String ENTITY_ID = "entityID";
static final String SSL_POLICY = "sslPolicy";
static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat";
static final String LOGOUT_PAGE = "logoutPage";
static final String FORCE_AUTHENTICATION = "forceAuthentication";
static final String ROLE_IDENTIFIERS = "RoleIdentifiers";
static final String SIGNING = "signing";
static final String ENCRYPTION = "encryption";
static final String KEYS = "Keys";
static final String KEY = "Key";
static final String RESOURCE = "resource";
static final String PASSWORD = "password";
static final String KEY_STORE = "KeyStore";
static final String PRIVATE_KEY = "PrivateKey";
static final String CERTIFICATE = "Certificate";
static final String PRIVATE_KEY_ALIAS = "alias";
static final String PRIVATE_KEY_PASSWORD = "password";
static final String CERTIFICATE_ALIAS = "alias";
static final String SIGN_REQUEST = "signRequest";
static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature";
static final String REQUEST_BINDING = "requestBinding";
static final String BINDING_URL = "bindingUrl";
static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature";
static final String SIGN_RESPONSE = "signResponse";
static final String RESPONSE_BINDING = "responseBinding";
static final String POST_BINDING_URL = "postBindingUrl";
static final String REDIRECT_BINDING_URL = "redirectBindingUrl";
static final String SINGLE_SIGN_ON = "SingleSignOnService";
static final String SINGLE_LOGOUT = "SingleLogoutService";
static final String IDENTITY_PROVIDER = "IDP";
static final String PRINCIPAL_NAME_MAPPING = "PrincipalNameMapping";
static final String PRINCIPAL_NAME_MAPPING_POLICY = "policy";
static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "attribute";
static final String ATTRIBUTE = "Attribute";
static final String SIGNATURE_ALGORITHM = "signatureAlgorithm";
static final String SIGNATURE_CANONICALIZATION_METHOD = "signatureCanonicalizationMethod";
static final String PRIVATE_KEY_PEM = "PrivateKeyPem";
static final String PUBLIC_KEY_PEM = "PublicKeyPem";
static final String CERTIFICATE_PEM = "CertificatePem";
static final String TYPE = "type";
static final String ALIAS = "alias";
static final String FILE = "file";
static final String SIGNATURES_REQUIRED = "signaturesRequired";
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class IdentityProviderAddHandler extends AbstractAddStepHandler {
IdentityProviderAddHandler() {
super(IdentityProviderDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,106 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class IdentityProviderDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SIGNATURES_REQUIRED =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURES_REQUIRED, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGNATURES_REQUIRED)
.build();
static final SimpleAttributeDefinition SIGNATURE_ALGORITHM =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_ALGORITHM, ModelType.STRING, true)
.setXmlName(Constants.XML.SIGNATURE_ALGORITHM)
.build();
static final SimpleAttributeDefinition SIGNATURE_CANONICALIZATION_METHOD =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_CANONICALIZATION_METHOD, ModelType.STRING, true)
.setXmlName(Constants.XML.SIGNATURE_CANONICALIZATION_METHOD)
.build();
static final ObjectTypeAttributeDefinition SINGLE_SIGN_ON =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_SIGN_ON,
SingleSignOnDefinition.ATTRIBUTES)
.setAllowNull(false)
.build();
static final ObjectTypeAttributeDefinition SINGLE_LOGOUT =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_LOGOUT,
SingleLogoutDefinition.ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD};
static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD, SINGLE_SIGN_ON, SINGLE_LOGOUT};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static final IdentityProviderDefinition INSTANCE = new IdentityProviderDefinition();
private IdentityProviderDefinition() {
super(PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.IDENTITY_PROVIDER),
new IdentityProviderAddHandler(),
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class KeyAddHandler extends AbstractAddStepHandler {
KeyAddHandler() {
super(KeyDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,122 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ObjectTypeAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SIGNING =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNING, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGNING)
.build();
static final SimpleAttributeDefinition ENCRYPTION =
new SimpleAttributeDefinitionBuilder(Constants.Model.ENCRYPTION, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.ENCRYPTION)
.build();
static final SimpleAttributeDefinition PRIVATE_KEY_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_PEM)
.build();
static final SimpleAttributeDefinition PUBLIC_KEY_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.PUBLIC_KEY_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.PUBLIC_KEY_PEM)
.build();
static final SimpleAttributeDefinition CERTIFICATE_PEM =
new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_PEM, ModelType.STRING, true)
.setXmlName(Constants.XML.CERTIFICATE_PEM)
.build();
static final ObjectTypeAttributeDefinition KEY_STORE =
ObjectTypeAttributeDefinition.Builder.of(Constants.Model.KEY_STORE,
KeyStoreDefinition.ALL_ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNING, ENCRYPTION};
static final SimpleAttributeDefinition[] ELEMENTS = {PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM};
static final AttributeDefinition[] ALL_ATTRIBUTES = {SIGNING, ENCRYPTION, PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM, KEY_STORE};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static final HashMap<String, SimpleAttributeDefinition> ELEMENT_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ELEMENTS) {
ELEMENT_MAP.put(def.getXmlName(), def);
}
}
static final KeyDefinition INSTANCE = new KeyDefinition();
private KeyDefinition() {
super(PathElement.pathElement(Constants.Model.KEY),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.KEY),
new KeyAddHandler(),
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
static SimpleAttributeDefinition lookupElement(String xmlName) {
return ELEMENT_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,36 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyStoreCertificateDefinition {
static final SimpleAttributeDefinition CERTIFICATE_ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.CERTIFICATE_ALIAS)
.build();
static SimpleAttributeDefinition lookup(String xmlName) {
return Constants.XML.CERTIFICATE_ALIAS.equals(xmlName) ? CERTIFICATE_ALIAS : null;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class KeyStoreDefinition {
static final SimpleAttributeDefinition RESOURCE =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESOURCE, ModelType.STRING, true)
.setXmlName(Constants.XML.RESOURCE)
.build();
static final SimpleAttributeDefinition PASSWORD =
new SimpleAttributeDefinitionBuilder(Constants.Model.PASSWORD, ModelType.STRING, true)
.setXmlName(Constants.XML.PASSWORD)
.build();
static final SimpleAttributeDefinition FILE =
new SimpleAttributeDefinitionBuilder(Constants.Model.FILE, ModelType.STRING, true)
.setXmlName(Constants.XML.FILE)
.build();
static final SimpleAttributeDefinition TYPE =
new SimpleAttributeDefinitionBuilder(Constants.Model.TYPE, ModelType.STRING, true)
.setXmlName(Constants.XML.TYPE)
.build();
static final SimpleAttributeDefinition ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.ALIAS)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS};
static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS,
KeyStorePrivateKeyDefinition.PRIVATE_KEY_ALIAS,
KeyStorePrivateKeyDefinition.PRIVATE_KEY_PASSWORD,
KeyStoreCertificateDefinition.CERTIFICATE_ALIAS
};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,52 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class KeyStorePrivateKeyDefinition {
static final SimpleAttributeDefinition PRIVATE_KEY_ALIAS =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_ALIAS, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_ALIAS)
.build();
static final SimpleAttributeDefinition PRIVATE_KEY_PASSWORD =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PASSWORD, ModelType.STRING, true)
.setXmlName(Constants.XML.PRIVATE_KEY_PASSWORD)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASSWORD};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,128 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.dmr.ModelNode;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.spec.LoginConfigMetaData;
import org.jboss.staxmapper.FormattingXMLStreamWriter;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import org.keycloak.adapters.saml.AdapterConstants;
import org.keycloak.subsystem.adapter.saml.extension.logging.KeycloakLogger;
import javax.xml.stream.XMLOutputFactory;
import javax.xml.stream.XMLStreamException;
import java.io.ByteArrayOutputStream;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
/**
* Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
if (Configuration.INSTANCE.getSecureDeployment(deploymentName) != null) {
addKeycloakSamlAuthData(phaseContext, deploymentName);
}
}
private void addKeycloakSamlAuthData(DeploymentPhaseContext phaseContext, String deploymentName) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) {
throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentName + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem.");
}
try {
addXMLData(getXML(deploymentName), warMetaData);
} catch (Exception e) {
throw new DeploymentUnitProcessingException("Failed to configure KeycloakSamlExtension from subsystem model", e);
}
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
warMetaData.setMergedJBossWebMetaData(webMetaData);
}
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
if (loginConfig == null) {
loginConfig = new LoginConfigMetaData();
webMetaData.setLoginConfig(loginConfig);
}
loginConfig.setAuthMethod("KEYCLOAK-SAML");
KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName);
}
private String getXML(String deploymentName) throws XMLStreamException {
ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentName);
if (node != null) {
KeycloakSubsystemParser writer = new KeycloakSubsystemParser();
ByteArrayOutputStream output = new ByteArrayOutputStream();
XMLExtendedStreamWriter streamWriter = new FormattingXMLStreamWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(output));
try {
streamWriter.writeStartElement("keycloak-saml-adapter");
writer.writeSps(streamWriter, node);
streamWriter.writeEndElement();
} finally {
streamWriter.close();
}
return new String(output.toByteArray(), Charset.forName("utf-8"));
}
return null;
}
private void addXMLData(String xml, WarMetaData warMetaData) {
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) {
webMetaData = new JBossWebMetaData();
warMetaData.setMergedJBossWebMetaData(webMetaData);
}
List<ParamValueMetaData> contextParams = webMetaData.getContextParams();
if (contextParams == null) {
contextParams = new ArrayList<>();
}
ParamValueMetaData param = new ParamValueMetaData();
param.setParamName(AdapterConstants.AUTH_DATA_PARAM_NAME);
param.setParamValue(xml);
contextParams.add(param);
webMetaData.setContextParams(contextParams);
}
@Override
public void undeploy(DeploymentUnit du) {
}
}

View file

@ -29,7 +29,6 @@ import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoader;
/**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor {

View file

@ -37,13 +37,12 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUB
public class KeycloakSamlExtension implements Extension {
public static final String SUBSYSTEM_NAME = "keycloak-saml";
public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6";
public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.1";
private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions";
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0);
private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1, 1, 0);
static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@ -68,7 +67,12 @@ public class KeycloakSamlExtension implements Extension {
public void initialize(final ExtensionContext context) {
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION);
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KeycloakSubsystemDefinition.INSTANCE);
ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SecureDeploymentDefinition.INSTANCE);
ManagementResourceRegistration serviceProviderRegistration = secureDeploymentRegistration.registerSubModel(ServiceProviderDefinition.INSTANCE);
serviceProviderRegistration.registerSubModel(KeyDefinition.INSTANCE);
ManagementResourceRegistration idpRegistration = serviceProviderRegistration.registerSubModel(IdentityProviderDefinition.INSTANCE);
idpRegistration.registerSubModel(KeyDefinition.INSTANCE);
subsystem.registerXMLElementWriter(PARSER);
}

View file

@ -16,7 +16,6 @@
*/
package org.keycloak.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.server.AbstractDeploymentChainStep;

View file

@ -14,7 +14,6 @@
* License for the specific language governing permissions and limitations under
* the License.
*/
package org.keycloak.subsystem.adapter.saml.extension;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
@ -23,12 +22,15 @@ import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler
import org.jboss.as.controller.registry.ManagementResourceRegistration;
/**
* Definition of subsystem=keycloak.
* Definition of subsystem=keycloak-saml.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
protected KeycloakSubsystemDefinition() {
static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition();
private KeycloakSubsystemDefinition() {
super(KeycloakSamlExtension.SUBSYSTEM_PATH,
KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE,
@ -41,5 +43,4 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
}

View file

@ -0,0 +1,569 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
/**
* The subsystem parser, which uses stax to read and write to and from xml
*/
class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
/**
* {@inheritDoc}
*/
@Override
public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
// Require no attributes
ParseUtils.requireNoAttributes(reader);
ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM));
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
if (reader.getLocalName().equals(Constants.XML.SECURE_DEPLOYMENT)) {
readSecureDeployment(reader, list);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
// used for debugging
private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
return reader.nextTag();
}
void readSecureDeployment(XMLExtendedStreamReader reader, List<ModelNode> list) throws XMLStreamException {
String name = readRequiredAttribute(reader, Constants.XML.NAME);
PathAddress addr = PathAddress.pathAddress(
PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakSamlExtension.SUBSYSTEM_NAME),
PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT, name));
ModelNode addSecureDeployment = Util.createAddOperation(addr);
list.add(addSecureDeployment);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (tagName.equals(Constants.XML.SERVICE_PROVIDER)) {
readServiceProvider(reader, list, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readServiceProvider(XMLExtendedStreamReader reader, List<ModelNode> list, PathAddress parentAddr) throws XMLStreamException {
String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID);
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.SERVICE_PROVIDER, entityId));
ModelNode addServiceProvider = Util.createAddOperation(addr);
list.add(addServiceProvider);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
if (Constants.XML.ENTITY_ID.equals(name)) {
continue;
}
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = ServiceProviderDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addServiceProvider, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.KEYS.equals(tagName)) {
readKeys(list, reader, addr);
} else if (Constants.XML.PRINCIPAL_NAME_MAPPING.equals(tagName)) {
readPrincipalNameMapping(addServiceProvider, reader);
} else if (Constants.XML.ROLE_IDENTIFIERS.equals(tagName)) {
readRoleIdentifiers(addServiceProvider, reader);
} else if (Constants.XML.IDENTITY_PROVIDER.equals(tagName)) {
readIdentityProvider(list, reader, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readIdentityProvider(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID);
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER, entityId));
ModelNode addIdentityProvider = Util.createAddOperation(addr);
list.add(addIdentityProvider);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
if (Constants.XML.ENTITY_ID.equals(name)
// don't break if encountering this noop attr from client-adapter/core keycloak_saml_adapter_1_6.xsd
|| "encryption".equals(name)) {
continue;
}
SimpleAttributeDefinition attr = IdentityProviderDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addIdentityProvider, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.SINGLE_SIGN_ON.equals(tagName)) {
readSingleSignOn(addIdentityProvider, reader);
} else if (Constants.XML.SINGLE_LOGOUT.equals(tagName)) {
readSingleLogout(addIdentityProvider, reader);
} else if (Constants.XML.KEYS.equals(tagName)) {
readKeys(list, reader, addr);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readSingleSignOn(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode sso = addIdentityProvider.get(Constants.Model.SINGLE_SIGN_ON);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = SingleSignOnDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, sso, reader);
}
ParseUtils.requireNoContent(reader);
}
void readSingleLogout(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode slo = addIdentityProvider.get(Constants.Model.SINGLE_LOGOUT);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = SingleLogoutDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, slo, reader);
}
ParseUtils.requireNoContent(reader);
}
void readKeys(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
List<ModelNode> keyList = new LinkedList<>();
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (!Constants.XML.KEY.equals(tagName)) {
throw ParseUtils.unexpectedElement(reader);
}
readKey(keyList, reader, parentAddr);
}
list.addAll(keyList);
}
void readKey(List<ModelNode> list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException {
PathAddress addr = PathAddress.pathAddress(parentAddr,
PathElement.pathElement(Constants.Model.KEY, "key-" + list.size()));
ModelNode addKey = Util.createAddOperation(addr);
list.add(addKey);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKey, reader);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.KEY_STORE.equals(tagName)) {
readKeyStore(addKey, reader);
} else if (Constants.XML.PRIVATE_KEY_PEM.equals(tagName)
|| Constants.XML.PUBLIC_KEY_PEM.equals(tagName)
|| Constants.XML.CERTIFICATE_PEM.equals(tagName)) {
readNoAttrElementContent(KeyDefinition.lookupElement(tagName), addKey, reader);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readNoAttrElementContent(SimpleAttributeDefinition attr, ModelNode model, XMLExtendedStreamReader reader) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
String value = reader.getElementText();
attr.parseAndSetParameter(value, model, reader);
}
void readKeyStore(ModelNode addKey, XMLExtendedStreamReader reader) throws XMLStreamException {
ModelNode addKeyStore = addKey.get(Constants.Model.KEY_STORE);
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStoreDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.FILE) && !addKeyStore.hasDefined(Constants.Model.RESOURCE)) {
throw new XMLStreamException("KeyStore element must have 'file' or 'resource' attribute set", reader.getLocation());
}
if (!addKeyStore.hasDefined(Constants.Model.PASSWORD)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PASSWORD);
}
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (Constants.XML.PRIVATE_KEY.equals(tagName)) {
readPrivateKey(reader, addKeyStore);
} else if (Constants.XML.CERTIFICATE.equals(tagName)) {
readCertificate(reader, addKeyStore);
} else {
throw ParseUtils.unexpectedElement(reader);
}
}
}
void readPrivateKey(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStorePrivateKeyDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_ALIAS)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_ALIAS);
}
if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_PASSWORD)) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_PASSWORD);
}
ParseUtils.requireNoContent(reader);
}
void readCertificate(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException {
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.lookup(name);
if (attr == null) {
throw ParseUtils.unexpectedAttribute(reader, i);
}
attr.parseAndSetParameter(value, addKeyStore, reader);
}
if (!addKeyStore.hasDefined(Constants.Model.CERTIFICATE_ALIAS)) {
throw ParseUtils.missingRequired(reader, Constants.XML.CERTIFICATE_ALIAS);
}
ParseUtils.requireNoContent(reader);
}
void readRoleIdentifiers(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
ParseUtils.requireNoAttributes(reader);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
String tagName = reader.getLocalName();
if (!Constants.XML.ATTRIBUTE.equals(tagName)) {
throw ParseUtils.unexpectedElement(reader);
}
ParseUtils.requireSingleAttribute(reader, Constants.XML.NAME);
String name = ParseUtils.readStringAttributeElement(reader, Constants.XML.NAME);
ServiceProviderDefinition.ROLE_ATTRIBUTES.parseAndAddParameterElement(name, addServiceProvider, reader);
}
}
void readPrincipalNameMapping(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException {
boolean policySet = false;
for (int i = 0; i < reader.getAttributeCount(); i++) {
String name = reader.getAttributeLocalName(i);
String value = reader.getAttributeValue(i);
if (Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY.equals(name)) {
policySet = true;
ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_POLICY.parseAndSetParameter(value, addServiceProvider, reader);
} else if (Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.equals(name)) {
ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.parseAndSetParameter(value, addServiceProvider, reader);
} else {
throw ParseUtils.unexpectedAttribute(reader, i);
}
}
if (!policySet) {
throw ParseUtils.missingRequired(reader, Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY);
}
ParseUtils.requireNoContent(reader);
}
/**
* Read an attribute, and throw exception if attribute is not present
*/
String readRequiredAttribute(XMLExtendedStreamReader reader, String attrName) throws XMLStreamException {
String value = null;
for (int i = 0; i < reader.getAttributeCount(); i++) {
String attr = reader.getAttributeLocalName(i);
if (attr.equals(attrName)) {
value = reader.getAttributeValue(i);
break;
}
}
if (value == null) {
throw ParseUtils.missingRequired(reader, Collections.singleton(attrName));
}
return value;
}
/**
* {@inheritDoc}
*/
@Override
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false);
writeSecureDeployment(writer, context.getModelNode());
writer.writeEndElement();
}
public void writeSecureDeployment(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.get(Constants.Model.SECURE_DEPLOYMENT).isDefined()) {
return;
}
for (Property sp : model.get(Constants.Model.SECURE_DEPLOYMENT).asPropertyList()) {
writer.writeStartElement(Constants.XML.SECURE_DEPLOYMENT);
writer.writeAttribute(Constants.XML.NAME, sp.getName());
writeSps(writer, sp.getValue());
writer.writeEndElement();
}
}
void writeSps(final XMLExtendedStreamWriter writer, final ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
for (Property sp : model.get(Constants.Model.SERVICE_PROVIDER).asPropertyList()) {
writer.writeStartElement(Constants.XML.SERVICE_PROVIDER);
writer.writeAttribute(Constants.XML.ENTITY_ID, sp.getName());
ModelNode spAttributes = sp.getValue();
for (SimpleAttributeDefinition attr : ServiceProviderDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, spAttributes, false, writer);
}
writeKeys(writer, spAttributes.get(Constants.Model.KEY));
writePrincipalNameMapping(writer, spAttributes);
writeRoleIdentifiers(writer, spAttributes);
writeIdentityProvider(writer, spAttributes.get(Constants.Model.IDENTITY_PROVIDER));
writer.writeEndElement();
}
}
void writeIdentityProvider(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
for (Property idp : model.asPropertyList()) {
writer.writeStartElement(Constants.XML.IDENTITY_PROVIDER);
writer.writeAttribute(Constants.XML.ENTITY_ID, idp.getName());
ModelNode idpAttributes = idp.getValue();
for (SimpleAttributeDefinition attr : IdentityProviderDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, idpAttributes, false, writer);
}
writeSingleSignOn(writer, idpAttributes.get(Constants.Model.SINGLE_SIGN_ON));
writeSingleLogout(writer, idpAttributes.get(Constants.Model.SINGLE_LOGOUT));
writeKeys(writer, idpAttributes.get(Constants.Model.KEY));
}
writer.writeEndElement();
}
void writeSingleSignOn(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.SINGLE_SIGN_ON);
for (SimpleAttributeDefinition attr : SingleSignOnDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeSingleLogout(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.SINGLE_LOGOUT);
for (SimpleAttributeDefinition attr : SingleLogoutDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeKeys(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
boolean contains = false;
for (Property key : model.asPropertyList()) {
if (!contains) {
writer.writeStartElement(Constants.XML.KEYS);
contains = true;
}
writer.writeStartElement(Constants.XML.KEY);
ModelNode keyAttributes = key.getValue();
for (SimpleAttributeDefinition attr : KeyDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, keyAttributes, false, writer);
}
for (SimpleAttributeDefinition attr : KeyDefinition.ELEMENTS) {
attr.getAttributeMarshaller().marshallAsElement(attr, keyAttributes, false, writer);
}
writeKeyStore(writer, keyAttributes.get(Constants.Model.KEY_STORE));
writer.writeEndElement();
}
if (contains) {
writer.writeEndElement();
}
}
void writeKeyStore(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
if (!model.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.KEY_STORE);
for (SimpleAttributeDefinition attr : KeyStoreDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writePrivateKey(writer, model);
writeCertificate(writer, model);
writer.writeEndElement();
}
void writeCertificate(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode value = model.get(Constants.Model.CERTIFICATE_ALIAS);
if (!value.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.CERTIFICATE);
SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.CERTIFICATE_ALIAS;
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
writer.writeEndElement();
}
void writePrivateKey(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode pk_alias = model.get(Constants.Model.PRIVATE_KEY_ALIAS);
ModelNode pk_password = model.get(Constants.Model.PRIVATE_KEY_PASSWORD);
if (!pk_alias.isDefined() && !pk_password.isDefined()) {
return;
}
writer.writeStartElement(Constants.XML.PRIVATE_KEY);
for (SimpleAttributeDefinition attr : KeyStorePrivateKeyDefinition.ATTRIBUTES) {
attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer);
}
writer.writeEndElement();
}
void writeRoleIdentifiers(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
ModelNode value = model.get(Constants.Model.ROLE_ATTRIBUTES);
if (!value.isDefined()) {
return;
}
List<ModelNode> items = value.asList();
if (items.size() == 0) {
return;
}
writer.writeStartElement(Constants.XML.ROLE_IDENTIFIERS);
for (ModelNode item : items) {
writer.writeStartElement(Constants.XML.ATTRIBUTE);
writer.writeAttribute("name", item.asString());
writer.writeEndElement();
}
writer.writeEndElement();
}
void writePrincipalNameMapping(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException {
writer.writeStartElement(Constants.XML.PRINCIPAL_NAME_MAPPING);
ModelNode value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY);
if (value.isDefined()) {
writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY, value.asString());
}
value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME);
if (value.isDefined()) {
writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, value.asString());
}
writer.writeEndElement();
}
}

View file

@ -0,0 +1,38 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class SecureDeploymentAddHandler extends AbstractAddStepHandler {
static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler();
private SecureDeploymentAddHandler() {
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,47 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.*;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
/**
* Defines attributes and operations for a secure-deployment.
*/
public class SecureDeploymentDefinition extends SimpleResourceDefinition {
static final SecureDeploymentDefinition INSTANCE = new SecureDeploymentDefinition();
private SecureDeploymentDefinition() {
super(PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SECURE_DEPLOYMENT),
SecureDeploymentAddHandler.INSTANCE,
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
}

View file

@ -0,0 +1,39 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AbstractAddStepHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.dmr.ModelNode;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
class ServiceProviderAddHandler extends AbstractAddStepHandler {
static final ServiceProviderAddHandler INSTANCE = new ServiceProviderAddHandler();
ServiceProviderAddHandler() {
super(ServiceProviderDefinition.ALL_ATTRIBUTES);
}
@Override
protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException {
Configuration.INSTANCE.updateModel(operation, model);
}
}

View file

@ -0,0 +1,125 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.ListAttributeDefinition;
import org.jboss.as.controller.OperationStepHandler;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.StringListAttributeDefinition;
import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.dmr.ModelType;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
public class ServiceProviderDefinition extends SimpleResourceDefinition {
static final SimpleAttributeDefinition SSL_POLICY =
new SimpleAttributeDefinitionBuilder(Constants.Model.SSL_POLICY, ModelType.STRING, true)
.setXmlName(Constants.XML.SSL_POLICY)
.build();
static final SimpleAttributeDefinition NAME_ID_POLICY_FORMAT =
new SimpleAttributeDefinitionBuilder(Constants.Model.NAME_ID_POLICY_FORMAT, ModelType.STRING, true)
.setXmlName(Constants.XML.NAME_ID_POLICY_FORMAT)
.build();
static final SimpleAttributeDefinition LOGOUT_PAGE =
new SimpleAttributeDefinitionBuilder(Constants.Model.LOGOUT_PAGE, ModelType.STRING, true)
.setXmlName(Constants.XML.LOGOUT_PAGE)
.build();
static final SimpleAttributeDefinition FORCE_AUTHENTICATION =
new SimpleAttributeDefinitionBuilder(Constants.Model.FORCE_AUTHENTICATION, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.FORCE_AUTHENTICATION)
.build();
static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_POLICY =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY, ModelType.STRING, true)
.setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY)
.build();
static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME =
new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ModelType.STRING, true)
.setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME)
.build();
static final ListAttributeDefinition ROLE_ATTRIBUTES =
new StringListAttributeDefinition.Builder(Constants.Model.ROLE_ATTRIBUTES)
.setAllowNull(false)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION};
static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static final HashMap<String, AttributeDefinition> ALL_MAP = new HashMap<>();
static final Collection<AttributeDefinition> ALL_ATTRIBUTES;
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
ALL_MAP.putAll(ATTRIBUTE_MAP);
for (AttributeDefinition def : ELEMENTS) {
ALL_MAP.put(def.getXmlName(), def);
}
ALL_ATTRIBUTES = Collections.unmodifiableCollection(ALL_MAP.values());
}
static final ServiceProviderDefinition INSTANCE = new ServiceProviderDefinition();
private ServiceProviderDefinition() {
super(PathElement.pathElement(Constants.Model.SERVICE_PROVIDER),
KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SERVICE_PROVIDER),
ServiceProviderAddHandler.INSTANCE,
ReloadRequiredRemoveStepHandler.INSTANCE);
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE);
}
@Override
public void registerAttributes(ManagementResourceRegistration resourceRegistration) {
super.registerAttributes(resourceRegistration);
final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES);
for (AttributeDefinition attribute : ALL_ATTRIBUTES) {
resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class SingleLogoutDefinition {
static final SimpleAttributeDefinition VALIDATE_REQUEST_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_REQUEST_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_REQUEST_SIGNATURE)
.build();
static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE)
.build();
static final SimpleAttributeDefinition SIGN_REQUEST =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_REQUEST)
.build();
static final SimpleAttributeDefinition SIGN_RESPONSE =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_RESPONSE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_RESPONSE)
.build();
static final SimpleAttributeDefinition REQUEST_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.REQUEST_BINDING)
.build();
static final SimpleAttributeDefinition RESPONSE_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.RESPONSE_BINDING)
.build();
static final SimpleAttributeDefinition POST_BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.POST_BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.POST_BINDING_URL)
.build();
static final SimpleAttributeDefinition REDIRECT_BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.REDIRECT_BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.REDIRECT_BINDING_URL)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {VALIDATE_REQUEST_SIGNATURE, VALIDATE_RESPONSE_SIGNATURE,
SIGN_REQUEST, SIGN_RESPONSE, REQUEST_BINDING, RESPONSE_BINDING, POST_BINDING_URL, REDIRECT_BINDING_URL};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,68 @@
/*
* Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.dmr.ModelType;
import java.util.HashMap;
/**
* @author <a href="mailto:mstrukel@redhat.com">Marko Strukelj</a>
*/
abstract class SingleSignOnDefinition {
static final SimpleAttributeDefinition SIGN_REQUEST =
new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.SIGN_REQUEST)
.build();
static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE =
new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true)
.setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE)
.build();
static final SimpleAttributeDefinition REQUEST_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.REQUEST_BINDING)
.build();
static final SimpleAttributeDefinition RESPONSE_BINDING =
new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true)
.setXmlName(Constants.XML.RESPONSE_BINDING)
.build();
static final SimpleAttributeDefinition BINDING_URL =
new SimpleAttributeDefinitionBuilder(Constants.Model.BINDING_URL, ModelType.STRING, true)
.setXmlName(Constants.XML.BINDING_URL)
.build();
static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL};
static final HashMap<String, SimpleAttributeDefinition> ATTRIBUTE_MAP = new HashMap<>();
static {
for (SimpleAttributeDefinition def : ATTRIBUTES) {
ATTRIBUTE_MAP.put(def.getXmlName(), def);
}
}
static SimpleAttributeDefinition lookup(String xmlName) {
return ATTRIBUTE_MAP.get(xmlName);
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension.logging;
import org.jboss.logging.BasicLogger;
import org.jboss.logging.Logger;
import org.jboss.logging.annotations.LogMessage;
import org.jboss.logging.annotations.Message;
import org.jboss.logging.annotations.MessageLogger;
import static org.jboss.logging.Logger.Level.INFO;
/**
* This interface to be fleshed out later when error messages are fully externalized.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/
@MessageLogger(projectCode = "KEYCLOAK")
public interface KeycloakLogger extends BasicLogger {
/**
* A logger with a category of the package name.
*/
KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak");
@LogMessage(level = INFO)
@Message(value = "Keycloak subsystem override for deployment %s")
void deploymentSecured(String deployment);
}

View file

@ -0,0 +1,34 @@
/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension.logging;
import org.jboss.logging.Messages;
import org.jboss.logging.annotations.MessageBundle;
/**
* This interface to be fleshed out later when error messages are fully externalized.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc.
*/
@MessageBundle(projectCode = "KEYCLOAK")
public interface KeycloakMessages {
/**
* The messages
*/
KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class);
}

View file

@ -0,0 +1,63 @@
keycloak-saml.subsystem=Keycloak adapter subsystem
keycloak-saml.subsystem.add=Operation Adds Keycloak adapter subsystem
keycloak-saml.subsystem.remove=Operation removes Keycloak adapter subsystem
keycloak-saml.subsystem.secure-deployment=A deployment secured by Keycloak.
keycloak-saml.secure-deployment=A deployment secured by Keycloak
keycloak-saml.secure-deployment.add=Add a deployment to be secured by Keycloak
keycloak-saml.secure-deployment.remove=Remove a deployment to be secured by Keycloak
keycloak-saml.secure-deployment.service-provider=A security provider configuration for secure deployment
keycloak-saml.service-provider=A security provider configuration for secure deployment
keycloak-saml.service-provider.add=Add a security provider configuration to deployment secured by Keycloak SAML
keycloak-saml.service-provider.remove=Remove a security provider definition from deployment secured by Keycloak SAML
keycloak-saml.service-provider.ssl-policy=SSL Policy to use
keycloak-saml.service-provider.name-id-policy-format=Name ID policy format URN
keycloak-saml.service-provider.logout-page=URI to a logout page
keycloak-saml.service-provider.force-authentication=Redirected unauthenticated request to a login page
keycloak-saml.service-provider.role-attributes=Role identifiers
keycloak-saml.service-provider.principal-name-mapping-policy=Principal name mapping policy
keycloak-saml.service-provider.principal-name-mapping-attribute-name=Principal name mapping attribute name
keycloak-saml.service-provider.key=A key definition
keycloak-saml.service-provider.identity-provider=Identity provider definition
keycloak-saml.key=A key configuration for service provider or identity provider
keycloak-saml.key.add=Add a key definition
keycloak-saml.key.remove=Remove a key definition
keycloak-saml.key.signing=Key can be used for signing
keycloak-saml.key.encryption=Key can be used for encryption
keycloak-saml.key.private-key-pem=Private key string in pem format
keycloak-saml.key.public-key-pem=Public key string in pem format
keycloak-saml.key.certificate-pem=Certificate key string in pem format
keycloak-saml.key.key-store=Key store definition
keycloak-saml.key.key-store.file=Key store filesystem path
keycloak-saml.key.key-store.resource=Key store resource URI
keycloak-saml.key.key-store.password=Key store password
keycloak-saml.key.key-store.type=Key store format
keycloak-saml.key.key-store.alias=Key alias
keycloak-saml.key.key-store.private-key-alias=Private key alias
keycloak-saml.key.key-store.private-key-password=Private key password
keycloak-saml.key.key-store.certificate-alias=Certificate alias
keycloak-saml.identity-provider=An identity provider configuration
keycloak-saml.identity-provider.add=Add an identity provider
keycloak-saml.identity-provider.remove=Remove an identity provider
keycloak-saml.identity-provider.signatures-required=Require signatures for single-sign-on and single-logout
keycloak-saml.identity-provider.signature-algorithm=Signature algorithm
keycloak-saml.identity-provider.signature-canonicalization-method=Signature canonicalization method
keycloak-saml.identity-provider.single-sign-on=Single sign-on configuration
keycloak-saml.identity-provider.single-sign-on.sign-request=Sign SSO requests
keycloak-saml.identity-provider.single-sign-on.validate-response-signature=Validate an SSO response signature
keycloak-saml.identity-provider.single-sign-on.request-binding=HTTP method to use for requests
keycloak-saml.identity-provider.single-sign-on.response-binding=HTTP method to use for responses
keycloak-saml.identity-provider.single-sign-on.binding-url=SSO endpoint URL
keycloak-saml.identity-provider.single-logout=Single logout configuration
keycloak-saml.identity-provider.single-logout.validate-request-signature=Validate a single-logout request signature
keycloak-saml.identity-provider.single-logout.validate-response-signature=Validate a single-logout response signature
keycloak-saml.identity-provider.single-logout.sign-request=Sign single-logout requests
keycloak-saml.identity-provider.single-logout.sign-response=Sign single-logout responses
keycloak-saml.identity-provider.single-logout.request-binding=HTTP method to use for request
keycloak-saml.identity-provider.single-logout.response-binding=HTTP method to use for response
keycloak-saml.identity-provider.single-logout.post-binding-url=Endpoint URL for posting
keycloak-saml.identity-provider.single-logout.redirect-binding-url=Endpoint URL for redirects
keycloak-saml.identity-provider.key=Key definition for identity provider

View file

@ -0,0 +1,268 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:jboss:domain:keycloak-saml:1.1"
xmlns="urn:jboss:domain:keycloak-saml:1.1"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
version="1.0">
<!-- The subsystem root element -->
<xs:element name="subsystem" type="subsystem-type"/>
<xs:complexType name="subsystem-type">
<xs:annotation>
<xs:documentation>
<![CDATA[
The Keycloak SAML adapter subsystem, used to register deployments managed by Keycloak SAML adapter
]]>
</xs:documentation>
</xs:annotation>
<xs:all>
<xs:element name="secure-deployment" minOccurs="0" type="secure-deployment-type"/>
</xs:all>
</xs:complexType>
<xs:complexType name="secure-deployment-type">
<xs:all>
<xs:element name="SP" minOccurs="1" maxOccurs="1" type="sp-type"/>
</xs:all>
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The name of the realm.</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="sp-type">
<xs:all>
<xs:element name="Keys" minOccurs="0" maxOccurs="1" type="keys-type"/>
<xs:element name="PrincipalNameMapping" minOccurs="0" maxOccurs="1" type="principal-name-mapping-type"/>
<xs:element name="RoleIdentifiers" minOccurs="0" maxOccurs="1" type="role-identifiers-type"/>
<xs:element name="IDP" minOccurs="1" maxOccurs="1" type="identity-provider-type"/>
</xs:all>
<xs:attribute name="entityID" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The entity ID for SAML service provider</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="sslPolicy" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The ssl policy</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="nameIDPolicyFormat" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Name ID policy format URN</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="logoutPage" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>URI to a logout page</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="forceAuthentication" type="xs:boolean" use="required">
<xs:annotation>
<xs:documentation>Redirected unauthenticated request to a login page</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="identity-provider-type">
<xs:all minOccurs="1" maxOccurs="1">
<xs:element name="SingleSignOnService" minOccurs="1" maxOccurs="1" type="single-signon-type"/>
<xs:element name="SingleLogoutService" minOccurs="0" maxOccurs="1" type="single-logout-type"/>
<xs:element name="Keys" minOccurs="0" maxOccurs="1" type="keys-type"/>
</xs:all>
<xs:attribute name="entityID" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>The entity ID for SAML service provider</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signaturesRequired" type="xs:boolean" use="required">
<xs:annotation>
<xs:documentation>Require signatures for single-sign-on and single-logout</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signatureAlgorithm" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Algorithm used for signatures</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signatureCanonicalizationMethod" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Canonicalization method used for signatures</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="single-signon-type">
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign the SSO requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate the SSO response signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="requestBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="responseBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for response</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="bindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>SSO endpoint URL</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="single-logout-type">
<xs:attribute name="validateRequestSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate a single-logout request signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="validateResponseSignature" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Validate a single-logout response signature</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signRequest" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign single-logout requests</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="signResponse" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Sign single-logout responses</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="requestBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for request</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="responseBinding" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>HTTP method to use for response</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="postBindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Endpoint URL for posting</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="redirectBindingUrl" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Endpoint URL for redirects</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="keys-type">
<xs:sequence>
<xs:element name="Key" minOccurs="1" maxOccurs="2" type="key-type"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="key-type">
<xs:all>
<xs:element name="KeyStore" minOccurs="0" maxOccurs="1" type="keystore-type"/>
<xs:element name="PrivateKeyPem" minOccurs="0" maxOccurs="1" type="xs:string"/>
<xs:element name="PublicKeyPem" minOccurs="0" maxOccurs="1" type="xs:string"/>
<xs:element name="CertificatePem" minOccurs="0" maxOccurs="1" type="xs:string"/>
</xs:all>
<xs:attribute name="signing" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Key can be used for signing</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="encryption" type="xs:boolean" use="optional">
<xs:annotation>
<xs:documentation>Key can be used for encryption</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="keystore-type">
<xs:sequence minOccurs="0" maxOccurs="1">
<xs:element name="PrivateKey" minOccurs="0" maxOccurs="1" type="privatekey-type"/>
<xs:element name="Certificate" minOccurs="0" maxOccurs="1" type="certificate-type"/>
</xs:sequence>
<xs:attribute name="file" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store filesystem path</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="resource" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store resource URI</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Key store password</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="type" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key store format</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="alias" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Key alias</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="privatekey-type">
<xs:attribute name="alias" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Private key alias</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="password" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Private key password</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="certificate-type">
<xs:attribute name="alias" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Certificate alias</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="principal-name-mapping-type">
<xs:attribute name="policy" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Principal name mapping policy. Possible values: FROM_NAME_ID</xs:documentation>
</xs:annotation>
</xs:attribute>
<xs:attribute name="attribute" type="xs:string" use="optional">
<xs:annotation>
<xs:documentation>Name of the attribute to use for principal name mapping</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
<xs:complexType name="role-identifiers-type">
<xs:sequence minOccurs="0" maxOccurs="unbounded">
<xs:element name="Attribute" minOccurs="0" maxOccurs="unbounded" type="attribute-type"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="attribute-type">
<xs:attribute name="name" type="xs:string" use="required">
<xs:annotation>
<xs:documentation>Role attribute</xs:documentation>
</xs:annotation>
</xs:attribute>
</xs:complexType>
</xs:schema>

View file

@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<!-- Template used by WildFly build when directed to include Keycloak subsystem in a configuration. -->
<!-- Template used by WildFly build when directed to include Keycloak SAML subsystem in a configuration. -->
<config>
<extension-module>org.keycloak.keycloak-saml-adapter-subsystem</extension-module>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6">
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
</subsystem>
</config>

View file

@ -0,0 +1,56 @@
/*
* Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest;
import java.io.IOException;
/**
* Tests all management expects for subsystem, parsing, marshaling, model definition and other
* Here is an example that allows you a fine grained controller over what is tested and how. So it can give you ideas what can be done and tested.
* If you have no need for advanced testing of subsystem you look at {@link SubsystemBaseParsingTestCase} that testes same stuff but most of the code
* is hidden inside of test harness
*
* @author <a href="kabir.khan@jboss.com">Kabir Khan</a>
* @author Tomaz Cerar
* @author <a href="marko.strukelj@gmail.com">Marko Strukelj</a>
*/
public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest {
public SubsystemParsingTestCase() {
super(KeycloakSamlExtension.SUBSYSTEM_NAME, new KeycloakSamlExtension());
}
@Override
protected String getSubsystemXml() throws IOException {
return readResource("keycloak-saml-1.1.xml");
}
@Override
protected String getSubsystemXsdPath() throws Exception {
return "schema/wildfly-keycloak-saml_1_1.xsd";
}
@Override
protected String[] getSubsystemTemplatePaths() throws IOException {
return new String[]{
"/subsystem-templates/keycloak-saml-adapter.xml"
};
}
}

View file

@ -0,0 +1,50 @@
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
<secure-deployment name="my-app.war">
<SP entityID="http://localhost:8080/sales-post-enc/"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
logoutPage="/logout.jsp"
forceAuthentication="false">
<Keys>
<Key encryption="true" signing="true">
<PrivateKeyPem>my_key.pem</PrivateKeyPem>
<PublicKeyPem>my_key.pub</PublicKeyPem>
<CertificatePem>cert.cer</CertificatePem>
<KeyStore resource="/WEB-INF/keystore.jks" password="store123" file="test" alias="test" type="jks">
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
</KeyStore>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID" attribute="test"/>
<RoleIdentifiers>
<Attribute name="Role"/>
<Attribute name="Role2"/>
</RoleIdentifiers>
<IDP entityID="idp" signaturesRequired="true" signatureAlgorithm="test" signatureCanonicalizationMethod="test" encryption="test">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
responseBinding="POST"
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<Keys>
<Key signing="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="saml-demo"/>
</KeyStore>
</Key>
</Keys>
</IDP>
</SP>
</secure-deployment>
</subsystem>

View file

@ -0,0 +1,50 @@
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1">
<secure-deployment name="my-app.war">
<SP entityID="http://localhost:8080/sales-post-enc/"
sslPolicy="EXTERNAL"
nameIDPolicyFormat="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified"
logoutPage="/logout.jsp"
forceAuthentication="false">
<Keys>
<Key encryption="true" signing="true">
<PrivateKeyPem>my_key.pem</PrivateKeyPem>
<PublicKeyPem>my_key.pub</PublicKeyPem>
<CertificatePem>cert.cer</CertificatePem>
<KeyStore resource="/WEB-INF/keystore.jks" password="store123" file="test" alias="test" type="jks">
<PrivateKey alias="http://localhost:8080/sales-post-enc/" password="test123"/>
<Certificate alias="http://localhost:8080/sales-post-enc/"/>
</KeyStore>
</Key>
</Keys>
<PrincipalNameMapping policy="FROM_NAME_ID" attribute="test"/>
<RoleIdentifiers>
<Attribute name="Role"/>
<Attribute name="Role2"/>
</RoleIdentifiers>
<IDP entityID="idp" signaturesRequired="true" signatureAlgorithm="test" signatureCanonicalizationMethod="test">
<SingleSignOnService signRequest="true"
validateResponseSignature="true"
requestBinding="POST"
responseBinding="POST"
bindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<SingleLogoutService
validateRequestSignature="true"
validateResponseSignature="true"
signRequest="true"
signResponse="true"
requestBinding="POST"
responseBinding="POST"
postBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"
redirectBindingUrl="http://localhost:8080/auth/realms/saml-demo/protocol/saml"/>
<Keys>
<Key signing="true">
<KeyStore resource="/WEB-INF/keystore.jks" password="store123">
<Certificate alias="saml-demo"/>
</KeyStore>
</Key>
</Keys>
</IDP>
</SP>
</secure-deployment>
</subsystem>

View file

@ -1,53 +0,0 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.server.deployment.DeploymentPhaseContext;
import org.jboss.as.server.deployment.DeploymentUnit;
import org.jboss.as.server.deployment.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.web.common.WarMetaData;
import org.jboss.logging.Logger;
import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.spec.LoginConfigMetaData;
import java.util.ArrayList;
import java.util.List;
/**
* Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension.
*
* @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc.
*/
public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class);
@Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName();
}
@Override
public void undeploy(DeploymentUnit du) {
}
}

View file

@ -1,91 +0,0 @@
/*
* Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
*
* 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.subsystem.adapter.saml.extension;
import org.jboss.as.controller.AttributeDefinition;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.parsing.ParseUtils;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader;
import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* The subsystem parser, which uses stax to read and write to and from xml
*/
class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<List<ModelNode>>, XMLElementWriter<SubsystemMarshallingContext> {
/**
* {@inheritDoc}
*/
@Override
public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
// Require no attributes
ParseUtils.requireNoAttributes(reader);
ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM));
list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) {
}
}
// used for debugging
private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException {
return reader.nextTag();
}
/**
* {@inheritDoc}
*/
@Override
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false);
writer.writeEndElement();
}
// code taken from org.jboss.as.controller.AttributeMarshaller
private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException {
if (content.indexOf('\n') > -1) {
// Multiline content. Use the overloaded variant that staxmapper will format
writer.writeCharacters(content);
} else {
// Staxmapper will just output the chars without adding newlines if this is used
char[] chars = content.toCharArray();
writer.writeCharacters(chars, 0, chars.length);
}
}
}