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> </excludes>
</fileSet> </fileSet>
<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> <outputDirectory>keycloak</outputDirectory>
<excludes> <excludes>
<exclude>standalone/configuration/standalone-keycloak.xml</exclude> <exclude>standalone/configuration/standalone-keycloak.xml</exclude>

View file

@ -26,7 +26,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId> <artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>zip</type> <type>zip</type>
</dependency> </dependency>
<dependency> <dependency>
@ -116,9 +116,9 @@
<artifactItems> <artifactItems>
<artifactItem> <artifactItem>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-adapter-dist</artifactId> <artifactId>keycloak-saml-wildfly-adapter-dist</artifactId>
<type>zip</type> <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> </artifactItem>
</artifactItems> </artifactItems>
</configuration> </configuration>

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -12,9 +12,9 @@
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </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> <packaging>pom</packaging>
<dependencies> <dependencies>
<dependency> <dependency>
@ -51,7 +51,7 @@
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wf9-subsystem</artifactId> <artifactId>keycloak-saml-wildfly-subsystem</artifactId>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -30,6 +30,6 @@
</resources> </resources>
<dependencies> <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> </dependencies>
</module> </module>

View file

@ -22,7 +22,7 @@
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org. ~ 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> <resources>
<resource-root path="."/> <resource-root path="."/>

View file

@ -3,7 +3,7 @@
<para> <para>
To be able to secure WAR apps deployed on JBoss EAP 6.x or Wildfly, you must install and 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 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. Both methods are described in this section.
</para> </para>
<section id="jboss-adapter-installation"> <section id="jboss-adapter-installation">
@ -13,10 +13,10 @@
the Keycloak download site. They are also available as a maven artifact. the Keycloak download site. They are also available as a maven artifact.
</para> </para>
<para> <para>
Install on Wildfly 9: Install on Wildfly 9 or 10:
<programlisting> <programlisting>
$ cd $WILDFLY_HOME $ cd $WILDFLY_HOME
$ unzip keycloak-saml-wf9-adapter-dist.zip $ unzip keycloak-saml-wildfly-adapter-dist.zip
</programlisting> </programlisting>
</para> </para>
<para> <para>
@ -52,7 +52,7 @@ $ jboss-cli.sh -c --file=adapter-install.cli
</extensions> </extensions>
<profile> <profile>
<subsystem xmlns="urn:jboss:domain:keycloak-saml:1.6"/> <subsystem xmlns="urn:jboss:domain:keycloak-saml:1.1"/>
... ...
</profile> </profile>
]]> ]]>
@ -185,4 +185,92 @@ public class CustomerService {
</programlisting> </programlisting>
</para> </para>
</section> </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> </chapter>

View file

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

View file

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

View file

@ -101,9 +101,9 @@ projects that depend on this project.-->
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.jboss.msc</groupId> <groupId>org.jboss.as</groupId>
<artifactId>jboss-msc</artifactId> <artifactId>jboss-as-controller</artifactId>
<version>1.0.2.GA</version> <version>${jboss.version}</version>
</dependency> </dependency>
<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.DeploymentUnitProcessingException;
import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.as.server.deployment.DeploymentUnitProcessor;
import org.jboss.as.web.deployment.WarMetaData; import org.jboss.as.web.deployment.WarMetaData;
import org.jboss.dmr.ModelNode;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.metadata.javaee.spec.ParamValueMetaData; import org.jboss.metadata.javaee.spec.ParamValueMetaData;
import org.jboss.metadata.web.jboss.JBossWebMetaData; import org.jboss.metadata.web.jboss.JBossWebMetaData;
import org.jboss.metadata.web.jboss.ValveMetaData; import org.jboss.metadata.web.jboss.ValveMetaData;
import org.jboss.metadata.web.spec.LoginConfigMetaData; 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.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.ArrayList;
import java.util.List; import java.util.List;
@ -39,22 +48,16 @@ import java.util.List;
public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor { public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor {
protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class); 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 @Override
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
String deploymentName = deploymentUnit.getName(); String deploymentName = deploymentUnit.getName();
// if it's not a web-app there's nothing to secure
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY); WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
if (warMetaData == null) { if (warMetaData == null) {
return; return;
} }
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
if (webMetaData == null) { if (webMetaData == null) {
webMetaData = new JBossWebMetaData(); webMetaData = new JBossWebMetaData();
@ -64,14 +67,68 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
// otherwise // otherwise
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig(); 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) { // if secure-deployment configuration exists for web app, we force KEYCLOAK-SAML auth method on it
log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName); if (hasSubsystemConfig) {
addValve(webMetaData); 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) { private void addValve(JBossWebMetaData webMetaData) {
List<ValveMetaData> valves = webMetaData.getValves(); List<ValveMetaData> valves = webMetaData.getValves();
if (valves == null) { if (valves == null) {
@ -89,5 +146,4 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
public void undeploy(DeploymentUnit du) { 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.Extension;
import org.jboss.as.controller.ExtensionContext; import org.jboss.as.controller.ExtensionContext;
import org.jboss.as.controller.ModelVersion;
import org.jboss.as.controller.PathElement; import org.jboss.as.controller.PathElement;
import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.ResourceDefinition;
import org.jboss.as.controller.SubsystemRegistration; 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 class KeycloakSamlExtension implements Extension {
public static final String SUBSYSTEM_NAME = "keycloak-saml"; 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(); private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions";
private static final int MGMT_API_VERSION_MAJOR = 1; private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1, 1, 0);
private static final int MGMT_API_VERSION_MINOR = 1;
static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); 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) { public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@ -67,10 +65,15 @@ public class KeycloakSamlExtension implements Extension {
*/ */
@Override @Override
public void initialize(final ExtensionContext context) { public void initialize(final ExtensionContext context) {
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION_MAJOR, MGMT_API_VERSION_MINOR); final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME,
MGMT_API_VERSION.getMajor(), MGMT_API_VERSION.getMinor(), MGMT_API_VERSION.getMicro());
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); subsystem.registerXMLElementWriter(PARSER);
} }
} }

View file

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

View file

@ -14,23 +14,23 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package org.keycloak.subsystem.saml.as7; package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
import org.jboss.as.controller.SimpleResourceDefinition; 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.operations.common.GenericSubsystemDescribeHandler;
import org.jboss.as.controller.registry.ManagementResourceRegistration; 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. * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/ */
public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
protected KeycloakSubsystemDefinition() {
static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition();
private KeycloakSubsystemDefinition() {
super(KeycloakSamlExtension.SUBSYSTEM_PATH, super(KeycloakSamlExtension.SUBSYSTEM_PATH,
KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"), KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE, KeycloakSubsystemAdd.INSTANCE,
@ -41,7 +41,6 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
@Override @Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) { public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(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. * as indicated by the @author tags. All rights reserved.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * Licensed under the Apache License, Version 2.0 (the "License"); you may not
@ -17,9 +17,14 @@
package org.keycloak.subsystem.saml.as7; package org.keycloak.subsystem.saml.as7;
import org.jboss.as.controller.PathAddress; 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.parsing.ParseUtils;
import org.jboss.as.controller.persistence.SubsystemMarshallingContext; import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelNode;
import org.jboss.dmr.Property;
import org.jboss.staxmapper.XMLElementReader; import org.jboss.staxmapper.XMLElementReader;
import org.jboss.staxmapper.XMLElementWriter; import org.jboss.staxmapper.XMLElementWriter;
import org.jboss.staxmapper.XMLExtendedStreamReader; import org.jboss.staxmapper.XMLExtendedStreamReader;
@ -27,6 +32,10 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter;
import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamConstants;
import javax.xml.stream.XMLStreamException; 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; 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 { public void readElement(final XMLExtendedStreamReader reader, final List<ModelNode> list) throws XMLStreamException {
// Require no attributes // Require no attributes
ParseUtils.requireNoAttributes(reader); 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); list.add(addKeycloakSub);
while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { 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(); 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} * {@inheritDoc}
@ -60,8 +387,183 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader<Li
@Override @Override
public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException { public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException {
context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false); context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false);
writeSecureDeployment(writer, context.getModelNode());
writer.writeEndElement(); 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 $ * @version $Revision: 1 $
*/ */
public class AdapterConstants { 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.LoginConfig;
import io.undertow.servlet.api.ServletSessionConfig; import io.undertow.servlet.api.ServletSessionConfig;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.adapters.saml.AdapterConstants;
import org.keycloak.adapters.saml.DefaultSamlDeployment; import org.keycloak.adapters.saml.DefaultSamlDeployment;
import org.keycloak.adapters.saml.SamlConfigResolver; import org.keycloak.adapters.saml.SamlConfigResolver;
import org.keycloak.adapters.saml.SamlDeployment; import org.keycloak.adapters.saml.SamlDeployment;
@ -38,6 +39,7 @@ import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
import org.keycloak.saml.common.exceptions.ParsingException; import org.keycloak.saml.common.exceptions.ParsingException;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
import java.io.ByteArrayInputStream;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.InputStream; import java.io.InputStream;
@ -64,8 +66,16 @@ public class SamlServletExtension implements ServletExtension {
return false; 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) { private static InputStream getConfigInputStream(ServletContext context) {
InputStream is = null; InputStream is = getXMLFromServletContext(context);
if (is == null) { if (is == null) {
String path = context.getInitParameter("keycloak.config.file"); String path = context.getInitParameter("keycloak.config.file");
if (path == null) { if (path == null) {

View file

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

View file

@ -24,8 +24,8 @@
<relativePath>../../../../pom.xml</relativePath> <relativePath>../../../../pom.xml</relativePath>
</parent> </parent>
<artifactId>keycloak-saml-wf9-subsystem</artifactId> <artifactId>keycloak-saml-wildfly-subsystem</artifactId>
<name>Keycloak Wildfly 9 SAML Adapter Subsystem</name> <name>Keycloak Wildfly SAML Adapter Subsystem</name>
<description/> <description/>
<packaging>jar</packaging> <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; import org.jboss.modules.ModuleLoader;
/** /**
*
* @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/ */
public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor { 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 class KeycloakSamlExtension implements Extension {
public static final String SUBSYSTEM_NAME = "keycloak-saml"; 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(); private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; 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); 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) { public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
@ -68,7 +67,12 @@ public class KeycloakSamlExtension implements Extension {
public void initialize(final ExtensionContext context) { public void initialize(final ExtensionContext context) {
final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION); 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); subsystem.registerXMLElementWriter(PARSER);
} }

View file

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

View file

@ -14,7 +14,6 @@
* License for the specific language governing permissions and limitations under * License for the specific language governing permissions and limitations under
* the License. * the License.
*/ */
package org.keycloak.subsystem.adapter.saml.extension; package org.keycloak.subsystem.adapter.saml.extension;
import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; 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; 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. * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
*/ */
public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
protected KeycloakSubsystemDefinition() {
static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition();
private KeycloakSubsystemDefinition() {
super(KeycloakSamlExtension.SUBSYSTEM_PATH, super(KeycloakSamlExtension.SUBSYSTEM_PATH,
KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"), KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
KeycloakSubsystemAdd.INSTANCE, KeycloakSubsystemAdd.INSTANCE,
@ -41,5 +43,4 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
super.registerOperations(resourceRegistration); super.registerOperations(resourceRegistration);
resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); 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'?> <?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> <config>
<extension-module>org.keycloak.keycloak-saml-adapter-subsystem</extension-module> <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> </subsystem>
</config> </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);
}
}
}