\ No newline at end of file
diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml
index f15b757736..041f1c472b 100755
--- a/distribution/downloads/pom.xml
+++ b/distribution/downloads/pom.xml
@@ -248,7 +248,106 @@
tar.gz
- target/${project.version}/adapters
+ target/${project.version}/adapters/keycloak-oidc
+
+
+
+ saml-adapter-downloads
+ install
+
+ copy
+
+
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-eap6-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-eap6-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-jetty81-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-jetty81-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-jetty92-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-jetty92-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat6-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-tomcat6-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat7-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-tomcat7-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat8-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-tomcat8-adapter-dist
+ tar.gz
+
+
+
+ org.keycloak
+ keycloak-saml-wf9-adapter-dist
+ zip
+
+
+ org.keycloak
+ keycloak-saml-wf9-adapter-dist
+ tar.gz
+
+
+ target/${project.version}/adapters/saml
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..1acb6aa511
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/assembly.xml
@@ -0,0 +1,36 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ ${project.build.directory}/unpacked
+
+ org/keycloak/keycloak-common/**
+ org/keycloak/keycloak-saml-core/**
+ org/keycloak/keycloak-saml-adapter-core/**
+ org/keycloak/keycloak-adapter-spi/**
+ org/keycloak/keycloak-jboss-adapter-core/**
+ org/bouncycastle/**
+ org/keycloak/keycloak-saml-as7-adapter/**
+ org/keycloak/keycloak-saml-as7-subsystem/**
+ org/keycloak/keycloak-saml-adapter-subsystem/**
+
+
+ **/*.war
+
+ modules
+
+
+
+
+ ../../shared-cli/adapter-install.cli
+ bin
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml
new file mode 100755
index 0000000000..8cee17b18c
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-adapter-zip/pom.xml
@@ -0,0 +1,79 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../../pom.xml
+
+
+ keycloak-saml-as7-adapter-dist
+ pom
+ Keycloak SAML AS7 Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter
+
+
+ org.keycloak
+ keycloak-saml-as7-modules
+ zip
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack
+ prepare-package
+
+ unpack
+
+
+
+
+ org.keycloak
+ keycloak-saml-as7-modules
+ zip
+ ${project.build.directory}/unpacked
+
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml
new file mode 100755
index 0000000000..4a34435ac4
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/assembly.xml
@@ -0,0 +1,22 @@
+
+ dist
+
+
+ zip
+
+ false
+
+
+
+ ../../
+
+ License.html
+
+
+
+
+ ${project.build.directory}/modules
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml
new file mode 100755
index 0000000000..f62783b409
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/build.xml
@@ -0,0 +1,88 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml
new file mode 100755
index 0000000000..3d9438a4a4
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/lib.xml
@@ -0,0 +1,282 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+ project.setProperty("current.maven.root", root);
+ ]]>
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ";
+ if(path.indexOf('${') != -1) {
+ throw "Module resource root not found, make sure it is listed in build/pom.xml" + path;
+ }
+ if(attributes.get("jandex") == "true" ) {
+ root = root + "\n\t";
+ }
+ project.setProperty("current.resource.root", root);
+ ]]>
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml
new file mode 100755
index 0000000000..0f1b31776a
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/pom.xml
@@ -0,0 +1,179 @@
+
+
+
+ 4.0.0
+
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../../pom.xml
+
+
+ keycloak-saml-as7-modules
+
+ Keycloak SAML AS7 / JBoss EAP 6 Modules
+ pom
+
+
+ org.keycloak
+ keycloak-common
+
+
+ org.keycloak
+ keycloak-adapter-spi
+
+
+ org.keycloak
+ keycloak-as7-adapter-spi
+
+
+ org.keycloak
+ keycloak-saml-core
+
+
+ org.keycloak
+ keycloak-saml-adapter-core
+
+
+ org.keycloak
+ keycloak-saml-tomcat-adapter-core
+
+
+ org.keycloak
+ keycloak-jboss-adapter-core
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter
+
+
+ org.keycloak
+ keycloak-saml-as7-subsystem
+
+
+ org.keycloak
+ keycloak-tomcat-adapter-spi
+
+
+ org.keycloak
+ keycloak-jboss-adapter-core
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
+ org.bouncycastle
+ bcpkix-jdk15on
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-antrun-plugin
+ false
+
+
+ build-dist
+
+ run
+
+ compile
+
+
+
+
+
+
+
+
+
+
+
+ org.jboss
+ jandex
+ 1.0.3.Final
+
+
+ ant-contrib
+ ant-contrib
+ 1.0b3
+
+
+ ant
+ ant
+
+
+
+
+ org.apache.ant
+ ant-apache-bsf
+ 1.9.3
+
+
+ org.apache.bsf
+ bsf-api
+ 3.1
+
+
+ rhino
+ js
+ 1.7R2
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-resources-plugin
+
+
+ copy-resources
+
+ validate
+
+ copy-resources
+
+
+ ${project.build.directory}/modules/org/keycloak/keycloak-saml-adapter-subsystem
+
+
+ src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem
+ true
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/bouncycastle/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/bouncycastle/main/module.xml
new file mode 100755
index 0000000000..d8fcf474cb
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/bouncycastle/main/module.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
new file mode 100755
index 0000000000..3fb12cca44
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
new file mode 100755
index 0000000000..b7bcee4e97
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
new file mode 100755
index 0000000000..cf898253ff
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml
new file mode 100755
index 0000000000..f9399db215
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml
new file mode 100755
index 0000000000..edbea102fe
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml
new file mode 100755
index 0000000000..ebf3ce347d
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-adapter/main/module.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml
new file mode 100755
index 0000000000..8a14597b97
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-as7-subsystem/main/module.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml
new file mode 100755
index 0000000000..6a7cd653cd
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/as7-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..f844a41510
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/assembly.xml
@@ -0,0 +1,36 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ ${project.build.directory}/unpacked
+
+ org/keycloak/keycloak-common/**
+ org/keycloak/keycloak-saml-core/**
+ org/keycloak/keycloak-saml-adapter-core/**
+ org/keycloak/keycloak-adapter-spi/**
+ org/keycloak/keycloak-jboss-adapter-core/**
+ org/bouncycastle/**
+ org/keycloak/keycloak-saml-as7-adapter/**
+ org/keycloak/keycloak-saml-as7-subsystem/**
+ org/keycloak/keycloak-saml-adapter-subsystem/**
+
+
+ **/*.war
+
+ modules/system/layers/base
+
+
+
+
+ ../../shared-cli/adapter-install.cli
+ bin
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml
new file mode 100755
index 0000000000..3e004566ec
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/eap6-adapter-zip/pom.xml
@@ -0,0 +1,79 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../../pom.xml
+
+
+ keycloak-saml-eap6-adapter-dist
+ pom
+ Keycloak SAML JBoss EAP 6 Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-as7-adapter
+
+
+ org.keycloak
+ keycloak-as7-modules
+ zip
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-dependency-plugin
+
+
+ unpack
+ prepare-package
+
+ unpack
+
+
+
+
+ org.keycloak
+ keycloak-saml-as7-modules
+ zip
+ ${project.build.directory}/unpacked
+
+
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/as7-eap6-adapter/pom.xml b/distribution/saml-adapters/as7-eap6-adapter/pom.xml
new file mode 100755
index 0000000000..b608eab6a4
--- /dev/null
+++ b/distribution/saml-adapters/as7-eap6-adapter/pom.xml
@@ -0,0 +1,21 @@
+
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+ Keycloak SAML AS7 / JBoss EAP 6 Adapter Distros
+
+ 4.0.0
+
+ keycloak-saml-as7-eap6-adapter-dist-pom
+ pom
+
+
+ as7-modules
+ as7-adapter-zip
+ eap6-adapter-zip
+
+
diff --git a/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..d28d51a9ba
--- /dev/null
+++ b/distribution/saml-adapters/jetty81-adapter-zip/assembly.xml
@@ -0,0 +1,26 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-jetty81-adapter
+
+
+ org.eclipse.jetty:jetty-server
+ org.eclipse.jetty:jetty-util
+ org.eclipse.jetty:jetty-security
+
+ lib/keycloak
+
+
+
diff --git a/distribution/saml-adapters/jetty81-adapter-zip/pom.xml b/distribution/saml-adapters/jetty81-adapter-zip/pom.xml
new file mode 100755
index 0000000000..412a5c851b
--- /dev/null
+++ b/distribution/saml-adapters/jetty81-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-jetty81-adapter-dist
+ pom
+ Keycloak SAML Jetty 8.1.x Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-jetty81-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..13eb8e0e30
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/assembly.xml
@@ -0,0 +1,39 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+
+
+ keycloak.mod
+
+ modules
+
+
+ ${project.build.directory}/modules
+
+
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-jetty91-adapter
+
+
+ org.eclipse.jetty:jetty-server
+ org.eclipse.jetty:jetty-util
+ org.eclipse.jetty:jetty-security
+
+ lib/keycloak
+
+
+
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod b/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod
new file mode 100755
index 0000000000..10550d86ae
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/keycloak.mod
@@ -0,0 +1,11 @@
+#
+# Keycloak Jetty Adapter
+#
+
+[depend]
+server
+security
+
+[lib]
+lib/keycloak/*.jar
+
diff --git a/distribution/saml-adapters/jetty91-adapter-zip/pom.xml b/distribution/saml-adapters/jetty91-adapter-zip/pom.xml
new file mode 100755
index 0000000000..2c15be444a
--- /dev/null
+++ b/distribution/saml-adapters/jetty91-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-jetty91-adapter-dist
+ pom
+ Keycloak SAML Jetty 9.1.x Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-jetty91-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml b/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..08a4cba846
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/assembly.xml
@@ -0,0 +1,39 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+
+
+ keycloak.mod
+
+ modules
+
+
+ ${project.build.directory}/modules
+
+
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-jetty92-adapter
+
+
+ org.eclipse.jetty:jetty-server
+ org.eclipse.jetty:jetty-util
+ org.eclipse.jetty:jetty-security
+
+ lib/keycloak
+
+
+
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod b/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod
new file mode 100755
index 0000000000..10550d86ae
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/keycloak.mod
@@ -0,0 +1,11 @@
+#
+# Keycloak Jetty Adapter
+#
+
+[depend]
+server
+security
+
+[lib]
+lib/keycloak/*.jar
+
diff --git a/distribution/saml-adapters/jetty92-adapter-zip/pom.xml b/distribution/saml-adapters/jetty92-adapter-zip/pom.xml
new file mode 100755
index 0000000000..62881cc11c
--- /dev/null
+++ b/distribution/saml-adapters/jetty92-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-jetty92-adapter-dist
+ pom
+ Keycloak SAML Jetty 9.2.x Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-jetty92-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml
index 8e08bdf6ad..1a1c4b1db1 100755
--- a/distribution/saml-adapters/pom.xml
+++ b/distribution/saml-adapters/pom.xml
@@ -16,5 +16,13 @@
wf9-adapter
+ tomcat6-adapter-zip
+ tomcat7-adapter-zip
+ tomcat8-adapter-zip
+ jetty81-adapter-zip
+
+ jetty92-adapter-zip
+ as7-eap6-adapter
diff --git a/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..ae01a392bf
--- /dev/null
+++ b/distribution/saml-adapters/tomcat6-adapter-zip/assembly.xml
@@ -0,0 +1,24 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-tomcat6-adapter
+
+
+ org.apache.tomcat:catalina
+
+
+
+
+
diff --git a/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml
new file mode 100755
index 0000000000..89609d41c4
--- /dev/null
+++ b/distribution/saml-adapters/tomcat6-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-tomcat6-adapter-dist
+ pom
+ Keycloak SAML Tomcat 6 Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat6-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..6c205d17a7
--- /dev/null
+++ b/distribution/saml-adapters/tomcat7-adapter-zip/assembly.xml
@@ -0,0 +1,25 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-tomcat7-adapter
+
+
+ org.apache.tomcat:tomcat-servlet-api
+ org.apache.tomcat:tomcat-catalina
+
+
+
+
+
diff --git a/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml
new file mode 100755
index 0000000000..4486d7459a
--- /dev/null
+++ b/distribution/saml-adapters/tomcat7-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-tomcat7-adapter-dist
+ pom
+ Keycloak SAML Tomcat 7 Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat7-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml b/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml
new file mode 100755
index 0000000000..76665bc551
--- /dev/null
+++ b/distribution/saml-adapters/tomcat8-adapter-zip/assembly.xml
@@ -0,0 +1,25 @@
+
+ war-dist
+
+
+ zip
+ tar.gz
+
+ false
+
+
+
+ false
+ true
+ true
+
+ org.keycloak:keycloak-saml-tomcat8-adapter
+
+
+ org.apache.tomcat:tomcat-servlet-api
+ org.apache.tomcat:tomcat-catalina
+
+
+
+
+
diff --git a/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml b/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml
new file mode 100755
index 0000000000..13741dd5ca
--- /dev/null
+++ b/distribution/saml-adapters/tomcat8-adapter-zip/pom.xml
@@ -0,0 +1,51 @@
+
+ 4.0.0
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+
+ keycloak-saml-tomcat8-adapter-dist
+ pom
+ Keycloak SAML Tomcat 8 Adapter Distro
+
+
+
+
+ org.keycloak
+ keycloak-saml-tomcat8-adapter
+
+
+
+
+
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ assembly.xml
+
+
+ target
+
+
+ target/assembly/work
+
+ false
+
+
+
+
+
+
+
+
diff --git a/docbook/auth-server-docs/reference/en/en-US/master.xml b/docbook/auth-server-docs/reference/en/en-US/master.xml
index 7a798f8fcb..39a571abe5 100755
--- a/docbook/auth-server-docs/reference/en/en-US/master.xml
+++ b/docbook/auth-server-docs/reference/en/en-US/master.xml
@@ -94,6 +94,12 @@ This one is short
types are supported and how to configure and install them so that you can use Keycloak to secure
your applications.
+
+ These client adapters use an extension of the OpenID Connect protocol (a derivate of OAuth 2.0).
+ This extension provides support for clustering, backchannel logout, and other non-standard adminstrative functions.
+ The Keycloak project also provides a separate, standalone, generic, SAML client adapter. But that is describe in a separate
+ document and has a different download.
+
&AdapterConfig;
&JBossAdapter;
&TomcatAdapter;
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/master.xml b/docbook/saml-adapter-docs/reference/en/en-US/master.xml
index 51040ac463..55ce6608ca 100755
--- a/docbook/saml-adapter-docs/reference/en/en-US/master.xml
+++ b/docbook/saml-adapter-docs/reference/en/en-US/master.xml
@@ -1,6 +1,12 @@
+
+
+
+
+
]>
@@ -33,6 +39,13 @@ This one is short
+ &AdapterConfig;
+ &JBossAdapter;
+ &TomcatAdapter;
+ &Jetty9Adapter;
+ &Jetty8Adapter;
+ &Logout;
+
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
new file mode 100755
index 0000000000..1f741560ec
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/adapter-config.xml
@@ -0,0 +1,475 @@
+
+ General Adapter Config
+
+ Each SAML adapter supported by Keycloak can be configured by a simple XML text file. This is what one might
+ look like:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+
+
+ Some of these configuration switches may be adapter specific and some are common across all adapters.
+ For Java adapters you can use ${...} enclosure as System property replacement.
+ For example ${jboss.server.config.dir}.
+
+
+ SP Element
+
+ Here is the explanation of the SP element attributes
+
+
+...
+]]>
+
+
+
+ entityID
+
+
+ This is the identifier for this client. The IDP needs this value to determine
+ who the client is that is communicating with it.
+ REQUIRED.
+
+
+
+
+ sslPolicy
+
+
+ This is the SSL policy the adapter will enforce. Valid values are:
+ ALL, EXTERNAL, and NONE. For ALL, all requests must come in via HTTPS. For
+ EXTERNAL, only non-private IP addresses must come over the wire via HTTPS. For
+ NONE, no requests are required to come over via HTTPS. This is
+ OPTIONAL. and defaults to EXTERNAL.
+
+
+
+
+ nameIDPolicyFormat
+
+
+ SAML clients can request a specific NameID Subject format. Fill in this value
+ if you want a specific format. It must be a standard SAML format identifier, i.e.
+ urn:oasis:names:tc:SAML:2.0:nameid-format:transient
+ OPTIONAL.. By default, no special format is requested.
+
+
+
+
+ forceAuthentication
+
+
+ SAML clients can request that a user is re-authenticated even if
+ they are already logged in at the IDP. Set this to true if you
+ want this.
+ OPTIONAL.. Set to false by default.
+
+
+
+
+
+
+
+ SP Keys and Key elements
+
+ If the IDP requires that the SP sign all of its requests and/or if the IDP will
+ encrypt assertions, you must define the keys used to do this. For client signed
+ documents you must define both the private and public key or certificate that will
+ be used to sign documents. For encryption, you only have to define the private key
+ that will be used to decrypt.
+
+
+ There are two ways to describe your keys. Either they are stored within a Java KeyStore
+ or you can cut and paste the keys directly within keycloak-saml.xml
+ in the PEM format.
+
+
+
+
+
+
+
+
+
+]]>
+
+
+ The Key element has two optional attributes signing
+ and encryption. When set to true these tell the adapter what the
+ key will be used for. If both attributes are set to true, then the key will be used for both
+ signing documents and decrypting encrypted assertions. You must set at least one of these
+ attributes to true.
+
+
+ KeyStore element
+
+
+
+ file
+
+
+ File path to the key store.
+ OPTIONAL. The file or resource attribute
+ must be set.
+
+
+
+
+ resource
+
+
+ WAR resource path to the KeyStore. This is a path used in method call to ServletContext.getResourceAsStream().
+ OPTIONAL. The file or resource attribute
+ must be set.
+
+
+
+
+ password
+
+
+ The password of the KeyStore
+ REQUIRED.
+
+
+
+
+
+
+
+ You can and must also specify references to your private keys and certificates within
+ the Java KeyStore. The PrivateKey and Certificate
+ elements do this. The alias attribute defines the alias within the
+ KeyStore for the key. For PrivateKey, a password is required to access this key
+ specify that value in the password attribute.
+
+
+
+ Key PEMS
+
+ Within the Key element you alternatively declare your keys and certificates
+ directly using the sub elements PrivateKeyPem, PublicKeyPem, and
+ CertificatePem. The values contained in these elements must conform to the
+ PEM key format. You usually use this option if you are generating keys using openssl
+
+
+
+
+ SP PrincipalNameMapping element
+
+ This element is optional. When creating a Java Principal object that you obtain from
+ methods like HttpServletRequest.getUserPrincipal(), you can define what name that is returned
+ by the Principal.getName() method. The policy attribute defines the
+ policy used to populate this value. The values are FROM_NAME_ID. This policy
+ just grabs whatever the SAML subject value is. The other is FROM_ATTRIBUTE. This will
+ pull the value of Principal.getName() from one of the attributes in the SAML assertion received from the server.
+ The default value is FROM_NAME_ID.
+
+
+
+ RoleIdentifiers element
+
+
+
+
+
+]]>
+
+ This element is optional. It defines which SAML attribute values in the assertion should be
+ mapped to a Java EE role. By default Role attribute values are converted
+ to Java EE roles. Some IDPs send roles via a member or memberOf
+ attribute assertion. You define one or more Attribute elements to specify
+ which SAML attributes must be converted into roles.
+
+
+
+ IDP Element
+
+ Everything in the IDP element describes the settings for the IDP the SP is communicating
+ with.
+
+
+
+...
+]]>
+
+
+
+
+ entityID
+
+
+ This is the issuer ID of the IDP.
+ REQUIRED..
+
+
+
+
+ signaturesRequired
+
+
+ If set to true, the client adapter will sign every document
+ it sends to the IDP. Also, the client will expect that the IDP
+ will be signing an documents sent to it. This switch sets
+ the default for all request and response types, but you will see
+ later that you have some fine grain control over this.
+ OPTIONAL.
+
+
+
+
+ signatureAlgorithm
+
+
+ This is the signature algorithm that the IDP expects signed documents
+ to use
+ OPTIONAL.. The default value is RSA_SHA256, but
+ you can also use RSA_SHA1, RSA_256, RSA_512, and DSA_SHA1.
+
+
+
+
+ signatureCanonicalizationMethod
+
+
+ This is the signature canonicalization method that the IDP expects signed documents
+ to use
+ OPTIONAL.. The default value is http://www.w3.org/2001/10/xml-exc-c14n#
+ and should be good for most IDPs.
+
+
+
+
+
+
+
+ IDP SingleSignOnService sub element
+
+ The SignleSignOnService sub element defines the
+ login SAML endpoint of the IDP.
+
+
+]]>
+
+
+
+ signRequest
+
+
+ Should the client sign authn requests?
+ OPTIONAL.. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ validateResponseSignature
+
+
+ Should the client expect the IDP to sign the assertion response document
+ sent back from an auhtn request?
+ OPTIONAL. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ requestBinding
+
+
+ This is the SAML binding type used for communicating with the IDP
+ OPTIONAL.. The default value is POST, but
+ you can set it to REDIRECT as well.
+
+
+
+
+ responseBinding
+
+
+ SAML allows the client to request what binding type it wants authn responses
+ to use. The values of this can be POST or REDIRECT
+ OPTIONAL.. The default is that the client will not request
+ a specific binding type for responses.
+
+
+
+
+ bindingUrl
+
+
+ This is the URL for the ID login service that the client will send requests to.
+ REQUIRED..
+
+
+
+
+
+
+ IDP SingleSignOnService sub element
+
+ The SignleSignOnService sub element defines the
+ login SAML endpoint of the IDP.
+
+
+]]>
+
+
+
+ signRequest
+
+
+ Should the client sign logout requests it makes to the IDP?
+ OPTIONAL.. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ signResponse
+
+
+ Should the client sign logout responses it sends to the IDP requests?
+ OPTIONAL.. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ validateRequestSignature
+
+
+ Should the client expect signed logout request documents from the IDP?
+ OPTIONAL. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ validateResponseSignature
+
+
+ Should the client expect signed logout response documents from the IDP?
+ OPTIONAL. Defaults to whatever the
+ IDP signaturesRequired element value is.
+
+
+
+
+ requestBinding
+
+
+ This is the SAML binding type used for communicating SAML requests to the IDP
+ OPTIONAL.. The default value is POST, but
+ you can set it to REDIRECT as well.
+
+
+
+
+ responseBinding
+
+
+ This is the SAML binding type used for communicating SAML responses to the IDP
+ The values of this can be POST or REDIRECT
+ OPTIONAL.. The default value is POST, but
+ you can set it to REDIRECT as well.
+
+
+
+
+ postBindingUrl
+
+
+ This is the URL for the IDP's logout service when using the POST binding.
+ REQUIRED if using the POST binding at all.
+
+
+
+
+ redirectBindingUrl
+
+
+ This is the URL for the IDP's logout service when using the REDIRECT binding.
+ REQUIRED if using the REDIRECT binding at all.
+
+
+
+
+
+
+
+ IDP Keys subelement
+
+ The Keys sub element of IDP is only used to define the certificate or
+ public key to use to verify documents signed by the IDP. It is defined
+ in the same way as the SP's Key's element. But
+ again, you only have to define one certificate or public key reference.
+
+
+
+
+
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml
new file mode 100755
index 0000000000..fc29402c9d
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jboss-adapter.xml
@@ -0,0 +1,188 @@
+
+ JBoss/Wildfly Adapter
+
+ To be able to secure WAR apps deployed on JBoss EAP 6.x or Wildfly, you must install and
+ configure the Keycloak SAML Adapter Subsystem. You then provide a keycloak
+ config, /WEB-INF/keycloak-saml file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml.
+ Both methods are described in this section.
+
+
+ Adapter Installation
+
+ SAML Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+
+
+ Install on Wildfly 9:
+
+$ cd $WILDFLY_HOME
+$ unzip keycloak-saml-wf9-adapter-dist.zip
+
+
+
+ Install on JBoss EAP 6.x:
+
+$ cd $JBOSS_HOME
+$ unzip keycloak-saml-eap6-adapter-dist.zip
+
+
+
+ This zip file creates new JBoss Modules specific to the Wildfly Keycloak SAML Adapter within your Wildfly distro.
+
+
+ After adding the Keycloak modules, you must then enable the Keycloak SAML Subsystem within your app server's server configuration:
+ domain.xml or standalone.xml.
+
+
+ There is a CLI script that will help you modify your server configuration. Start the server and run the script
+ from the server's bin directory:
+
+$ cd $JBOSS_HOME/bin
+$ jboss-cli.sh -c --file=adapter-install.cli
+
+ The script will add the extension, subsystem, and optional security-domain as described below.
+
+
+
+
+
+
+ ...
+
+
+
+
+ ...
+
+]]>
+
+
+
+ The keycloak security domain should be used with EJBs and other components when you need the security context created
+ in the secured web tier to be propagated to the EJBs (other EE component) you are invoking. Otherwise
+ this configuration is optional.
+
+
+
+
+...
+
+
+
+
+
+
+]]>
+
+
+ For example, if you have a JAX-RS service that is an EJB within your WEB-INF/classes directory, you'll want
+ to annotate it with the @SecurityDomain annotation as follows:
+
+ getCustomers() {
+ return db.getCustomers();
+ }
+}
+]]>
+
+
+ We hope to improve our integration in the future so that you don't have to specify the @SecurityDomain
+ annotation when you want to propagate a keycloak security context to the EJB tier.
+
+
+
+
+ Required Per WAR Configuration
+
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+
+
+ The first thing you must do is create
+ a keycloak-saml.xml adapter config file within the WEB-INF directory
+ of your WAR. The format of this config file is describe in the general adapter configuration
+ section.
+
+
+ Next you must set the auth-method to KEYCLOAK-SAML in web.xml. You also
+ have to use standard servlet security to specify role-base constraints on your URLs. Here's an example
+ pulled from one of the examples that comes distributed with Keycloak.
+
+
+
+
+
+ customer-portal
+
+
+
+ Admins
+ /admin/*
+
+
+ admin
+
+
+ CONFIDENTIAL
+
+
+
+
+ Customers
+ /customers/*
+
+
+ user
+
+
+ CONFIDENTIAL
+
+
+
+
+ KEYCLOAK-SAML
+ this is ignored currently
+
+
+
+ admin
+
+
+ user
+
+
+]]>
+
+
+
+
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml
new file mode 100755
index 0000000000..dbbef0cb0a
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty8-adapter.xml
@@ -0,0 +1,49 @@
+
+ Jetty 8.1.x SAML Adapter
+
+ Keycloak has a separate SAML adapter for Jetty 8.1.x that you will have to install into your Jetty
+ installation. You then have to provide some extra configuration in each WAR you deploy to
+ Jetty. Let's go over these steps.
+
+
+ Adapter Installation
+
+ Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+
+
+ You must unzip the Jetty 8.1.x distro into Jetty 8.1.x's root directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work!
+
+
+
+$ cd $JETTY_HOME
+$ unzip keycloak-saml-jetty81-adapter-dist.zip
+
+
+
+ Next, you will have to enable the keycloak option. Edit start.ini and add keycloak to the options
+
+
+
+
+
+
+
+
+
+
+ Required Per WAR Configuration
+
+ Enabling Keycloak for your WARs is the same as the Jetty 9.x adapter. See Required Per WAR Configuration
+
+
+
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml
new file mode 100755
index 0000000000..44cd972de6
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/jetty9-adapter.xml
@@ -0,0 +1,106 @@
+
+ Jetty 9.x SAML Adapters
+
+ Keycloak has a separate SAML adapter for Jetty 9.x. You then have to provide some extra configuration in each WAR you deploy to
+ Jetty. Let's go over these steps.
+
+
+ Adapter Installation
+
+ Adapters are no longer included with the appliance or war distribution.Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+
+
+ You must unzip the Jetty 9.x distro into Jetty 9.x's root directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work!
+
+
+
+$ cd $JETTY_HOME
+$ unzip keycloak-saml-jetty92-adapter-dist.zip
+
+
+
+ Next, you will have to enable the keycloak module for your jetty.base.
+
+
+
+$ cd your-base
+$ java -jar $JETTY_HOME/start.jar --add-to-startd=keycloak
+
+
+
+
+
+
+ Required Per WAR Configuration
+
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+
+
+ The first thing you must do is create a WEB-INF/jetty-web.xml file in your WAR package. This is
+ a Jetty specific config file and you must define a Keycloak specific authenticator within it.
+
+
+
+
+
+
+
+
+
+
+
+]]>
+
+
+ Next you must create
+ a keycloak-saml.xml adapter config file within the WEB-INF directory
+ of your WAR. The format of this config file is describe in the general adapter configuration
+ section.
+
+
+ Finally you must specify both a login-config and use standard servlet security to specify
+ role-base constraints on your URLs. Here's an example:
+
+
+
+
+
+ customer-portal
+
+
+
+ Customers
+ /*
+
+
+ user
+
+
+ CONFIDENTIAL
+
+
+
+
+ BASIC
+ this is ignored currently
+
+
+
+ admin
+
+
+ user
+
+
+]]>
+
+
+
+
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml
new file mode 100755
index 0000000000..e258d60fd8
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/logout.xml
@@ -0,0 +1,10 @@
+
+ Logout
+
+ There are multiple ways you can logout from a web application. For Java EE servlet containers, you can call
+ HttpServletRequest.logout().
+ For any other browser application, you can point the browser at any url of your web application that has
+ a security constraing and pass in a query parameter GLO, i.e. http://myapp?GLO=true.
+ This will log you out if you have an SSO session with your browser.
+
+
\ No newline at end of file
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml
new file mode 100755
index 0000000000..322aadb059
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/multi-tenancy.xml
@@ -0,0 +1,56 @@
+
+ Multi Tenancy
+
+ Multi Tenancy, in our context, means that one single target application (WAR) can be secured by a single (or clustered) Keycloak server, authenticating
+ its users against different realms. In practice, this means that one application needs to use different keycloak.json files.
+ For this case, there are two possible solutions:
+
+
+
+ The same WAR file deployed under two different names, each with its own Keycloak configuration (probably via the Keycloak Subsystem).
+ This scenario is suitable when the number of realms is known in advance or when there's a dynamic provision of application instances.
+ One example would be a service provider that dynamically creates servers/deployments for their clients, like a PaaS.
+
+
+
+ A WAR file deployed once (possibly in a cluster), that decides which realm to authenticate against based on the request parameters.
+ This scenario is suitable when there are an undefined number of realms. One example would be a SaaS provider that have only one deployment
+ (perhaps in a cluster) serving several companies, differentiating between clients based on the hostname
+ (client1.acme.com, client2.acme.com) or path (/app/client1/,
+ /app/client2/) or even via a special HTTP Header.
+
+
+
+
+ This chapter of the reference guide focus on this second scenario.
+
+
+
+ Keycloak provides an extension point for applications that need to evaluate the realm on a request basis. During the authentication
+ and authorization phase of the incoming request, Keycloak queries the application via this extension point and expects the application
+ to return a complete representation of the realm. With this, Keycloak then proceeds the authentication and authorization process,
+ accepting or refusing the request based on the incoming credentials and on the returned realm.
+
+ For this scenario, an application needs to:
+
+
+
+
+ Add a context parameter to the web.xml, named keycloak.config.resolver.
+ The value of this property should be the fully qualified name of the class extending
+ org.keycloak.adapters.KeycloakConfigResolver.
+
+
+
+ A concrete implementation of org.keycloak.adapters.KeycloakConfigResolver. Keycloak will call the
+ resolve(org.keycloak.adapters.HttpFacade.Request) method and expects a complete
+ org.keycloak.adapters.KeycloakDeployment in response. Note that Keycloak will call this for every request,
+ so, take the usual performance precautions.
+
+
+
+
+
+ An implementation of this feature can be found in the examples.
+
+
diff --git a/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
new file mode 100755
index 0000000000..f6db1b038e
--- /dev/null
+++ b/docbook/saml-adapter-docs/reference/en/en-US/modules/tomcat-adapter.xml
@@ -0,0 +1,92 @@
+
+ Tomcat 6, 7 and 8 SAML dapters
+
+ To be able to secure WAR apps deployed on Tomcat 6, 7 and 8 you must install the Keycloak Tomcat 6, 7 or 8 SAML adapter
+ into your Tomcat installation. You then have to provide some extra configuration in each WAR you deploy to
+ Tomcat. Let's go over these steps.
+
+
+ Adapter Installation
+
+ Adapters are no longer included with the appliance or war distribution. Each adapter is a separate download on
+ the Keycloak download site. They are also available as a maven artifact.
+
+
+ You must unzip the adapter distro into Tomcat's lib/ directory. Including
+ adapter's jars within your WEB-INF/lib directory will not work! The Keycloak SAML adapter is implemented as a Valve
+ and valve code must reside in Tomcat's main lib/ directory.
+
+
+
+$ cd $TOMCAT_HOME/lib
+$ unzip keycloak-saml-tomcat6-adapter-dist.zip
+ or
+$ unzip keycloak-saml-tomcat7-adapter-dist.zip
+ or
+$ unzip keycloak-saml-tomcat8-adapter-dist.zip
+
+
+
+
+
+ Required Per WAR Configuration
+
+ This section describes how to secure a WAR directly by adding config and editing files within your WAR package.
+
+
+ The first thing you must do is create a META-INF/context.xml file in your WAR package. This is
+ a Tomcat specific config file and you must define a Keycloak specific Valve.
+
+
+
+
+]]>
+
+
+ Next you must create
+ a keycloak-saml.xml adapter config file within the WEB-INF directory
+ of your WAR. The format of this config file is describe in the general adapter configuration
+ section.
+
+
+ Finally you must specify both a login-config and use standard servlet security to specify
+ role-base constraints on your URLs. Here's an example:
+
+
+
+
+
+ customer-portal
+
+
+
+ Customers
+ /*
+
+
+ user
+
+
+
+
+ BASIC
+ this is ignored currently
+
+
+
+ admin
+
+
+ user
+
+
+]]>
+
+
+
+
\ No newline at end of file
diff --git a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
index c3132e2168..a094acf3f4 100755
--- a/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
+++ b/examples/saml/post-with-encryption/src/main/webapp/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
-
+
-
+
-
+
-
+
-
+
-
+
keycloak-saml-tomcat6-adapter
${project.version}
+
+ org.keycloak
+ keycloak-saml-as7-adapter
+ ${project.version}
+
+
+ org.keycloak
+ keycloak-saml-as7-subsystem
+ ${project.version}
+ org.keycloakkeycloak-saml-tomcat7-adapter
@@ -1098,6 +1108,12 @@
${project.version}zip
+
+ org.keycloak
+ keycloak-saml-as7-modules
+ ${project.version}
+ zip
+ org.keycloakkeycloak-as7-modules
@@ -1253,6 +1269,48 @@
${project.version}zip
+
+ org.keycloak
+ keycloak-saml-tomcat6-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-tomcat7-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-tomcat8-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-jetty81-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-jetty92-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter-dist
+ ${project.version}
+ zip
+
+
+ org.keycloak
+ keycloak-saml-eap6-adapter-dist
+ ${project.version}
+ zip
+ org.keycloakkeycloak-testsuite-integration
diff --git a/saml/client-adapter/as7-eap6/adapter/pom.xml b/saml/client-adapter/as7-eap6/adapter/pom.xml
new file mode 100755
index 0000000000..06833435d6
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/adapter/pom.xml
@@ -0,0 +1,101 @@
+
+
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../../pom.xml
+
+ 4.0.0
+
+ keycloak-saml-as7-adapter
+ Keycloak SAML AS7 Integration
+
+
+
+
+ org.keycloak
+ keycloak-common
+
+
+ org.keycloak
+ keycloak-as7-adapter-spi
+
+
+ org.keycloak
+ keycloak-adapter-spi
+
+
+ org.keycloak
+ keycloak-saml-adapter-core
+
+
+ org.keycloak
+ keycloak-saml-adapter-core
+
+
+ org.bouncycastle
+ bcprov-jdk15on
+
+
+ org.jboss.spec.javax.servlet
+ jboss-servlet-api_3.0_spec
+ provided
+
+
+ org.jboss.logging
+ jboss-logging
+ ${jboss.logging.version}
+ provided
+
+
+ org.jboss.web
+ jbossweb
+ 7.0.17.Final
+ provided
+
+
+ org.jboss.as
+ jboss-as-web
+ 7.1.2.Final
+ provided
+
+
+ org.keycloak
+ keycloak-saml-tomcat-adapter-core
+
+
+ org.apache.tomcat
+ tomcat-servlet-api
+
+
+ org.apache.tomcat
+ tomcat-catalina
+
+
+ org.apache.tomcat
+ catalina
+
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+
+
+
+
+
+
diff --git a/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java b/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java
new file mode 100755
index 0000000000..17ad74b6c4
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/adapter/src/main/java/org/keycloak/adapters/saml/jbossweb/SamlAuthenticatorValve.java
@@ -0,0 +1,57 @@
+package org.keycloak.adapters.saml.jbossweb;
+
+import org.apache.catalina.LifecycleException;
+import org.apache.catalina.connector.Request;
+import org.apache.catalina.connector.Response;
+import org.apache.catalina.core.StandardContext;
+import org.apache.catalina.deploy.LoginConfig;
+import org.keycloak.adapters.jbossweb.JBossWebPrincipalFactory;
+import org.keycloak.adapters.saml.AbstractSamlAuthenticatorValve;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.tomcat.GenericPrincipalFactory;
+
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * Keycloak authentication valve
+ *
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class SamlAuthenticatorValve extends AbstractSamlAuthenticatorValve {
+ public boolean authenticate(Request request, HttpServletResponse response, LoginConfig config) throws java.io.IOException {
+ return authenticateInternal(request, response, config);
+ }
+
+ @Override
+ protected boolean forwardToErrorPageInternal(Request request, HttpServletResponse response, Object loginConfig) throws IOException {
+ if (loginConfig == null) return false;
+ LoginConfig config = (LoginConfig)loginConfig;
+ if (config.getErrorPage() == null) return false;
+ forwardToErrorPage(request, (Response)response, config);
+ return true;
+ }
+
+ @Override
+ protected void forwardToLogoutPage(Request request, HttpServletResponse response, SamlDeployment deployment) {
+ super.forwardToLogoutPage(request, response, deployment);
+ }
+
+ @Override
+ public void start() throws LifecycleException {
+ StandardContext standardContext = (StandardContext) context;
+ standardContext.addLifecycleListener(this);
+ super.start();
+ }
+
+
+ public void logout(Request request) {
+ logoutInternal(request);
+ }
+
+ @Override
+ protected GenericPrincipalFactory createPrincipalFactory() {
+ return new JBossWebPrincipalFactory();
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/pom.xml b/saml/client-adapter/as7-eap6/pom.xml
new file mode 100755
index 0000000000..c6bca52f89
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/pom.xml
@@ -0,0 +1,20 @@
+
+
+ keycloak-parent
+ org.keycloak
+ 1.6.0.Final-SNAPSHOT
+ ../../../pom.xml
+
+ Keycloak SAML EAP Integration
+
+ 4.0.0
+
+ keycloak-saml-eap-integration-pom
+ pom
+
+
+ adapter
+ subsystem
+
+
diff --git a/saml/client-adapter/as7-eap6/subsystem/pom.xml b/saml/client-adapter/as7-eap6/subsystem/pom.xml
new file mode 100755
index 0000000000..198c360625
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/pom.xml
@@ -0,0 +1,115 @@
+
+
+
+ 4.0.0
+
+
+ org.keycloak
+ keycloak-parent
+ 1.6.0.Final-SNAPSHOT
+ ../../../../pom.xml
+
+
+ keycloak-saml-as7-subsystem
+ Keycloak SAML AS7 Subsystem
+
+ jar
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ 2.8.1
+
+ false
+ true
+ -Xmx512m
+
+
+ jboss.home
+ ${jboss.home}
+
+
+
+ **/*TestCase.java
+
+ once
+
+
+
+
+
+
+
+ org.keycloak
+ keycloak-saml-as7-adapter
+
+
+
+ org.jboss.as
+ jboss-as-naming
+ ${jboss.version}
+
+
+
+ org.jboss.as
+ jboss-as-server
+ ${jboss.version}
+
+
+
+ org.jboss.as
+ jboss-as-ee
+ ${jboss.version}
+
+
+
+ org.jboss.as
+ jboss-as-web
+ ${jboss.version}
+
+
+
+ org.jboss.logging
+ jboss-logging
+ 3.1.0.GA
+
+
+
+ org.jboss.logging
+ jboss-logging-processor
+
+ provided
+ true
+ 1.0.0.Final
+
+
+
+ org.jboss.msc
+ jboss-msc
+ 1.0.2.GA
+
+
+
+ junit
+ junit
+ test
+
+
+
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java
new file mode 100755
index 0000000000..31008d4662
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakAdapterConfigDeploymentProcessor.java
@@ -0,0 +1,93 @@
+/*
+ * 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.saml.as7;
+
+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.deployment.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.jboss.ValveMetaData;
+import org.jboss.metadata.web.spec.LoginConfigMetaData;
+import org.keycloak.adapters.saml.jbossweb.SamlAuthenticatorValve;
+
+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);
+
+ // This param name is defined again in Keycloak Undertow Integration class
+ // org.keycloak.adapters.undertow.KeycloakServletExtension. We have this value in
+ // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration.
+ public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.saml.adapterConfig";
+
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+ String deploymentName = deploymentUnit.getName();
+
+ // if it's not a web-app there's nothing to secure
+ WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
+ if (warMetaData == null) {
+ return;
+ }
+ JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
+ if (webMetaData == null) {
+ webMetaData = new JBossWebMetaData();
+ warMetaData.setMergedJBossWebMetaData(webMetaData);
+ }
+
+ // otherwise
+ LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
+
+ boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod());
+
+ if (webRequiresKC) {
+ log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName);
+ addValve(webMetaData);
+ }
+ }
+
+ private void addValve(JBossWebMetaData webMetaData) {
+ List valves = webMetaData.getValves();
+ if (valves == null) {
+ valves = new ArrayList(1);
+ webMetaData.setValves(valves);
+ }
+ ValveMetaData valve = new ValveMetaData();
+ valve.setValveClass(SamlAuthenticatorValve.class.getName());
+ valve.setModule("org.keycloak.keycloak-saml-as7-adapter");
+ //log.info("******* adding Keycloak valve to: " + deploymentName);
+ valves.add(valve);
+ }
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java
new file mode 100755
index 0000000000..c214774a3c
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessor.java
@@ -0,0 +1,67 @@
+/*
+ * 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;
+
+import org.jboss.as.server.deployment.Attachments;
+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.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.Module;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
+
+ private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
+ private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-core");
+ private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
+
+ @Override
+ public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
+ final DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
+
+ // Next phase, need to detect if this is a Keycloak deployment. If not, don't add the modules.
+
+ final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
+ final ModuleLoader moduleLoader = Module.getBootModuleLoader();
+ addCommonModules(moduleSpecification, moduleLoader);
+ addPlatformSpecificModules(moduleSpecification, moduleLoader);
+ }
+
+ private void addCommonModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+ // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
+ }
+
+ abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
+
+ @Override
+ public void undeploy(DeploymentUnit du) {
+
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java
new file mode 100755
index 0000000000..700fc828c6
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakDependencyProcessorAS7.java
@@ -0,0 +1,19 @@
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.server.deployment.module.ModuleDependency;
+import org.jboss.as.server.deployment.module.ModuleSpecification;
+import org.jboss.modules.ModuleIdentifier;
+import org.jboss.modules.ModuleLoader;
+
+/**
+ * @author Marko Strukelj
+ */
+public class KeycloakDependencyProcessorAS7 extends KeycloakDependencyProcessor {
+
+ private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-as7-adapter");
+
+ @Override
+ protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader) {
+ // ModuleDependency(ModuleLoader moduleLoader, ModuleIdentifier identifier, boolean optional, boolean export, boolean importServices, boolean userSpecified)
+ moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_AS7_ADAPTER, false, false, true, false));
+ }}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java
new file mode 100755
index 0000000000..c52f2b5f7c
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSamlExtension.java
@@ -0,0 +1,76 @@
+/*
+ * 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;
+
+import org.jboss.as.controller.Extension;
+import org.jboss.as.controller.ExtensionContext;
+import org.jboss.as.controller.PathElement;
+import org.jboss.as.controller.ResourceDefinition;
+import org.jboss.as.controller.SubsystemRegistration;
+import org.jboss.as.controller.descriptions.StandardResourceDescriptionResolver;
+import org.jboss.as.controller.parsing.ExtensionParsingContext;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUBSYSTEM;
+
+
+/**
+ * Main Extension class for the subsystem.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSamlExtension implements Extension {
+
+ public static final String SUBSYSTEM_NAME = "keycloak-saml";
+ public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6";
+ private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser();
+ static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions";
+ private static final int MGMT_API_VERSION_MAJOR = 1;
+ private static final int MGMT_API_VERSION_MINOR = 1;
+
+ static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME);
+ private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition();
+
+ public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) {
+ StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME);
+ for (String kp : keyPrefix) {
+ prefix.append('.').append(kp);
+ }
+ return new StandardResourceDescriptionResolver(prefix.toString(), RESOURCE_NAME, KeycloakSamlExtension.class.getClassLoader(), true, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initializeParsers(final ExtensionParsingContext context) {
+ context.setSubsystemXmlMapping(SUBSYSTEM_NAME, KeycloakSamlExtension.NAMESPACE, PARSER);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void initialize(final ExtensionContext context) {
+ final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION_MAJOR, MGMT_API_VERSION_MINOR);
+
+ ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE);
+
+ subsystem.registerXMLElementWriter(PARSER);
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java
new file mode 100755
index 0000000000..2a7fd55242
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemAdd.java
@@ -0,0 +1,59 @@
+/*
+ * 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;
+
+
+import org.jboss.as.controller.AbstractBoottimeAddStepHandler;
+import org.jboss.as.controller.OperationContext;
+import org.jboss.as.controller.OperationFailedException;
+import org.jboss.as.controller.ServiceVerificationHandler;
+import org.jboss.as.server.AbstractDeploymentChainStep;
+import org.jboss.as.server.DeploymentProcessorTarget;
+import org.jboss.as.server.deployment.Phase;
+import org.jboss.dmr.ModelNode;
+import org.jboss.msc.service.ServiceController;
+
+import java.util.List;
+
+/**
+ * The Keycloak subsystem add update handler.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler {
+
+ static final KeycloakSubsystemAdd INSTANCE = new KeycloakSubsystemAdd();
+
+ @Override
+ protected void performBoottime(final OperationContext context, ModelNode operation, final ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) {
+ context.addStep(new AbstractDeploymentChainStep() {
+ @Override
+ protected void execute(DeploymentProcessorTarget processorTarget) {
+ processorTarget.addDeploymentProcessor(Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessorAS7());
+ processorTarget.addDeploymentProcessor(
+ Phase.POST_MODULE, // PHASE
+ Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY
+ new KeycloakAdapterConfigDeploymentProcessor());
+ }
+ }, OperationContext.Stage.RUNTIME);
+ }
+
+ @Override
+ protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException {
+ model.setEmptyObject();
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java
new file mode 100755
index 0000000000..400822e40d
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemDefinition.java
@@ -0,0 +1,47 @@
+/*
+ * 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.saml.as7;
+
+import org.jboss.as.controller.ReloadRequiredRemoveStepHandler;
+import org.jboss.as.controller.SimpleResourceDefinition;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler;
+import org.jboss.as.controller.registry.ManagementResourceRegistration;
+import org.jboss.as.controller.registry.OperationEntry;
+
+/**
+ * Definition of subsystem=keycloak.
+ *
+ * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc.
+ */
+public class KeycloakSubsystemDefinition extends SimpleResourceDefinition {
+ protected KeycloakSubsystemDefinition() {
+ super(KeycloakSamlExtension.SUBSYSTEM_PATH,
+ KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"),
+ KeycloakSubsystemAdd.INSTANCE,
+ ReloadRequiredRemoveStepHandler.INSTANCE
+ );
+ }
+
+ @Override
+ public void registerOperations(ManagementResourceRegistration resourceRegistration) {
+ super.registerOperations(resourceRegistration);
+ resourceRegistration.registerOperationHandler(ModelDescriptionConstants.DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE);
+ }
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java
new file mode 100755
index 0000000000..14899e1d7b
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeycloakSubsystemParser.java
@@ -0,0 +1,67 @@
+/*
+ * 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.saml.as7;
+
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.parsing.ParseUtils;
+import org.jboss.as.controller.persistence.SubsystemMarshallingContext;
+import org.jboss.dmr.ModelNode;
+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.List;
+
+/**
+ * The subsystem parser, which uses stax to read and write to and from xml
+ */
+class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter {
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void readElement(final XMLExtendedStreamReader reader, final List list) throws XMLStreamException {
+ // Require no attributes
+ ParseUtils.requireNoAttributes(reader);
+ ModelNode addKeycloakSub = org.jboss.as.controller.operations.common.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();
+ }
+
+
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java
new file mode 100755
index 0000000000..e73f338b42
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Util.java
@@ -0,0 +1,42 @@
+package org.keycloak.subsystem.saml.as7;
+
+import org.jboss.as.controller.PathAddress;
+import org.jboss.as.controller.descriptions.ModelDescriptionConstants;
+import org.jboss.dmr.ModelNode;
+
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP;
+import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
+
+/**
+ * @author Bill Burke
+ * @version $Revision: 1 $
+ */
+public class Util {
+ public static ModelNode createAddOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.ADD, address);
+ }
+
+ public static ModelNode createAddOperation() {
+ return createEmptyOperation(ModelDescriptionConstants.ADD, null);
+ }
+
+ public static ModelNode createRemoveOperation(final PathAddress address) {
+ return createOperation(ModelDescriptionConstants.REMOVE, address);
+ }
+
+ public static ModelNode createOperation(final String operationName, final PathAddress address) {
+ return createEmptyOperation(operationName, address);
+ }
+
+ public static ModelNode createEmptyOperation(String operationName, final PathAddress address) {
+ ModelNode op = new ModelNode();
+ op.get(OP).set(operationName);
+ if (address != null) {
+ op.get(OP_ADDR).set(address.toModelNode());
+ } else {
+ // Just establish the standard structure; caller can fill in address later
+ op.get(OP_ADDR);
+ }
+ return op;
+ }
+}
diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
new file mode 100755
index 0000000000..2fd653a56d
--- /dev/null
+++ b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension
@@ -0,0 +1 @@
+org.keycloak.subsystem.saml.as7.KeycloakSamlExtension
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
index bebb506903..953c4cf6db 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlAuthenticator.java
@@ -292,15 +292,14 @@ public abstract class SamlAuthenticator {
}
}
}
- if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE_NAME) {
+ if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
if (deployment.getPrincipalAttributeName() != null) {
String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
if (attribute != null) principalName = attribute;
- }
- } else if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_FRIENDLY_ATTRIBUTE_NAME) {
- if (deployment.getPrincipalAttributeName() != null) {
- String attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
- if (attribute != null) principalName = attribute;
+ else {
+ attribute = friendlyAttributes.getFirst(deployment.getPrincipalAttributeName());
+ if (attribute != null) principalName = attribute;
+ }
}
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
index 681e405a99..258f14bca3 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/SamlDeployment.java
@@ -67,8 +67,7 @@ public interface SamlDeployment {
enum PrincipalNamePolicy {
FROM_NAME_ID,
- FROM_ATTRIBUTE_NAME,
- FROM_FRIENDLY_ATTRIBUTE_NAME
+ FROM_ATTRIBUTE
}
PrincipalNamePolicy getPrincipalNamePolicy();
String getPrincipalAttributeName();
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
index fdb8284675..404fc5822a 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/ConfigXmlConstants.java
@@ -37,7 +37,7 @@ public class ConfigXmlConstants {
public static final String ATTRIBUTE_ATTR = "attribute";
- public static final String ROLE_MAPPING_ELEMENT = "RoleMapping";
+ public static final String ROLE_IDENTIFIERS_ELEMENT = "RoleIdentifiers";
public static final String ATTRIBUTE_ELEMENT = "Attribute";
public static final String NAME_ATTR = "name";
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
index 0421fda4b2..0e92bb703c 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/DeploymentBuilder.java
@@ -20,6 +20,8 @@ import java.security.KeyStoreException;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.cert.Certificate;
+import java.util.HashSet;
+import java.util.Set;
/**
* @author Bill Burke
@@ -52,6 +54,11 @@ public class DeploymentBuilder {
deployment.setPrincipalAttributeName(sp.getPrincipalNameMapping().getAttributeName());
}
deployment.setRoleAttributeNames(sp.getRoleAttributes());
+ if (sp.getRoleAttributes() == null) {
+ Set roles = new HashSet<>();
+ roles.add("Role");
+ deployment.setRoleAttributeNames(roles);
+ }
if (sp.getSslPolicy() != null) {
SslRequired ssl = SslRequired.valueOf(sp.getSslPolicy());
deployment.setSslRequired(ssl);
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
index fc08e449a3..d1aaea9c66 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/IDPXmlParser.java
@@ -24,16 +24,16 @@ public class IDPXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.IDP_ELEMENT);
IDP idp = new IDP();
- String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ String entityID = SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on IDP");
}
idp.setEntityID(entityID);
- boolean signaturesRequired = StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
- idp.setSignatureCanonicalizationMethod(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
- idp.setSignatureAlgorithm(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
+ boolean signaturesRequired = SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNATURES_REQUIRED_ATTR);
+ idp.setSignatureCanonicalizationMethod(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_CANONICALIZATION_METHOD_ATTR));
+ idp.setSignatureAlgorithm(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.SIGNATURE_ALGORITHM_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -73,25 +73,25 @@ public class IDPXmlParser extends AbstractParser {
protected IDP.SingleLogoutService parseSingleLogoutService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleLogoutService slo = new IDP.SingleLogoutService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- slo.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
- slo.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
- slo.setValidateRequestSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
- slo.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
- slo.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
- slo.setSignResponse(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
- slo.setPostBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
- slo.setRedirectBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
+ slo.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ slo.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+ slo.setValidateRequestSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_REQUEST_SIGNATURE_ATTR, signaturesRequired));
+ slo.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ slo.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ slo.setSignResponse(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_RESPONSE_ATTR, signaturesRequired));
+ slo.setPostBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.POST_BINDING_URL_ATTR));
+ slo.setRedirectBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REDIRECT_BINDING_URL_ATTR));
return slo;
}
protected IDP.SingleSignOnService parseSingleSignOnService(XMLEventReader xmlEventReader, boolean signaturesRequired) throws ParsingException {
IDP.SingleSignOnService sso = new IDP.SingleSignOnService();
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- sso.setSignRequest(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
- sso.setValidateResponseSignature(StaxParserUtil.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
- sso.setRequestBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
- sso.setResponseBinding(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
- sso.setBindingUrl(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
+ sso.setSignRequest(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.SIGN_REQUEST_ATTR, signaturesRequired));
+ sso.setValidateResponseSignature(SPXmlParser.getBooleanAttributeValue(element, ConfigXmlConstants.VALIDATE_RESPONSE_SIGNATURE_ATTR, signaturesRequired));
+ sso.setRequestBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.REQUEST_BINDING_ATTR));
+ sso.setResponseBinding(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.RESPONSE_BINDING_ATTR));
+ sso.setBindingUrl(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.BINDING_URL_ATTR));
return sso;
}
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
index 6fbd8d037f..0308a56a22 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/KeyXmlParser.java
@@ -7,8 +7,6 @@ import org.keycloak.saml.common.util.StaxParserUtil;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
-import javax.xml.stream.XMLStreamException;
-import javax.xml.stream.events.Characters;
import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
@@ -24,8 +22,8 @@ public class KeyXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEY_ELEMENT);
Key key = new Key();
- key.setSigning(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
- key.setEncryption(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
+ key.setSigning(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.SIGNING_ATTR));
+ key.setEncryption(SPXmlParser.getBooleanAttributeValue(startElement, ConfigXmlConstants.ENCRYPTION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -46,13 +44,13 @@ public class KeyXmlParser extends AbstractParser {
key.setKeystore(parseKeyStore(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.CERTIFICATE_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setCertificatePem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setCertificatePem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PUBLIC_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setPublicKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setPublicKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_PEM_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- key.setPrivateKeyPem(StaxParserUtil.getElementText(xmlEventReader));
+ key.setPrivateKeyPem(SPXmlParser.getElementText(xmlEventReader));
} else {
StaxParserUtil.bypassElementBlock(xmlEventReader, tag);
}
@@ -65,14 +63,14 @@ public class KeyXmlParser extends AbstractParser {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.KEYS_STORE_ELEMENT);
Key.KeyStoreConfig keyStore = new Key.KeyStoreConfig();
- keyStore.setType(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
- keyStore.setAlias(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
- keyStore.setFile(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
- keyStore.setResource(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
+ keyStore.setType(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.TYPE_ATTR));
+ keyStore.setAlias(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setFile(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.FILE_ATTR));
+ keyStore.setResource(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.RESOURCE_ATTR));
if (keyStore.getFile() == null && keyStore.getResource() == null) {
throw new ParsingException("KeyStore element must have the url or classpath attribute set");
}
- keyStore.setPassword(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
+ keyStore.setPassword(SPXmlParser.getAttributeValue(startElement, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPassword() == null) {
throw new ParsingException("KeyStore element must have the password attribute set");
}
@@ -97,19 +95,19 @@ public class KeyXmlParser extends AbstractParser {
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.CERTIFICATE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- keyStore.setCertificateAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setCertificateAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getCertificateAlias() == null) {
throw new ParsingException("KeyStore Certificate element must have the alias attribute set");
}
} else if (tag.equals(ConfigXmlConstants.PRIVATE_KEY_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- keyStore.setPrivateKeyAlias(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
+ keyStore.setPrivateKeyAlias(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.ALIAS_ATTR));
if (keyStore.getPrivateKeyAlias() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the alias attribute set");
}
- keyStore.setPrivateKeyPassword(StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
+ keyStore.setPrivateKeyPassword(SPXmlParser.getAttributeValue(element, ConfigXmlConstants.PASSWORD_ATTR));
if (keyStore.getPrivateKeyPassword() == null) {
throw new ParsingException("KeyStore PrivateKey element must have the password attribute set");
diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
index f0104707b4..ef67c45791 100755
--- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
+++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/config/parsers/SPXmlParser.java
@@ -6,6 +6,7 @@ import org.keycloak.adapters.saml.config.SP;
import org.keycloak.saml.common.exceptions.ParsingException;
import org.keycloak.saml.common.parsers.AbstractParser;
import org.keycloak.saml.common.util.StaxParserUtil;
+import org.keycloak.util.StringPropertyReplacer;
import javax.xml.namespace.QName;
import javax.xml.stream.XMLEventReader;
@@ -13,7 +14,6 @@ import javax.xml.stream.events.EndElement;
import javax.xml.stream.events.StartElement;
import javax.xml.stream.events.XMLEvent;
import java.util.HashSet;
-import java.util.LinkedList;
import java.util.List;
import java.util.Set;
@@ -23,21 +23,44 @@ import java.util.Set;
*/
public class SPXmlParser extends AbstractParser {
+ public static String getAttributeValue(StartElement startElement, String tag) {
+ String str = StaxParserUtil.getAttributeValue(startElement, tag);
+ if (str != null) return StringPropertyReplacer.replaceProperties(str);
+ else return str;
+ }
+
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag, boolean defaultValue) {
+ String result = getAttributeValue(startElement, tag);
+ if (result == null) return defaultValue;
+ return Boolean.valueOf(result);
+ }
+
+ public static boolean getBooleanAttributeValue(StartElement startElement, String tag) {
+ return getBooleanAttributeValue(startElement, tag, false);
+ }
+
+ public static String getElementText(XMLEventReader xmlEventReader) throws ParsingException {
+ String result = StaxParserUtil.getElementText(xmlEventReader);
+ if (result != null) result = StringPropertyReplacer.replaceProperties(result);
+ return result;
+ }
+
+
@Override
public Object parse(XMLEventReader xmlEventReader) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
StaxParserUtil.validate(startElement, ConfigXmlConstants.SP_ELEMENT);
SP sp = new SP();
- String entityID = StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
+ String entityID = getAttributeValue(startElement, ConfigXmlConstants.ENTITY_ID_ATTR);
if (entityID == null) {
throw new ParsingException("entityID must be set on SP");
}
sp.setEntityID(entityID);
- sp.setSslPolicy(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
- sp.setLogoutPage(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
- sp.setNameIDPolicyFormat(StaxParserUtil.getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
- sp.setForceAuthentication(StaxParserUtil.getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
+ sp.setSslPolicy(getAttributeValue(startElement, ConfigXmlConstants.SSL_POLICY_ATTR));
+ sp.setLogoutPage(getAttributeValue(startElement, ConfigXmlConstants.LOGOUT_PAGE_ATTR));
+ sp.setNameIDPolicyFormat(getAttributeValue(startElement, ConfigXmlConstants.NAME_ID_POLICY_FORMAT_ATTR));
+ sp.setForceAuthentication(getBooleanAttributeValue(startElement, ConfigXmlConstants.FORCE_AUTHENTICATION_ATTR));
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
if (xmlEvent == null)
@@ -60,18 +83,18 @@ public class SPXmlParser extends AbstractParser {
sp.setKeys(keys);
} else if (tag.equals(ConfigXmlConstants.PRINCIPAL_NAME_MAPPING_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- String policy = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
+ String policy = getAttributeValue(element, ConfigXmlConstants.POLICY_ATTR);
if (policy == null) {
throw new ParsingException("PrincipalNameMapping element must have the policy attribute set");
}
- String attribute = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
+ String attribute = getAttributeValue(element, ConfigXmlConstants.ATTRIBUTE_ATTR);
SP.PrincipalNameMapping mapping = new SP.PrincipalNameMapping();
mapping.setPolicy(policy);
mapping.setAttributeName(attribute);
sp.setPrincipalNameMapping(mapping);
- } else if (tag.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT)) {
+ } else if (tag.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT)) {
parseRoleMapping(xmlEventReader, sp);
} else if (tag.equals(ConfigXmlConstants.IDP_ELEMENT)) {
IDPXmlParser parser = new IDPXmlParser();
@@ -87,7 +110,7 @@ public class SPXmlParser extends AbstractParser {
protected void parseRoleMapping(XMLEventReader xmlEventReader, SP sp) throws ParsingException {
StartElement startElement = StaxParserUtil.getNextStartElement(xmlEventReader);
- StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_MAPPING_ELEMENT);
+ StaxParserUtil.validate(startElement, ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT);
Set roleAttributes = new HashSet<>();
while (xmlEventReader.hasNext()) {
XMLEvent xmlEvent = StaxParserUtil.peek(xmlEventReader);
@@ -96,7 +119,7 @@ public class SPXmlParser extends AbstractParser {
if (xmlEvent instanceof EndElement) {
EndElement endElement = (EndElement) StaxParserUtil.getNextEvent(xmlEventReader);
String endElementName = StaxParserUtil.getEndElementName(endElement);
- if (endElementName.equals(ConfigXmlConstants.ROLE_MAPPING_ELEMENT))
+ if (endElementName.equals(ConfigXmlConstants.ROLE_IDENTIFIERS_ELEMENT))
break;
else
continue;
@@ -107,7 +130,7 @@ public class SPXmlParser extends AbstractParser {
String tag = StaxParserUtil.getStartElementName(startElement);
if (tag.equals(ConfigXmlConstants.ATTRIBUTE_ELEMENT)) {
StartElement element = StaxParserUtil.getNextStartElement(xmlEventReader);
- String attributeValue = StaxParserUtil.getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
+ String attributeValue = getAttributeValue(element, ConfigXmlConstants.NAME_ATTR);
if (attributeValue == null) {
throw new ParsingException("RoleMapping Attribute element must have the name attribute set");
diff --git a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
index b9e0799e97..534c9aef56 100755
--- a/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
+++ b/saml/client-adapter/core/src/main/resources/schema/keycloak_saml_adapter_1_6.xsd
@@ -25,7 +25,7 @@
-
+
@@ -70,7 +70,7 @@
-
+
diff --git a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
index 5f881975e7..ef910dceca 100755
--- a/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
+++ b/saml/client-adapter/core/src/test/resources/keycloak-saml.xml
@@ -20,9 +20,9 @@
-
+
-
+
tomcat
jettywildfly
+ as7-eap6
diff --git a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
index 4dad97304d..febbb72830 100755
--- a/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
+++ b/saml/client-adapter/tomcat/tomcat-core/src/main/java/org/keycloak/adapters/saml/AbstractSamlAuthenticatorValve.java
@@ -167,7 +167,7 @@ public abstract class AbstractSamlAuthenticatorValve extends FormAuthenticator i
try {
- disp.forward(request, response);
+ disp.forward(request.getRequest(), response);
} catch (ServletException e) {
throw new RuntimeException(e);
} catch (IOException e) {
diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
index d0a50eb4ac..2dad307529 100755
--- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
+++ b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java
@@ -36,7 +36,6 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-saml-adapter-core");
- private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
private static final ModuleIdentifier KEYCLOAK_COMMON = ModuleIdentifier.create("org.keycloak.keycloak-common");
@Override
@@ -56,7 +55,6 @@ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProce
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_COMMON, false, false, false, false));
- moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
}
abstract protected void addPlatformSpecificModules(ModuleSpecification moduleSpecification, ModuleLoader moduleLoader);
diff --git a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
index 8460a13d76..e042f3c9b7 100755
--- a/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
+++ b/testsuite/integration/src/test/resources/keycloak-saml/bad-client-signed-post/WEB-INF/keycloak-saml.xml
@@ -13,9 +13,9 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
+