From 38d160fab2e0f1400d3fc3faa27319059dc76a08 Mon Sep 17 00:00:00 2001 From: Marko Strukelj Date: Mon, 9 Nov 2015 15:12:01 +0100 Subject: [PATCH] KEYCLOAK-1924 SAML adapter full subsystem EAP/Wildfly --- distribution/demo-dist/assembly.xml | 2 +- distribution/demo-dist/pom.xml | 6 +- .../demo-dist/src/main/xslt/standalone.xsl | 2 +- distribution/downloads/pom.xml | 4 +- distribution/saml-adapters/pom.xml | 2 +- .../{wf9-adapter => wildfly-adapter}/pom.xml | 8 +- .../wildfly-adapter-zip}/assembly.xml | 2 +- .../wildfly-adapter-zip}/pom.xml | 8 +- .../wildfly-modules}/assembly.xml | 0 .../wildfly-modules}/build.xml | 4 +- .../wildfly-modules}/lib.xml | 0 .../wildfly-modules}/pom.xml | 6 +- .../keycloak-adapter-spi/main/module.xml | 0 .../keycloak/keycloak-common/main/module.xml | 0 .../main/module.xml | 0 .../main/module.xml | 0 .../main/module.xml | 2 +- .../keycloak-saml-core/main/module.xml | 0 .../main/module.xml | 0 .../main/module.xml | 0 .../main/module.xml | 2 +- .../en/en-US/modules/jboss-adapter.xml | 96 ++- pom.xml | 6 +- saml/client-adapter/as7-eap6/adapter/pom.xml | 4 - .../client-adapter/as7-eap6/subsystem/pom.xml | 6 +- .../subsystem/saml/as7/Configuration.java | 60 ++ .../subsystem/saml/as7/Constants.java | 120 ++++ .../saml/as7/IdentityProviderAddHandler.java | 41 ++ .../saml/as7/IdentityProviderDefinition.java | 106 ++++ .../subsystem/saml/as7/KeyAddHandler.java | 41 ++ .../subsystem/saml/as7/KeyDefinition.java | 122 ++++ .../as7/KeyStoreCertificateDefinition.java | 36 ++ .../saml/as7/KeyStoreDefinition.java | 73 +++ .../as7/KeyStorePrivateKeyDefinition.java | 52 ++ ...cloakAdapterConfigDeploymentProcessor.java | 80 ++- .../saml/as7/KeycloakSamlExtension.java | 19 +- .../saml/as7/KeycloakSubsystemAdd.java | 18 +- .../saml/as7/KeycloakSubsystemDefinition.java | 13 +- .../saml/as7/KeycloakSubsystemParser.java | 506 +++++++++++++++- .../saml/as7/SecureDeploymentAddHandler.java | 42 ++ .../saml/as7/SecureDeploymentDefinition.java | 44 ++ .../saml/as7/ServiceProviderAddHandler.java | 43 ++ .../saml/as7/ServiceProviderDefinition.java | 125 ++++ .../saml/as7/SingleLogoutDefinition.java | 84 +++ .../saml/as7/SingleSignOnDefinition.java | 68 +++ .../saml/as7/logging/KeycloakLogger.java | 49 ++ .../saml/as7/logging/KeycloakMessages.java | 34 ++ .../as7/xml/FormattingXMLStreamWriter.java | 534 ++++++++++++++++ .../subsystem/saml/as7/xml/Spliterator.java | 66 ++ .../saml/as7/LocalDescriptions.properties | 63 ++ .../schema/wildfly-keycloak-saml_1_1.xsd | 268 +++++++++ .../subsystem/saml/as7/keycloak-saml-1.1.xml | 49 ++ .../adapters/saml/AdapterConstants.java | 2 +- .../saml/undertow/SamlServletExtension.java | 12 +- saml/client-adapter/wildfly/pom.xml | 2 +- .../pom.xml | 4 +- .../adapter/saml/extension/Configuration.java | 56 ++ .../adapter/saml/extension/Constants.java | 120 ++++ .../extension/IdentityProviderAddHandler.java | 37 ++ .../extension/IdentityProviderDefinition.java | 106 ++++ .../adapter/saml/extension/KeyAddHandler.java | 37 ++ .../adapter/saml/extension/KeyDefinition.java | 122 ++++ .../KeyStoreCertificateDefinition.java | 36 ++ .../saml/extension/KeyStoreDefinition.java | 73 +++ .../KeyStorePrivateKeyDefinition.java | 52 ++ ...cloakAdapterConfigDeploymentProcessor.java | 128 ++++ .../KeycloakDependencyProcessor.java | 1 - .../KeycloakDependencyProcessorWildFly.java | 0 .../saml/extension/KeycloakSamlExtension.java | 12 +- .../saml/extension/KeycloakSubsystemAdd.java | 1 - .../KeycloakSubsystemDefinition.java | 9 +- .../extension/KeycloakSubsystemParser.java | 569 ++++++++++++++++++ .../extension/SecureDeploymentAddHandler.java | 38 ++ .../extension/SecureDeploymentDefinition.java | 47 ++ .../extension/ServiceProviderAddHandler.java | 39 ++ .../extension/ServiceProviderDefinition.java | 125 ++++ .../extension/SingleLogoutDefinition.java | 84 +++ .../extension/SingleSignOnDefinition.java | 68 +++ .../extension/logging/KeycloakLogger.java | 45 ++ .../extension/logging/KeycloakMessages.java | 34 ++ .../org.jboss.as.controller.Extension | 0 .../extension/LocalDescriptions.properties | 63 ++ .../schema/wildfly-keycloak-saml_1_1.xsd | 268 +++++++++ .../keycloak-saml-adapter.xml} | 4 +- .../extension/SubsystemParsingTestCase.java | 56 ++ .../saml/extension/keycloak-saml-1.1-err.xml | 50 ++ .../saml/extension/keycloak-saml-1.1.xml | 50 ++ ...cloakAdapterConfigDeploymentProcessor.java | 53 -- .../extension/KeycloakSubsystemParser.java | 91 --- 89 files changed, 5174 insertions(+), 236 deletions(-) rename distribution/saml-adapters/{wf9-adapter => wildfly-adapter}/pom.xml (73%) rename distribution/saml-adapters/{wf9-adapter/wf9-adapter-zip => wildfly-adapter/wildfly-adapter-zip}/assembly.xml (94%) rename distribution/saml-adapters/{wf9-adapter/wf9-adapter-zip => wildfly-adapter/wildfly-adapter-zip}/pom.xml (90%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/assembly.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/build.xml (96%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/lib.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/pom.xml (97%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml (92%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules => wildfly-adapter/wildfly-modules}/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml (100%) rename distribution/saml-adapters/{wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wf9-subsystem => wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem}/main/module.xml (98%) create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Configuration.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderAddHandler.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyAddHandler.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreCertificateDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStorePrivateKeyDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentAddHandler.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderAddHandler.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleLogoutDefinition.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleSignOnDefinition.java create mode 100755 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakLogger.java create mode 100755 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakMessages.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/FormattingXMLStreamWriter.java create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/Spliterator.java create mode 100755 saml/client-adapter/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd create mode 100644 saml/client-adapter/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.1.xml rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/pom.xml (97%) create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Configuration.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderAddHandler.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyAddHandler.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreCertificateDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStorePrivateKeyDefinition.java create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java (99%) rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessorWildFly.java (100%) rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java (82%) rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java (99%) rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java (90%) create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentAddHandler.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderAddHandler.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleLogoutDefinition.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleSignOnDefinition.java create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakLogger.java create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakMessages.java rename saml/client-adapter/wildfly/{wildfly9-subsystem => wildfly-subsystem}/src/main/resources/META-INF/services/org.jboss.as.controller.Extension (100%) create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd rename saml/client-adapter/wildfly/{wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml => wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml} (68%) create mode 100755 saml/client-adapter/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1-err.xml create mode 100644 saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1.xml delete mode 100755 saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java delete mode 100755 saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java diff --git a/distribution/demo-dist/assembly.xml b/distribution/demo-dist/assembly.xml index f1c39c2d14..a06ed135df 100755 --- a/distribution/demo-dist/assembly.xml +++ b/distribution/demo-dist/assembly.xml @@ -40,7 +40,7 @@ - ${project.build.directory}/unpacked/keycloak-saml-wf9-adapter-${project.version} + ${project.build.directory}/unpacked/keycloak-saml-wildfly-adapter-${project.version} keycloak standalone/configuration/standalone-keycloak.xml diff --git a/distribution/demo-dist/pom.xml b/distribution/demo-dist/pom.xml index 94434786b5..baebb362fc 100755 --- a/distribution/demo-dist/pom.xml +++ b/distribution/demo-dist/pom.xml @@ -26,7 +26,7 @@ org.keycloak - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist zip @@ -116,9 +116,9 @@ org.keycloak - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist zip - ${project.build.directory}/unpacked/keycloak-saml-wf9-adapter-${project.version} + ${project.build.directory}/unpacked/keycloak-saml-wildfly-adapter-${project.version} diff --git a/distribution/demo-dist/src/main/xslt/standalone.xsl b/distribution/demo-dist/src/main/xslt/standalone.xsl index 106c08f304..9f52547f68 100755 --- a/distribution/demo-dist/src/main/xslt/standalone.xsl +++ b/distribution/demo-dist/src/main/xslt/standalone.xsl @@ -44,7 +44,7 @@ auth - + diff --git a/distribution/downloads/pom.xml b/distribution/downloads/pom.xml index b9daadf569..9e91285d51 100755 --- a/distribution/downloads/pom.xml +++ b/distribution/downloads/pom.xml @@ -338,12 +338,12 @@ org.keycloak - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist zip org.keycloak - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist tar.gz diff --git a/distribution/saml-adapters/pom.xml b/distribution/saml-adapters/pom.xml index 456f292e82..ea95663b13 100755 --- a/distribution/saml-adapters/pom.xml +++ b/distribution/saml-adapters/pom.xml @@ -15,7 +15,7 @@ pom - wf9-adapter + wildfly-adapter tomcat6-adapter-zip tomcat7-adapter-zip tomcat8-adapter-zip diff --git a/distribution/saml-adapters/wf9-adapter/pom.xml b/distribution/saml-adapters/wildfly-adapter/pom.xml similarity index 73% rename from distribution/saml-adapters/wf9-adapter/pom.xml rename to distribution/saml-adapters/wildfly-adapter/pom.xml index dd9b619032..689bcc087f 100755 --- a/distribution/saml-adapters/wf9-adapter/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/pom.xml @@ -6,15 +6,15 @@ 1.7.0.Final-SNAPSHOT ../../../pom.xml - Keycloak Wildfly 9 SAML Adapter + Keycloak Wildfly SAML Adapter 4.0.0 - keycloak-saml-wf9-adapter-dist-pom + keycloak-saml-wildfly-adapter-dist-pom pom - wf9-modules - wf9-adapter-zip + wildfly-modules + wildfly-adapter-zip diff --git a/distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/assembly.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml similarity index 94% rename from distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/assembly.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml index df413f239b..26126510f2 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/assembly.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml @@ -18,7 +18,7 @@ org/keycloak/keycloak-jboss-adapter-core/** org/keycloak/keycloak-saml-undertow-adapter/** org/keycloak/keycloak-saml-wildfly-adapter/** - org/keycloak/keycloak-saml-wf9-subsystem/** + org/keycloak/keycloak-saml-wildfly-subsystem/** org/keycloak/keycloak-saml-adapter-subsystem/** diff --git a/distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml similarity index 90% rename from distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/pom.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml index dfe6f0bd33..1dd7224cdc 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-adapter-zip/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/pom.xml @@ -8,15 +8,15 @@ ../../../../pom.xml - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist pom - Keycloak SAML Wildfly 9 Adapter Distro + Keycloak SAML Wildfly Adapter Distro org.keycloak - keycloak-saml-wf9-modules + keycloak-saml-wildfly-modules zip @@ -37,7 +37,7 @@ org.keycloak - keycloak-saml-wf9-modules + keycloak-saml-wildfly-modules zip ${project.build.directory}/unpacked diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/assembly.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/assembly.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/assembly.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/assembly.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/build.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml similarity index 96% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/build.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml index ab5da84157..81330abc83 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-modules/build.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml @@ -69,8 +69,8 @@ - - + + diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/lib.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/lib.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/lib.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/lib.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml similarity index 97% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/pom.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml index 906360a33f..80f06a0a77 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-modules/pom.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml @@ -12,9 +12,9 @@ ../../../../pom.xml - keycloak-saml-wf9-modules + keycloak-saml-wildfly-modules - Keycloak SAML Wildfly 9 Modules + Keycloak SAML Wildfly Modules pom @@ -51,7 +51,7 @@ org.keycloak - keycloak-saml-wf9-subsystem + keycloak-saml-wildfly-subsystem diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-adapter-spi/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-common/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-jboss-adapter-core/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-core/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml similarity index 92% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml index cc692a6b74..12ffc3a1d3 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-adapter-subsystem/main/module.xml @@ -30,6 +30,6 @@ - + diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-core/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-undertow-adapter/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml similarity index 100% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-adapter/main/module.xml diff --git a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wf9-subsystem/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml similarity index 98% rename from distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wf9-subsystem/main/module.xml rename to distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml index 363c2f3cd9..98b24834b2 100755 --- a/distribution/saml-adapters/wf9-adapter/wf9-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wf9-subsystem/main/module.xml +++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-subsystem/main/module.xml @@ -22,7 +22,7 @@ ~ 02110-1301 USA, or see the FSF site: http://www.fsf.org. --> - + 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 index fc29402c9d..df07d8dbbb 100755 --- 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 @@ -3,7 +3,7 @@ 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. + config, /WEB-INF/keycloak-saml.xml file in your WAR and change the auth-method to KEYCLOAK-SAML within web.xml. Both methods are described in this section.
@@ -13,10 +13,10 @@ the Keycloak download site. They are also available as a maven artifact. - Install on Wildfly 9: + Install on Wildfly 9 or 10: $ cd $WILDFLY_HOME -$ unzip keycloak-saml-wf9-adapter-dist.zip +$ unzip keycloak-saml-wildfly-adapter-dist.zip @@ -52,7 +52,7 @@ $ jboss-cli.sh -c --file=adapter-install.cli - + ... ]]> @@ -185,4 +185,92 @@ public class CustomerService {
+
+ Securing WARs via Keycloak SAML Subsystem + + You do not have to crack open a WAR to secure it with Keycloak. Alternatively, you can externally secure + it via the Keycloak SAML Adapter Subsystem. While you don't have to specify KEYCLOAK-SAML as an auth-method, + you still have to define the security-constraints in web.xml. You do + not, however, have to create a WEB-INF/keycloak-saml.xml file. This metadata is instead defined + within XML in your server's domain.xml or standalone.xml subsystem + configuration section. + + + + + + + + + + + ... + + + + +]]> + + + + The secure-deployment name attribute identifies the WAR you want + to secure. Its value is the module-name defined in web.xml with + .war appended. The rest of the configuration uses the same XML syntax as + keycloak-saml.xml configuration defined in general adapter configuration. + + + An example configuration: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +
\ No newline at end of file diff --git a/pom.xml b/pom.xml index 62ae25c7e4..b81ec574ed 100755 --- a/pom.xml +++ b/pom.xml @@ -1048,7 +1048,7 @@
org.keycloak - keycloak-saml-wf9-subsystem + keycloak-saml-wildfly-subsystem ${project.version} @@ -1143,7 +1143,7 @@ org.keycloak - keycloak-saml-wf9-modules + keycloak-saml-wildfly-modules ${project.version} zip @@ -1155,7 +1155,7 @@ org.keycloak - keycloak-saml-wf9-adapter-dist + keycloak-saml-wildfly-adapter-dist ${project.version} zip diff --git a/saml/client-adapter/as7-eap6/adapter/pom.xml b/saml/client-adapter/as7-eap6/adapter/pom.xml index 6d1ace80b7..da632c7322 100755 --- a/saml/client-adapter/as7-eap6/adapter/pom.xml +++ b/saml/client-adapter/as7-eap6/adapter/pom.xml @@ -30,10 +30,6 @@ org.keycloak keycloak-saml-adapter-core - - org.keycloak - keycloak-saml-adapter-core - org.bouncycastle bcprov-jdk15on diff --git a/saml/client-adapter/as7-eap6/subsystem/pom.xml b/saml/client-adapter/as7-eap6/subsystem/pom.xml index fa87dcf156..872e5d1110 100755 --- a/saml/client-adapter/as7-eap6/subsystem/pom.xml +++ b/saml/client-adapter/as7-eap6/subsystem/pom.xml @@ -101,9 +101,9 @@ projects that depend on this project.--> - org.jboss.msc - jboss-msc - 1.0.2.GA + org.jboss.as + jboss-as-controller + ${jboss.version} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Configuration.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Configuration.java new file mode 100644 index 0000000000..966cd67a5e --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Configuration.java @@ -0,0 +1,60 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.Property; + +/** + * @author Marko Strukelj + */ +public class Configuration { + + static final Configuration INSTANCE = new Configuration(); + + private ModelNode config = new ModelNode(); + + private Configuration() { + } + + void updateModel(ModelNode operation, ModelNode model) { + ModelNode node = config; + ModelNode addr = operation.get("address"); + for (Property item : addr.asPropertyList()) { + node = getNodeForAddressElement(node, item); + } + node.set(model); + } + + private ModelNode getNodeForAddressElement(ModelNode node, Property item) { + String key = item.getValue().asString(); + ModelNode keymodel = node.get(item.getName()); + return keymodel.get(key); + } + + public ModelNode getSecureDeployment(String name) { + ModelNode secureDeployment = config.get("subsystem").get("keycloak-saml").get(Constants.Model.SECURE_DEPLOYMENT); + if (secureDeployment.hasDefined(name)) { + return secureDeployment.get(name); + } + return null; + } + + public boolean isSecureDeployment(String name) { + return getSecureDeployment(name) != null; + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java new file mode 100644 index 0000000000..07af4f727e --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/Constants.java @@ -0,0 +1,120 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + + +/** + * @author Marko Strukelj + */ +public class Constants { + + static class Model { + static final String SECURE_DEPLOYMENT = "secure-deployment"; + static final String SERVICE_PROVIDER = "service-provider"; + + static final String SSL_POLICY = "ssl-policy"; + static final String NAME_ID_POLICY_FORMAT = "name-id-policy-format"; + static final String LOGOUT_PAGE = "logout-page"; + static final String FORCE_AUTHENTICATION = "force-authentication"; + static final String ROLE_ATTRIBUTES = "role-attributes"; + static final String SIGNING = "signing"; + static final String ENCRYPTION = "encryption"; + static final String KEY = "key"; + static final String RESOURCE = "resource"; + static final String PASSWORD = "password"; + + static final String PRIVATE_KEY_ALIAS = "private-key-alias"; + static final String PRIVATE_KEY_PASSWORD = "private-key-password"; + static final String CERTIFICATE_ALIAS = "certificate-alias"; + static final String KEY_STORE = "key-store"; + static final String SIGN_REQUEST = "sign-request"; + static final String VALIDATE_RESPONSE_SIGNATURE = "validate-response-signature"; + static final String REQUEST_BINDING = "request-binding"; + static final String BINDING_URL = "binding-url"; + static final String VALIDATE_REQUEST_SIGNATURE = "validate-request-signature"; + static final String SIGN_RESPONSE = "sign-response"; + static final String RESPONSE_BINDING = "response-binding"; + static final String POST_BINDING_URL = "post-binding-url"; + static final String REDIRECT_BINDING_URL = "redirect-binding-url"; + static final String SINGLE_SIGN_ON = "single-sign-on"; + static final String SINGLE_LOGOUT = "single-logout"; + static final String IDENTITY_PROVIDER = "identity-provider"; + static final String PRINCIPAL_NAME_MAPPING_POLICY = "principal-name-mapping-policy"; + static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "principal-name-mapping-attribute-name"; + static final String SIGNATURE_ALGORITHM = "signature-algorithm"; + static final String SIGNATURE_CANONICALIZATION_METHOD = "signature-canonicalization-method"; + static final String PRIVATE_KEY_PEM = "private-key-pem"; + static final String PUBLIC_KEY_PEM = "public-key-pem"; + static final String CERTIFICATE_PEM = "certificate-pem"; + static final String TYPE = "type"; + static final String ALIAS = "alias"; + static final String FILE = "file"; + static final String SIGNATURES_REQUIRED = "signatures-required"; + } + + + static class XML { + static final String SECURE_DEPLOYMENT = "secure-deployment"; + static final String SERVICE_PROVIDER = "SP"; + + static final String NAME = "name"; + static final String ENTITY_ID = "entityID"; + static final String SSL_POLICY = "sslPolicy"; + static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat"; + static final String LOGOUT_PAGE = "logoutPage"; + static final String FORCE_AUTHENTICATION = "forceAuthentication"; + static final String ROLE_IDENTIFIERS = "RoleIdentifiers"; + static final String SIGNING = "signing"; + static final String ENCRYPTION = "encryption"; + static final String KEYS = "Keys"; + static final String KEY = "Key"; + static final String RESOURCE = "resource"; + static final String PASSWORD = "password"; + static final String KEY_STORE = "KeyStore"; + static final String PRIVATE_KEY = "PrivateKey"; + static final String CERTIFICATE = "Certificate"; + + static final String PRIVATE_KEY_ALIAS = "alias"; + static final String PRIVATE_KEY_PASSWORD = "password"; + static final String CERTIFICATE_ALIAS = "alias"; + static final String SIGN_REQUEST = "signRequest"; + static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature"; + static final String REQUEST_BINDING = "requestBinding"; + static final String BINDING_URL = "bindingUrl"; + static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature"; + static final String SIGN_RESPONSE = "signResponse"; + static final String RESPONSE_BINDING = "responseBinding"; + static final String POST_BINDING_URL = "postBindingUrl"; + static final String REDIRECT_BINDING_URL = "redirectBindingUrl"; + static final String SINGLE_SIGN_ON = "SingleSignOnService"; + static final String SINGLE_LOGOUT = "SingleLogoutService"; + static final String IDENTITY_PROVIDER = "IDP"; + static final String PRINCIPAL_NAME_MAPPING = "PrincipalNameMapping"; + static final String PRINCIPAL_NAME_MAPPING_POLICY = "policy"; + static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "attribute"; + static final String ATTRIBUTE = "Attribute"; + static final String SIGNATURE_ALGORITHM = "signatureAlgorithm"; + static final String SIGNATURE_CANONICALIZATION_METHOD = "signatureCanonicalizationMethod"; + static final String PRIVATE_KEY_PEM = "PrivateKeyPem"; + static final String PUBLIC_KEY_PEM = "PublicKeyPem"; + static final String CERTIFICATE_PEM = "CertificatePem"; + static final String TYPE = "type"; + static final String ALIAS = "alias"; + static final String FILE = "file"; + static final String SIGNATURES_REQUIRED = "signaturesRequired"; + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderAddHandler.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderAddHandler.java new file mode 100644 index 0000000000..679658bc3e --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderAddHandler.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.ServiceVerificationHandler; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceController; + +import java.util.List; + +/** + * @author Marko Strukelj + */ +class IdentityProviderAddHandler extends AbstractAddStepHandler { + + IdentityProviderAddHandler() { + super(IdentityProviderDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderDefinition.java new file mode 100644 index 0000000000..09262f951c --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/IdentityProviderDefinition.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ObjectTypeAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class IdentityProviderDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SIGNATURES_REQUIRED = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURES_REQUIRED, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGNATURES_REQUIRED) + .build(); + + static final SimpleAttributeDefinition SIGNATURE_ALGORITHM = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_ALGORITHM, ModelType.STRING, true) + .setXmlName(Constants.XML.SIGNATURE_ALGORITHM) + .build(); + + static final SimpleAttributeDefinition SIGNATURE_CANONICALIZATION_METHOD = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_CANONICALIZATION_METHOD, ModelType.STRING, true) + .setXmlName(Constants.XML.SIGNATURE_CANONICALIZATION_METHOD) + .build(); + + static final ObjectTypeAttributeDefinition SINGLE_SIGN_ON = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_SIGN_ON, + SingleSignOnDefinition.ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final ObjectTypeAttributeDefinition SINGLE_LOGOUT = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_LOGOUT, + SingleLogoutDefinition.ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD}; + + static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD, SINGLE_SIGN_ON, SINGLE_LOGOUT}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static final IdentityProviderDefinition INSTANCE = new IdentityProviderDefinition(); + + private IdentityProviderDefinition() { + super(PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.IDENTITY_PROVIDER), + new IdentityProviderAddHandler(), + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} \ No newline at end of file diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyAddHandler.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyAddHandler.java new file mode 100644 index 0000000000..b362d4f579 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyAddHandler.java @@ -0,0 +1,41 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.ServiceVerificationHandler; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceController; + +import java.util.List; + +/** + * @author Marko Strukelj + */ +class KeyAddHandler extends AbstractAddStepHandler { + + KeyAddHandler() { + super(KeyDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyDefinition.java new file mode 100644 index 0000000000..0f76399d28 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyDefinition.java @@ -0,0 +1,122 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ObjectTypeAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class KeyDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SIGNING = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNING, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGNING) + .build(); + + static final SimpleAttributeDefinition ENCRYPTION = + new SimpleAttributeDefinitionBuilder(Constants.Model.ENCRYPTION, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.ENCRYPTION) + .build(); + + static final SimpleAttributeDefinition PRIVATE_KEY_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_PEM) + .build(); + + static final SimpleAttributeDefinition PUBLIC_KEY_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.PUBLIC_KEY_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.PUBLIC_KEY_PEM) + .build(); + + static final SimpleAttributeDefinition CERTIFICATE_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.CERTIFICATE_PEM) + .build(); + + static final ObjectTypeAttributeDefinition KEY_STORE = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.KEY_STORE, + KeyStoreDefinition.ALL_ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNING, ENCRYPTION}; + static final SimpleAttributeDefinition[] ELEMENTS = {PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM}; + static final AttributeDefinition[] ALL_ATTRIBUTES = {SIGNING, ENCRYPTION, PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM, KEY_STORE}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static final HashMap ELEMENT_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ELEMENTS) { + ELEMENT_MAP.put(def.getXmlName(), def); + } + } + + static final KeyDefinition INSTANCE = new KeyDefinition(); + + private KeyDefinition() { + super(PathElement.pathElement(Constants.Model.KEY), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.KEY), + new KeyAddHandler(), + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } + + static SimpleAttributeDefinition lookupElement(String xmlName) { + return ELEMENT_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreCertificateDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreCertificateDefinition.java new file mode 100644 index 0000000000..7ae2ff10a6 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreCertificateDefinition.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +/** + * @author Marko Strukelj + */ +public class KeyStoreCertificateDefinition { + + static final SimpleAttributeDefinition CERTIFICATE_ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.CERTIFICATE_ALIAS) + .build(); + + static SimpleAttributeDefinition lookup(String xmlName) { + return Constants.XML.CERTIFICATE_ALIAS.equals(xmlName) ? CERTIFICATE_ALIAS : null; + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreDefinition.java new file mode 100644 index 0000000000..2fb14f5d4f --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStoreDefinition.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class KeyStoreDefinition { + + static final SimpleAttributeDefinition RESOURCE = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESOURCE, ModelType.STRING, true) + .setXmlName(Constants.XML.RESOURCE) + .build(); + + static final SimpleAttributeDefinition PASSWORD = + new SimpleAttributeDefinitionBuilder(Constants.Model.PASSWORD, ModelType.STRING, true) + .setXmlName(Constants.XML.PASSWORD) + .build(); + + static final SimpleAttributeDefinition FILE = + new SimpleAttributeDefinitionBuilder(Constants.Model.FILE, ModelType.STRING, true) + .setXmlName(Constants.XML.FILE) + .build(); + + static final SimpleAttributeDefinition TYPE = + new SimpleAttributeDefinitionBuilder(Constants.Model.TYPE, ModelType.STRING, true) + .setXmlName(Constants.XML.TYPE) + .build(); + + static final SimpleAttributeDefinition ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.ALIAS) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS}; + static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS, + KeyStorePrivateKeyDefinition.PRIVATE_KEY_ALIAS, + KeyStorePrivateKeyDefinition.PRIVATE_KEY_PASSWORD, + KeyStoreCertificateDefinition.CERTIFICATE_ALIAS + }; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStorePrivateKeyDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStorePrivateKeyDefinition.java new file mode 100644 index 0000000000..1947ca94fe --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/KeyStorePrivateKeyDefinition.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class KeyStorePrivateKeyDefinition { + static final SimpleAttributeDefinition PRIVATE_KEY_ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_ALIAS) + .build(); + + static final SimpleAttributeDefinition PRIVATE_KEY_PASSWORD = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PASSWORD, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_PASSWORD) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASSWORD}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} 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 index 31008d4662..68e46798d4 100755 --- 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 @@ -21,13 +21,22 @@ import org.jboss.as.server.deployment.DeploymentUnit; import org.jboss.as.server.deployment.DeploymentUnitProcessingException; import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.as.web.deployment.WarMetaData; +import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; import org.jboss.metadata.javaee.spec.ParamValueMetaData; import org.jboss.metadata.web.jboss.JBossWebMetaData; import org.jboss.metadata.web.jboss.ValveMetaData; import org.jboss.metadata.web.spec.LoginConfigMetaData; +import org.jboss.staxmapper.XMLExtendedStreamWriter; +import org.keycloak.adapters.saml.AdapterConstants; import org.keycloak.adapters.saml.jbossweb.SamlAuthenticatorValve; +import org.keycloak.subsystem.saml.as7.logging.KeycloakLogger; +import org.keycloak.subsystem.saml.as7.xml.FormattingXMLStreamWriter; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; import java.util.ArrayList; import java.util.List; @@ -39,22 +48,16 @@ import java.util.List; public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor { protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class); - // This param name is defined again in Keycloak Undertow Integration class - // org.keycloak.adapters.undertow.KeycloakServletExtension. We have this value in - // two places to avoid dependency between Keycloak Subsystem and Keyclaok Undertow Integration. - public static final String AUTH_DATA_PARAM_NAME = "org.keycloak.saml.adapterConfig"; - - @Override public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); String deploymentName = deploymentUnit.getName(); - // if it's not a web-app there's nothing to secure WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY); if (warMetaData == null) { return; } + JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); if (webMetaData == null) { webMetaData = new JBossWebMetaData(); @@ -64,14 +67,68 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP // otherwise LoginConfigMetaData loginConfig = webMetaData.getLoginConfig(); - boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod()); + try { + boolean webRequiresKC = loginConfig != null && "KEYCLOAK-SAML".equalsIgnoreCase(loginConfig.getAuthMethod()); + boolean hasSubsystemConfig = Configuration.INSTANCE.isSecureDeployment(deploymentName); + if (hasSubsystemConfig || webRequiresKC) { + log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName); - if (webRequiresKC) { - log.debug("Setting up KEYCLOAK-SAML auth method for WAR: " + deploymentName); - addValve(webMetaData); + // if secure-deployment configuration exists for web app, we force KEYCLOAK-SAML auth method on it + if (hasSubsystemConfig) { + addXMLData(getXML(deploymentName), warMetaData); + if (loginConfig != null) { + loginConfig.setAuthMethod("KEYCLOAK-SAML"); + //loginConfig.setRealmName(service.getRealmName(deploymentName)); + } else { + log.warn("Failed to set up KEYCLOAK-SAML auth method for WAR: " + deploymentName + " (loginConfig == null)"); + } + } + addValve(webMetaData); + KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName); + } + } catch (Exception e) { + throw new DeploymentUnitProcessingException("Failed to configure KeycloakSamlExtension from subsystem model", e); } } + private String getXML(String deploymentName) throws XMLStreamException { + ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentName); + if (node != null) { + KeycloakSubsystemParser writer = new KeycloakSubsystemParser(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + XMLExtendedStreamWriter streamWriter = new FormattingXMLStreamWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(output)); + try { + streamWriter.writeStartElement("keycloak-saml-adapter"); + writer.writeSps(streamWriter, node); + streamWriter.writeEndElement(); + } finally { + streamWriter.close(); + } + return new String(output.toByteArray(), Charset.forName("utf-8")); + } + return null; + } + + private void addXMLData(String xml, WarMetaData warMetaData) { + JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); + if (webMetaData == null) { + webMetaData = new JBossWebMetaData(); + warMetaData.setMergedJBossWebMetaData(webMetaData); + } + + List contextParams = webMetaData.getContextParams(); + if (contextParams == null) { + contextParams = new ArrayList<>(); + } + + ParamValueMetaData param = new ParamValueMetaData(); + param.setParamName(AdapterConstants.AUTH_DATA_PARAM_NAME); + param.setParamValue(xml); + contextParams.add(param); + + webMetaData.setContextParams(contextParams); + } + private void addValve(JBossWebMetaData webMetaData) { List valves = webMetaData.getValves(); if (valves == null) { @@ -89,5 +146,4 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP public void undeploy(DeploymentUnit du) { } - } 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 index c52f2b5f7c..d93698295a 100755 --- 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 @@ -18,6 +18,7 @@ package org.keycloak.subsystem.saml.as7; import org.jboss.as.controller.Extension; import org.jboss.as.controller.ExtensionContext; +import org.jboss.as.controller.ModelVersion; import org.jboss.as.controller.PathElement; import org.jboss.as.controller.ResourceDefinition; import org.jboss.as.controller.SubsystemRegistration; @@ -36,15 +37,12 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUB public class KeycloakSamlExtension implements Extension { public static final String SUBSYSTEM_NAME = "keycloak-saml"; - public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6"; + public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.1"; private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser(); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; - private static final int MGMT_API_VERSION_MAJOR = 1; - private static final int MGMT_API_VERSION_MINOR = 1; - + private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1, 1, 0); static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); - private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition(); public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) { StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); @@ -67,10 +65,15 @@ public class KeycloakSamlExtension implements Extension { */ @Override public void initialize(final ExtensionContext context) { - final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION_MAJOR, MGMT_API_VERSION_MINOR); - - ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE); + final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, + MGMT_API_VERSION.getMajor(), MGMT_API_VERSION.getMinor(), MGMT_API_VERSION.getMicro()); + ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KeycloakSubsystemDefinition.INSTANCE); + ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SecureDeploymentDefinition.INSTANCE); + ManagementResourceRegistration serviceProviderRegistration = secureDeploymentRegistration.registerSubModel(ServiceProviderDefinition.INSTANCE); + serviceProviderRegistration.registerSubModel(KeyDefinition.INSTANCE); + ManagementResourceRegistration idpRegistration = serviceProviderRegistration.registerSubModel(IdentityProviderDefinition.INSTANCE); + idpRegistration.registerSubModel(KeyDefinition.INSTANCE); subsystem.registerXMLElementWriter(PARSER); } } 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 index 2a7fd55242..eda678fe07 100755 --- 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 @@ -16,13 +16,12 @@ */ package org.keycloak.subsystem.saml.as7; - import org.jboss.as.controller.AbstractBoottimeAddStepHandler; import org.jboss.as.controller.OperationContext; -import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.ServiceVerificationHandler; import org.jboss.as.server.AbstractDeploymentChainStep; import org.jboss.as.server.DeploymentProcessorTarget; +import org.jboss.as.server.deployment.DeploymentUnitProcessor; import org.jboss.as.server.deployment.Phase; import org.jboss.dmr.ModelNode; import org.jboss.msc.service.ServiceController; @@ -43,17 +42,20 @@ class KeycloakSubsystemAdd extends AbstractBoottimeAddStepHandler { context.addStep(new AbstractDeploymentChainStep() { @Override protected void execute(DeploymentProcessorTarget processorTarget) { - processorTarget.addDeploymentProcessor(Phase.DEPENDENCIES, 0, new KeycloakDependencyProcessorAS7()); - processorTarget.addDeploymentProcessor( + processorTarget.addDeploymentProcessor(KeycloakSamlExtension.SUBSYSTEM_NAME, Phase.DEPENDENCIES, 0, chooseDependencyProcessor()); + processorTarget.addDeploymentProcessor(KeycloakSamlExtension.SUBSYSTEM_NAME, Phase.POST_MODULE, // PHASE Phase.POST_MODULE_VALIDATOR_FACTORY - 1, // PRIORITY - new KeycloakAdapterConfigDeploymentProcessor()); + chooseConfigDeploymentProcessor()); } }, OperationContext.Stage.RUNTIME); } - @Override - protected void populateModel(ModelNode operation, ModelNode model) throws OperationFailedException { - model.setEmptyObject(); + private DeploymentUnitProcessor chooseDependencyProcessor() { + return new KeycloakDependencyProcessorAS7(); + } + + private DeploymentUnitProcessor chooseConfigDeploymentProcessor() { + return new KeycloakAdapterConfigDeploymentProcessor(); } } 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 index 400822e40d..4b7ef3f8f1 100755 --- 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 @@ -14,23 +14,23 @@ * License for the specific language governing permissions and limitations under * the License. */ - package org.keycloak.subsystem.saml.as7; import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; import org.jboss.as.controller.SimpleResourceDefinition; -import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; import org.jboss.as.controller.registry.ManagementResourceRegistration; -import org.jboss.as.controller.registry.OperationEntry; /** - * Definition of subsystem=keycloak. + * Definition of subsystem=keycloak-saml. * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { - protected KeycloakSubsystemDefinition() { + + static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition(); + + private KeycloakSubsystemDefinition() { super(KeycloakSamlExtension.SUBSYSTEM_PATH, KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"), KeycloakSubsystemAdd.INSTANCE, @@ -41,7 +41,6 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { @Override public void registerOperations(ManagementResourceRegistration resourceRegistration) { super.registerOperations(resourceRegistration); - resourceRegistration.registerOperationHandler(ModelDescriptionConstants.DESCRIBE, GenericSubsystemDescribeHandler.INSTANCE, GenericSubsystemDescribeHandler.INSTANCE, false, OperationEntry.EntryType.PRIVATE); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); } - } 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 index 14899e1d7b..0b2cef9130 100755 --- 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 @@ -1,5 +1,5 @@ /* - * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors * as indicated by the @author tags. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not @@ -17,9 +17,14 @@ package org.keycloak.subsystem.saml.as7; import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.descriptions.ModelDescriptionConstants; +import org.jboss.as.controller.operations.common.Util; import org.jboss.as.controller.parsing.ParseUtils; import org.jboss.as.controller.persistence.SubsystemMarshallingContext; import org.jboss.dmr.ModelNode; +import org.jboss.dmr.Property; import org.jboss.staxmapper.XMLElementReader; import org.jboss.staxmapper.XMLElementWriter; import org.jboss.staxmapper.XMLExtendedStreamReader; @@ -27,6 +32,10 @@ import org.jboss.staxmapper.XMLExtendedStreamWriter; import javax.xml.stream.XMLStreamConstants; import javax.xml.stream.XMLStreamException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; import java.util.List; /** @@ -41,10 +50,15 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • list) throws XMLStreamException { // Require no attributes ParseUtils.requireNoAttributes(reader); - ModelNode addKeycloakSub = org.jboss.as.controller.operations.common.Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM)); + ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM)); list.add(addKeycloakSub); while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + if (reader.getLocalName().equals(Constants.XML.SECURE_DEPLOYMENT)) { + readSecureDeployment(reader, list); + } else { + throw ParseUtils.unexpectedElement(reader); + } } } @@ -53,6 +67,319 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • list) throws XMLStreamException { + String name = readRequiredAttribute(reader, Constants.XML.NAME); + + PathAddress addr = PathAddress.pathAddress( + PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakSamlExtension.SUBSYSTEM_NAME), + PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT, name)); + ModelNode addSecureDeployment = Util.createAddOperation(addr); + list.add(addSecureDeployment); + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (tagName.equals(Constants.XML.SERVICE_PROVIDER)) { + readServiceProvider(reader, list, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readServiceProvider(XMLExtendedStreamReader reader, List list, PathAddress parentAddr) throws XMLStreamException { + String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID); + + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.SERVICE_PROVIDER, entityId)); + ModelNode addServiceProvider = Util.createAddOperation(addr); + list.add(addServiceProvider); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + if (Constants.XML.ENTITY_ID.equals(name)) { + continue; + } + + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = ServiceProviderDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addServiceProvider, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.KEYS.equals(tagName)) { + readKeys(list, reader, addr); + } else if (Constants.XML.PRINCIPAL_NAME_MAPPING.equals(tagName)) { + readPrincipalNameMapping(addServiceProvider, reader); + } else if (Constants.XML.ROLE_IDENTIFIERS.equals(tagName)) { + readRoleIdentifiers(addServiceProvider, reader); + } else if (Constants.XML.IDENTITY_PROVIDER.equals(tagName)) { + readIdentityProvider(list, reader, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readIdentityProvider(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID); + + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER, entityId)); + ModelNode addIdentityProvider = Util.createAddOperation(addr); + list.add(addIdentityProvider); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + if (Constants.XML.ENTITY_ID.equals(name) + // don't break if encountering this noop attr from client-adapter/core keycloak_saml_adapter_1_6.xsd + || "encryption".equals(name)) { + continue; + } + SimpleAttributeDefinition attr = IdentityProviderDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addIdentityProvider, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.SINGLE_SIGN_ON.equals(tagName)) { + readSingleSignOn(addIdentityProvider, reader); + } else if (Constants.XML.SINGLE_LOGOUT.equals(tagName)) { + readSingleLogout(addIdentityProvider, reader); + } else if (Constants.XML.KEYS.equals(tagName)) { + readKeys(list, reader, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readSingleSignOn(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode sso = addIdentityProvider.get(Constants.Model.SINGLE_SIGN_ON); + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = SingleSignOnDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, sso, reader); + } + ParseUtils.requireNoContent(reader); + } + + void readSingleLogout(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode slo = addIdentityProvider.get(Constants.Model.SINGLE_LOGOUT); + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = SingleLogoutDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, slo, reader); + } + ParseUtils.requireNoContent(reader); + } + + void readKeys(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + List keyList = new LinkedList<>(); + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (!Constants.XML.KEY.equals(tagName)) { + throw ParseUtils.unexpectedElement(reader); + } + readKey(keyList, reader, parentAddr); + } + list.addAll(keyList); + } + + void readKey(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.KEY, "key-" + list.size())); + ModelNode addKey = Util.createAddOperation(addr); + list.add(addKey); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKey, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.KEY_STORE.equals(tagName)) { + readKeyStore(addKey, reader); + } else if (Constants.XML.PRIVATE_KEY_PEM.equals(tagName) + || Constants.XML.PUBLIC_KEY_PEM.equals(tagName) + || Constants.XML.CERTIFICATE_PEM.equals(tagName)) { + + readNoAttrElementContent(KeyDefinition.lookupElement(tagName), addKey, reader); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readNoAttrElementContent(SimpleAttributeDefinition attr, ModelNode model, XMLExtendedStreamReader reader) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + String value = reader.getElementText(); + attr.parseAndSetParameter(value, model, reader); + } + + void readKeyStore(ModelNode addKey, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode addKeyStore = addKey.get(Constants.Model.KEY_STORE); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStoreDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.FILE) && !addKeyStore.hasDefined(Constants.Model.RESOURCE)) { + throw new XMLStreamException("KeyStore element must have 'file' or 'resource' attribute set", reader.getLocation()); + } + if (!addKeyStore.hasDefined(Constants.Model.PASSWORD)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PASSWORD); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (Constants.XML.PRIVATE_KEY.equals(tagName)) { + readPrivateKey(reader, addKeyStore); + } else if (Constants.XML.CERTIFICATE.equals(tagName)) { + readCertificate(reader, addKeyStore); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + + void readPrivateKey(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException { + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStorePrivateKeyDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_ALIAS)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_ALIAS); + } + if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_PASSWORD)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_PASSWORD); + } + + ParseUtils.requireNoContent(reader); + } + + void readCertificate(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException { + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.CERTIFICATE_ALIAS)) { + throw ParseUtils.missingRequired(reader, Constants.XML.CERTIFICATE_ALIAS); + } + + ParseUtils.requireNoContent(reader); + } + + void readRoleIdentifiers(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (!Constants.XML.ATTRIBUTE.equals(tagName)) { + throw ParseUtils.unexpectedElement(reader); + } + + ParseUtils.requireSingleAttribute(reader, Constants.XML.NAME); + String name = ParseUtils.readStringAttributeElement(reader, Constants.XML.NAME); + + ServiceProviderDefinition.ROLE_ATTRIBUTES.parseAndAddParameterElement(name, addServiceProvider, reader); + } + } + + void readPrincipalNameMapping(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + + boolean policySet = false; + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + if (Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY.equals(name)) { + policySet = true; + ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_POLICY.parseAndSetParameter(value, addServiceProvider, reader); + } else if (Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.equals(name)) { + ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.parseAndSetParameter(value, addServiceProvider, reader); + } else { + throw ParseUtils.unexpectedAttribute(reader, i); + } + } + + if (!policySet) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY); + } + ParseUtils.requireNoContent(reader); + } + + /** + * Read an attribute, and throw exception if attribute is not present + */ + String readRequiredAttribute(XMLExtendedStreamReader reader, String attrName) throws XMLStreamException { + String value = null; + for (int i = 0; i < reader.getAttributeCount(); i++) { + String attr = reader.getAttributeLocalName(i); + if (attr.equals(attrName)) { + value = reader.getAttributeValue(i); + break; + } + } + if (value == null) { + throw ParseUtils.missingRequired(reader, Collections.singleton(attrName)); + } + return value; + } /** * {@inheritDoc} @@ -60,8 +387,183 @@ class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader
  • items = value.asList(); + if (items.size() == 0) { + return; + } + + writer.writeStartElement(Constants.XML.ROLE_IDENTIFIERS); + for (ModelNode item : items) { + writer.writeStartElement(Constants.XML.ATTRIBUTE); + writer.writeAttribute("name", item.asString()); + writer.writeEndElement(); + } + writer.writeEndElement(); + } + + void writePrincipalNameMapping(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + writer.writeStartElement(Constants.XML.PRINCIPAL_NAME_MAPPING); + ModelNode value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY); + if (value.isDefined()) { + writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY, value.asString()); + } + value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME); + if (value.isDefined()) { + writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, value.asString()); + } + writer.writeEndElement(); + } } diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentAddHandler.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentAddHandler.java new file mode 100644 index 0000000000..c5325f6bd9 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentAddHandler.java @@ -0,0 +1,42 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.ServiceVerificationHandler; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceController; + +import java.util.List; + +/** + * @author Marko Strukelj + */ +class SecureDeploymentAddHandler extends AbstractAddStepHandler { + + static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler(); + + private SecureDeploymentAddHandler() { + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentDefinition.java new file mode 100644 index 0000000000..75f4dca38e --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SecureDeploymentDefinition.java @@ -0,0 +1,44 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; + +/** + * Defines attributes and operations for a secure-deployment. + */ +public class SecureDeploymentDefinition extends SimpleResourceDefinition { + + static final SecureDeploymentDefinition INSTANCE = new SecureDeploymentDefinition(); + + private SecureDeploymentDefinition() { + super(PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SECURE_DEPLOYMENT), + SecureDeploymentAddHandler.INSTANCE, + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderAddHandler.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderAddHandler.java new file mode 100644 index 0000000000..33d601503b --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderAddHandler.java @@ -0,0 +1,43 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.as.controller.ServiceVerificationHandler; +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.ServiceController; + +import java.util.List; + +/** + * @author Marko Strukelj + */ +class ServiceProviderAddHandler extends AbstractAddStepHandler { + + static final ServiceProviderAddHandler INSTANCE = new ServiceProviderAddHandler(); + + ServiceProviderAddHandler() { + super(ServiceProviderDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model, ServiceVerificationHandler verificationHandler, List> newControllers) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderDefinition.java new file mode 100644 index 0000000000..02ecc8f701 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/ServiceProviderDefinition.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ListAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.StringListAttributeDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class ServiceProviderDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SSL_POLICY = + new SimpleAttributeDefinitionBuilder(Constants.Model.SSL_POLICY, ModelType.STRING, true) + .setXmlName(Constants.XML.SSL_POLICY) + .build(); + + static final SimpleAttributeDefinition NAME_ID_POLICY_FORMAT = + new SimpleAttributeDefinitionBuilder(Constants.Model.NAME_ID_POLICY_FORMAT, ModelType.STRING, true) + .setXmlName(Constants.XML.NAME_ID_POLICY_FORMAT) + .build(); + + static final SimpleAttributeDefinition LOGOUT_PAGE = + new SimpleAttributeDefinitionBuilder(Constants.Model.LOGOUT_PAGE, ModelType.STRING, true) + .setXmlName(Constants.XML.LOGOUT_PAGE) + .build(); + + static final SimpleAttributeDefinition FORCE_AUTHENTICATION = + new SimpleAttributeDefinitionBuilder(Constants.Model.FORCE_AUTHENTICATION, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.FORCE_AUTHENTICATION) + .build(); + + static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_POLICY = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY, ModelType.STRING, true) + .setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY) + .build(); + + static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ModelType.STRING, true) + .setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME) + .build(); + + static final ListAttributeDefinition ROLE_ATTRIBUTES = + new StringListAttributeDefinition.Builder(Constants.Model.ROLE_ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION}; + static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES}; + + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + static final HashMap ALL_MAP = new HashMap<>(); + static final Collection ALL_ATTRIBUTES; + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + + ALL_MAP.putAll(ATTRIBUTE_MAP); + for (AttributeDefinition def : ELEMENTS) { + ALL_MAP.put(def.getXmlName(), def); + } + ALL_ATTRIBUTES = Collections.unmodifiableCollection(ALL_MAP.values()); + } + + static final ServiceProviderDefinition INSTANCE = new ServiceProviderDefinition(); + + private ServiceProviderDefinition() { + super(PathElement.pathElement(Constants.Model.SERVICE_PROVIDER), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SERVICE_PROVIDER), + ServiceProviderAddHandler.INSTANCE, + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleLogoutDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleLogoutDefinition.java new file mode 100644 index 0000000000..beb884c50b --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleLogoutDefinition.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class SingleLogoutDefinition { + + static final SimpleAttributeDefinition VALIDATE_REQUEST_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_REQUEST_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_REQUEST_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition SIGN_REQUEST = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_REQUEST) + .build(); + + static final SimpleAttributeDefinition SIGN_RESPONSE = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_RESPONSE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_RESPONSE) + .build(); + + static final SimpleAttributeDefinition REQUEST_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.REQUEST_BINDING) + .build(); + + static final SimpleAttributeDefinition RESPONSE_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.RESPONSE_BINDING) + .build(); + + static final SimpleAttributeDefinition POST_BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.POST_BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.POST_BINDING_URL) + .build(); + + static final SimpleAttributeDefinition REDIRECT_BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.REDIRECT_BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.REDIRECT_BINDING_URL) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {VALIDATE_REQUEST_SIGNATURE, VALIDATE_RESPONSE_SIGNATURE, + SIGN_REQUEST, SIGN_RESPONSE, REQUEST_BINDING, RESPONSE_BINDING, POST_BINDING_URL, REDIRECT_BINDING_URL}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleSignOnDefinition.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleSignOnDefinition.java new file mode 100644 index 0000000000..827be9b8f9 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/SingleSignOnDefinition.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class SingleSignOnDefinition { + + static final SimpleAttributeDefinition SIGN_REQUEST = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_REQUEST) + .build(); + + static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition REQUEST_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.REQUEST_BINDING) + .build(); + + static final SimpleAttributeDefinition RESPONSE_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.RESPONSE_BINDING) + .build(); + + static final SimpleAttributeDefinition BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.BINDING_URL) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakLogger.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakLogger.java new file mode 100755 index 0000000000..8bd7ac1b98 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakLogger.java @@ -0,0 +1,49 @@ +/* + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7.logging; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.LogMessage; +import org.jboss.logging.Logger; +import org.jboss.logging.Message; +import org.jboss.logging.MessageLogger; + +import static org.jboss.logging.Logger.Level.DEBUG; +import static org.jboss.logging.Logger.Level.INFO; + +/** + * This interface to be fleshed out later when error messages are fully externalized. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ +@MessageLogger(projectCode = "KEYCLOAK") +public interface KeycloakLogger extends BasicLogger { + + /** + * A logger with a category of the package name. + */ + KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak"); + + @LogMessage(level = INFO) + @Message(value = "Keycloak SAML subsystem override for deployment %s") + void deploymentSecured(String deployment); + + @LogMessage(level = DEBUG) + @Message(value = "Keycloak SAML has overriden and secured deployment %s") + void warSecured(String deployment); + +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakMessages.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakMessages.java new file mode 100755 index 0000000000..f4917b1de3 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/logging/KeycloakMessages.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.saml.as7.logging; + +import org.jboss.logging.MessageBundle; +import org.jboss.logging.Messages; + +/** + * This interface to be fleshed out later when error messages are fully externalized. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc. + */ +@MessageBundle(projectCode = "TLIP") +public interface KeycloakMessages { + + /** + * The messages + */ + KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class); +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/FormattingXMLStreamWriter.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/FormattingXMLStreamWriter.java new file mode 100644 index 0000000000..dda3ed3424 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/FormattingXMLStreamWriter.java @@ -0,0 +1,534 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.keycloak.subsystem.saml.as7.xml; + +import org.jboss.staxmapper.XMLExtendedStreamWriter; + +import java.lang.reflect.UndeclaredThrowableException; +import java.util.ArrayDeque; +import java.util.Iterator; + +import javax.xml.namespace.NamespaceContext; +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamWriter; + +/** + * An XML stream writer which nicely formats the XML for configuration files. + * + * @author David M. Lloyd + */ +public final class FormattingXMLStreamWriter implements XMLExtendedStreamWriter, XMLStreamConstants { + private static final String NO_NAMESPACE = new String(); + private final XMLStreamWriter delegate; + private final ArrayDeque attrQueue = new ArrayDeque(); + private int level; + private int state = START_DOCUMENT; + private boolean indentEndElement = false; + private ArrayDeque unspecifiedNamespaces = new ArrayDeque(); + + + public FormattingXMLStreamWriter(final XMLStreamWriter delegate) { + this.delegate = delegate; + unspecifiedNamespaces.push(NO_NAMESPACE); + } + + private void nl() throws XMLStreamException { + delegate.writeCharacters("\n"); + } + + private void indent() throws XMLStreamException { + int level = this.level; + final XMLStreamWriter delegate = this.delegate; + for (int i = 0; i < level; i ++) { + delegate.writeCharacters(" "); + } + } + + private interface ArgRunnable { + public void run(int arg) throws XMLStreamException; + } + + @Override + public void setUnspecifiedElementNamespace(final String namespace) { + ArrayDeque namespaces = this.unspecifiedNamespaces; + namespaces.pop(); + namespaces.push(namespace == null ? NO_NAMESPACE : namespace); + } + + private String nestUnspecifiedNamespace() { + ArrayDeque namespaces = unspecifiedNamespaces; + String clone = namespaces.getFirst(); + namespaces.push(clone); + return clone; + } + + @Override + public void writeStartElement(final String localName) throws XMLStreamException { + ArrayDeque namespaces = unspecifiedNamespaces; + String namespace = namespaces.getFirst(); + if (namespace != NO_NAMESPACE) { + writeStartElement(namespace, localName); + return; + } + + unspecifiedNamespaces.push(namespace); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + if (arg == 0) { + delegate.writeStartElement(localName); + } else { + delegate.writeEmptyElement(localName); + } + } + }); + + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeStartElement(final String namespaceURI, final String localName) throws XMLStreamException { + nestUnspecifiedNamespace(); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + if (arg == 0) { + delegate.writeStartElement(namespaceURI, localName); + } else { + delegate.writeEmptyElement(namespaceURI, localName); + } + } + }); + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeStartElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { + nestUnspecifiedNamespace(); + + // If this is a nested element flush the outer + runAttrQueue(); + nl(); + indent(); + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + if (arg == 0) { + delegate.writeStartElement(prefix, namespaceURI, localName); + } else { + delegate.writeEmptyElement(prefix, namespaceURI, localName); + } + } + }); + level++; + state = START_ELEMENT; + indentEndElement = false; + } + + @Override + public void writeEmptyElement(final String namespaceURI, final String localName) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(namespaceURI, localName); + state = END_ELEMENT; + } + + @Override + public void writeEmptyElement(final String prefix, final String localName, final String namespaceURI) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(prefix, namespaceURI, localName); + state = END_ELEMENT; + } + + @Override + public void writeEmptyElement(final String localName) throws XMLStreamException { + String namespace = unspecifiedNamespaces.getFirst(); + if (namespace != NO_NAMESPACE) { + writeEmptyElement(namespace, localName); + return; + } + + runAttrQueue(); + nl(); + indent(); + delegate.writeEmptyElement(localName); + state = END_ELEMENT; + } + + @Override + public void writeEndElement() throws XMLStreamException { + level--; + if (state != START_ELEMENT) { + runAttrQueue(); + if (state != CHARACTERS || indentEndElement) { + nl(); + indent(); + indentEndElement = false; + } + delegate.writeEndElement(); + } else { + // Change the start element to an empty element + ArgRunnable start = attrQueue.poll(); + if (start == null) { + delegate.writeEndElement(); + } else { + start.run(1); + // Write everything else + runAttrQueue(); + } + } + + unspecifiedNamespaces.pop(); + state = END_ELEMENT; + } + + private void runAttrQueue() throws XMLStreamException { + ArgRunnable attr; + while ((attr = attrQueue.poll()) != null) { + attr.run(0); + } + } + + @Override + public void writeEndDocument() throws XMLStreamException { + delegate.writeEndDocument(); + state = END_DOCUMENT; + } + + @Override + public void close() throws XMLStreamException { + delegate.close(); + state = END_DOCUMENT; + } + + @Override + public void flush() throws XMLStreamException { + delegate.flush(); + } + + @Override + public void writeAttribute(final String localName, final String value) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + try { + delegate.writeAttribute(localName, value); + } catch (XMLStreamException e) { + throw new UndeclaredThrowableException(e); + } + } + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String value) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(prefix, namespaceURI, localName, value); + } + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final String value) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(namespaceURI, localName, value); + } + }); + } + + @Override + public void writeAttribute(final String localName, final String[] values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(localName, join(values)); + } + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); + } + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final String[] values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(namespaceURI, localName, join(values)); + } + }); + } + + @Override + public void writeAttribute(final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(localName, join(values)); + } + }); + } + + @Override + public void writeAttribute(final String prefix, final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(prefix, namespaceURI, localName, join(values)); + } + }); + } + + @Override + public void writeAttribute(final String namespaceURI, final String localName, final Iterable values) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeAttribute(namespaceURI, localName, join(values)); + } + }); + } + + @Override + public void writeNamespace(final String prefix, final String namespaceURI) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeNamespace(prefix, namespaceURI); + } + }); + } + + @Override + public void writeDefaultNamespace(final String namespaceURI) throws XMLStreamException { + attrQueue.add(new ArgRunnable() { + public void run(int arg) throws XMLStreamException { + delegate.writeDefaultNamespace(namespaceURI); + } + }); + } + + @Override + public void writeComment(final String data) throws XMLStreamException { + runAttrQueue(); + nl(); + nl(); + indent(); + final StringBuilder b = new StringBuilder(data.length()); + final Iterator i = Spliterator.over(data, '\n'); + if (! i.hasNext()) { + return; + } else { + final String first = i.next(); + if (! i.hasNext()) { + delegate.writeComment(" " + first + " "); + state = COMMENT; + return; + } else { + b.append('\n'); + for (int q = 0; q < level; q++) { + b.append(" "); + } + b.append(" ~ "); + b.append(first); + do { + b.append('\n'); + for (int q = 0; q < level; q++) { + b.append(" "); + } + b.append(" ~ "); + b.append(i.next()); + } while (i.hasNext()); + } + b.append('\n'); + for (int q = 0; q < level; q ++) { + b.append(" "); + } + b.append(" "); + delegate.writeComment(b.toString()); + state = COMMENT; + } + } + + @Override + public void writeProcessingInstruction(final String target) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeProcessingInstruction(target); + state = PROCESSING_INSTRUCTION; + } + + @Override + public void writeProcessingInstruction(final String target, final String data) throws XMLStreamException { + runAttrQueue(); + nl(); + indent(); + delegate.writeProcessingInstruction(target, data); + state = PROCESSING_INSTRUCTION; + } + + @Override + public void writeCData(final String data) throws XMLStreamException { + runAttrQueue(); + delegate.writeCData(data); + state = CDATA; + } + + @Override + public void writeDTD(final String dtd) throws XMLStreamException { + nl(); + indent(); + delegate.writeDTD(dtd); + state = DTD; + } + + @Override + public void writeEntityRef(final String name) throws XMLStreamException { + runAttrQueue(); + delegate.writeEntityRef(name); + state = ENTITY_REFERENCE; + } + + @Override + public void writeStartDocument() throws XMLStreamException { + delegate.writeStartDocument(); + nl(); + state = START_DOCUMENT; + } + + @Override + public void writeStartDocument(final String version) throws XMLStreamException { + delegate.writeStartDocument(version); + nl(); + state = START_DOCUMENT; + } + + @Override + public void writeStartDocument(final String encoding, final String version) throws XMLStreamException { + delegate.writeStartDocument(encoding, version); + nl(); + state = START_DOCUMENT; + } + + @Override + public void writeCharacters(final String text) throws XMLStreamException { + runAttrQueue(); + if (state != CHARACTERS) { + nl(); + indent(); + } + final Iterator iterator = Spliterator.over(text, '\n'); + while (iterator.hasNext()) { + final String t = iterator.next(); + delegate.writeCharacters(t); + if (iterator.hasNext()) { + nl(); + indent(); + } + } + state = CHARACTERS; + indentEndElement = true; + } + + @Override + public void writeCharacters(final char[] text, final int start, final int len) throws XMLStreamException { + runAttrQueue(); + delegate.writeCharacters(text, start, len); + state = CHARACTERS; + } + + @Override + public String getPrefix(final String uri) throws XMLStreamException { + return delegate.getPrefix(uri); + } + + @Override + public void setPrefix(final String prefix, final String uri) throws XMLStreamException { + delegate.setPrefix(prefix, uri); + } + + @Override + public void setDefaultNamespace(final String uri) throws XMLStreamException { + runAttrQueue(); + delegate.setDefaultNamespace(uri); + } + + @Override + public void setNamespaceContext(final NamespaceContext context) throws XMLStreamException { + delegate.setNamespaceContext(context); + } + + @Override + public NamespaceContext getNamespaceContext() { + return delegate.getNamespaceContext(); + } + + @Override + public Object getProperty(final String name) throws IllegalArgumentException { + return delegate.getProperty(name); + } + + private static String join(final String[] values) { + final StringBuilder b = new StringBuilder(); + for (int i = 0, valuesLength = values.length; i < valuesLength; i++) { + final String s = values[i]; + if (s != null) { + if (i > 0) { + b.append(' '); + } + b.append(s); + } + } + return b.toString(); + } + + private static String join(final Iterable values) { + final StringBuilder b = new StringBuilder(); + Iterator iterator = values.iterator(); + while (iterator.hasNext()) { + final String s = iterator.next(); + if (s != null) { + b.append(s); + if (iterator.hasNext()) b.append(' '); + } + } + return b.toString(); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/Spliterator.java b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/Spliterator.java new file mode 100644 index 0000000000..e9cb5c6a07 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/java/org/keycloak/subsystem/saml/as7/xml/Spliterator.java @@ -0,0 +1,66 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2010, Red Hat, Inc., and individual contributors + * as indicated by the @author tags. See the copyright.txt file in the + * distribution for a full listing of individual contributors. + * + * This is free software; you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + * 02110-1301 USA, or see the FSF site: http://www.fsf.org. + */ + +package org.keycloak.subsystem.saml.as7.xml; + +import java.util.Iterator; +import java.util.NoSuchElementException; + +/** + * @author David M. Lloyd + */ +final class Spliterator implements Iterator { + private final String subject; + private final char delimiter; + private int i; + + Spliterator(final String subject, final char delimiter) { + this.subject = subject; + this.delimiter = delimiter; + i = 0; + } + + static Spliterator over(String subject, char delimiter) { + return new Spliterator(subject, delimiter); + } + + public boolean hasNext() { + return i != -1; + } + + public String next() { + final int i = this.i; + if (i == -1) { + throw new NoSuchElementException(); + } + int n = subject.indexOf(delimiter, i); + try { + return n == -1 ? subject.substring(i) : subject.substring(i, n); + } finally { + this.i = n == -1 ? -1 : n + 1; + } + } + + public void remove() { + throw new UnsupportedOperationException(); + } +} diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties new file mode 100755 index 0000000000..f8a4a11581 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/org/keycloak/subsystem/saml/as7/LocalDescriptions.properties @@ -0,0 +1,63 @@ +keycloak-saml.subsystem=Keycloak adapter subsystem +keycloak-saml.subsystem.add=Operation Adds Keycloak adapter subsystem +keycloak-saml.subsystem.remove=Operation removes Keycloak adapter subsystem +keycloak-saml.subsystem.secure-deployment=A deployment secured by Keycloak. + +keycloak-saml.secure-deployment=A deployment secured by Keycloak +keycloak-saml.secure-deployment.add=Add a deployment to be secured by Keycloak +keycloak-saml.secure-deployment.remove=Remove a deployment to be secured by Keycloak +keycloak-saml.secure-deployment.service-provider=A security provider configuration for secure deployment + +keycloak-saml.service-provider=A security provider configuration for secure deployment +keycloak-saml.service-provider.add=Add a security provider configuration to deployment secured by Keycloak SAML +keycloak-saml.service-provider.remove=Remove a security provider definition from deployment secured by Keycloak SAML +keycloak-saml.service-provider.ssl-policy=SSL Policy to use +keycloak-saml.service-provider.name-id-policy-format=Name ID policy format URN +keycloak-saml.service-provider.logout-page=URI to a logout page +keycloak-saml.service-provider.force-authentication=Redirected unauthenticated request to a login page +keycloak-saml.service-provider.role-attributes=Role identifiers +keycloak-saml.service-provider.principal-name-mapping-policy=Principal name mapping policy +keycloak-saml.service-provider.principal-name-mapping-attribute-name=Principal name mapping attribute name +keycloak-saml.service-provider.key=A key definition +keycloak-saml.service-provider.identity-provider=Identity provider definition + +keycloak-saml.key=A key configuration for service provider or identity provider +keycloak-saml.key.add=Add a key definition +keycloak-saml.key.remove=Remove a key definition +keycloak-saml.key.signing=Key can be used for signing +keycloak-saml.key.encryption=Key can be used for encryption +keycloak-saml.key.private-key-pem=Private key string in pem format +keycloak-saml.key.public-key-pem=Public key string in pem format +keycloak-saml.key.certificate-pem=Certificate key string in pem format +keycloak-saml.key.key-store=Key store definition +keycloak-saml.key.key-store.file=Key store filesystem path +keycloak-saml.key.key-store.resource=Key store resource URI +keycloak-saml.key.key-store.password=Key store password +keycloak-saml.key.key-store.type=Key store format +keycloak-saml.key.key-store.alias=Key alias +keycloak-saml.key.key-store.private-key-alias=Private key alias +keycloak-saml.key.key-store.private-key-password=Private key password +keycloak-saml.key.key-store.certificate-alias=Certificate alias + +keycloak-saml.identity-provider=An identity provider configuration +keycloak-saml.identity-provider.add=Add an identity provider +keycloak-saml.identity-provider.remove=Remove an identity provider +keycloak-saml.identity-provider.signatures-required=Require signatures for single-sign-on and single-logout +keycloak-saml.identity-provider.signature-algorithm=Signature algorithm +keycloak-saml.identity-provider.signature-canonicalization-method=Signature canonicalization method +keycloak-saml.identity-provider.single-sign-on=Single sign-on configuration +keycloak-saml.identity-provider.single-sign-on.sign-request=Sign SSO requests +keycloak-saml.identity-provider.single-sign-on.validate-response-signature=Validate an SSO response signature +keycloak-saml.identity-provider.single-sign-on.request-binding=HTTP method to use for requests +keycloak-saml.identity-provider.single-sign-on.response-binding=HTTP method to use for responses +keycloak-saml.identity-provider.single-sign-on.binding-url=SSO endpoint URL +keycloak-saml.identity-provider.single-logout=Single logout configuration +keycloak-saml.identity-provider.single-logout.validate-request-signature=Validate a single-logout request signature +keycloak-saml.identity-provider.single-logout.validate-response-signature=Validate a single-logout response signature +keycloak-saml.identity-provider.single-logout.sign-request=Sign single-logout requests +keycloak-saml.identity-provider.single-logout.sign-response=Sign single-logout responses +keycloak-saml.identity-provider.single-logout.request-binding=HTTP method to use for request +keycloak-saml.identity-provider.single-logout.response-binding=HTTP method to use for response +keycloak-saml.identity-provider.single-logout.post-binding-url=Endpoint URL for posting +keycloak-saml.identity-provider.single-logout.redirect-binding-url=Endpoint URL for redirects +keycloak-saml.identity-provider.key=Key definition for identity provider \ No newline at end of file diff --git a/saml/client-adapter/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd new file mode 100644 index 0000000000..725104ba36 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + The name of the realm. + + + + + + + + + + + + + + The entity ID for SAML service provider + + + + + The ssl policy + + + + + Name ID policy format URN + + + + + URI to a logout page + + + + + Redirected unauthenticated request to a login page + + + + + + + + + + + + The entity ID for SAML service provider + + + + + Require signatures for single-sign-on and single-logout + + + + + Algorithm used for signatures + + + + + Canonicalization method used for signatures + + + + + + + Sign the SSO requests + + + + + Validate the SSO response signature + + + + + HTTP method to use for requests + + + + + HTTP method to use for response + + + + + SSO endpoint URL + + + + + + + Validate a single-logout request signature + + + + + Validate a single-logout response signature + + + + + Sign single-logout requests + + + + + Sign single-logout responses + + + + + HTTP method to use for request + + + + + HTTP method to use for response + + + + + Endpoint URL for posting + + + + + Endpoint URL for redirects + + + + + + + + + + + + + + + + + + Key can be used for signing + + + + + Key can be used for encryption + + + + + + + + + + + Key store filesystem path + + + + + Key store resource URI + + + + + Key store password + + + + + Key store format + + + + + Key alias + + + + + + + + Private key alias + + + + + Private key password + + + + + + + + Certificate alias + + + + + + + + Principal name mapping policy. Possible values: FROM_NAME_ID + + + + + Name of the attribute to use for principal name mapping + + + + + + + + + + + + + + Role attribute + + + + diff --git a/saml/client-adapter/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.1.xml b/saml/client-adapter/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.1.xml new file mode 100644 index 0000000000..19d7528416 --- /dev/null +++ b/saml/client-adapter/as7-eap6/subsystem/src/test/resources/org/keycloak/subsystem/saml/as7/keycloak-saml-1.1.xml @@ -0,0 +1,49 @@ + + + + + + + my_key.pem + my_key.pub + cert.cer + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/AdapterConstants.java b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/AdapterConstants.java index 280b5f8739..e921d15251 100755 --- a/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/AdapterConstants.java +++ b/saml/client-adapter/core/src/main/java/org/keycloak/adapters/saml/AdapterConstants.java @@ -5,5 +5,5 @@ package org.keycloak.adapters.saml; * @version $Revision: 1 $ */ public class AdapterConstants { - public static final String AUTH_DATA_PARAM_NAME="org.keycloak.auth.deployment.data"; + public static final String AUTH_DATA_PARAM_NAME="org.keycloak.saml.xml.adapterConfig"; } diff --git a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java index 615e4a686a..ad22718658 100755 --- a/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java +++ b/saml/client-adapter/undertow/src/main/java/org/keycloak/adapters/saml/undertow/SamlServletExtension.java @@ -28,6 +28,7 @@ import io.undertow.servlet.api.DeploymentInfo; import io.undertow.servlet.api.LoginConfig; import io.undertow.servlet.api.ServletSessionConfig; import org.jboss.logging.Logger; +import org.keycloak.adapters.saml.AdapterConstants; import org.keycloak.adapters.saml.DefaultSamlDeployment; import org.keycloak.adapters.saml.SamlConfigResolver; import org.keycloak.adapters.saml.SamlDeployment; @@ -38,6 +39,7 @@ import org.keycloak.adapters.undertow.UndertowUserSessionManagement; import org.keycloak.saml.common.exceptions.ParsingException; import javax.servlet.ServletContext; +import java.io.ByteArrayInputStream; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.InputStream; @@ -64,8 +66,16 @@ public class SamlServletExtension implements ServletExtension { return false; } + private static InputStream getXMLFromServletContext(ServletContext servletContext) { + String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME); + if (json == null) { + return null; + } + return new ByteArrayInputStream(json.getBytes()); + } + private static InputStream getConfigInputStream(ServletContext context) { - InputStream is = null; + InputStream is = getXMLFromServletContext(context); if (is == null) { String path = context.getInitParameter("keycloak.config.file"); if (path == null) { diff --git a/saml/client-adapter/wildfly/pom.xml b/saml/client-adapter/wildfly/pom.xml index 2b5eb2578f..d9014b62c7 100755 --- a/saml/client-adapter/wildfly/pom.xml +++ b/saml/client-adapter/wildfly/pom.xml @@ -15,6 +15,6 @@ wildfly-adapter - wildfly9-subsystem + wildfly-subsystem diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/pom.xml b/saml/client-adapter/wildfly/wildfly-subsystem/pom.xml similarity index 97% rename from saml/client-adapter/wildfly/wildfly9-subsystem/pom.xml rename to saml/client-adapter/wildfly/wildfly-subsystem/pom.xml index 224db1b52d..3edade7527 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/pom.xml +++ b/saml/client-adapter/wildfly/wildfly-subsystem/pom.xml @@ -24,8 +24,8 @@ ../../../../pom.xml - keycloak-saml-wf9-subsystem - Keycloak Wildfly 9 SAML Adapter Subsystem + keycloak-saml-wildfly-subsystem + Keycloak Wildfly SAML Adapter Subsystem jar diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Configuration.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Configuration.java new file mode 100644 index 0000000000..e364e2d466 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Configuration.java @@ -0,0 +1,56 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.Property; + +/** + * @author Marko Strukelj + */ +public class Configuration { + + static Configuration INSTANCE = new Configuration(); + + private ModelNode config = new ModelNode(); + + private Configuration() { + } + + void updateModel(ModelNode operation, ModelNode model) { + ModelNode node = config; + ModelNode addr = operation.get("address"); + for (Property item : addr.asPropertyList()) { + node = getNodeForAddressElement(node, item); + } + node.set(model); + } + + private ModelNode getNodeForAddressElement(ModelNode node, Property item) { + String key = item.getValue().asString(); + ModelNode keymodel = node.get(item.getName()); + return keymodel.get(key); + } + + public ModelNode getSecureDeployment(String name) { + ModelNode secureDeployment = config.get("subsystem").get("keycloak-saml").get(Constants.Model.SECURE_DEPLOYMENT); + if (secureDeployment.hasDefined(name)) { + return secureDeployment.get(name); + } + return null; + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java new file mode 100644 index 0000000000..9b89fb2c80 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/Constants.java @@ -0,0 +1,120 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + + +/** + * @author Marko Strukelj + */ +public class Constants { + + static class Model { + static final String SECURE_DEPLOYMENT = "secure-deployment"; + static final String SERVICE_PROVIDER = "service-provider"; + + static final String SSL_POLICY = "ssl-policy"; + static final String NAME_ID_POLICY_FORMAT = "name-id-policy-format"; + static final String LOGOUT_PAGE = "logout-page"; + static final String FORCE_AUTHENTICATION = "force-authentication"; + static final String ROLE_ATTRIBUTES = "role-attributes"; + static final String SIGNING = "signing"; + static final String ENCRYPTION = "encryption"; + static final String KEY = "key"; + static final String RESOURCE = "resource"; + static final String PASSWORD = "password"; + + static final String PRIVATE_KEY_ALIAS = "private-key-alias"; + static final String PRIVATE_KEY_PASSWORD = "private-key-password"; + static final String CERTIFICATE_ALIAS = "certificate-alias"; + static final String KEY_STORE = "key-store"; + static final String SIGN_REQUEST = "sign-request"; + static final String VALIDATE_RESPONSE_SIGNATURE = "validate-response-signature"; + static final String REQUEST_BINDING = "request-binding"; + static final String BINDING_URL = "binding-url"; + static final String VALIDATE_REQUEST_SIGNATURE = "validate-request-signature"; + static final String SIGN_RESPONSE = "sign-response"; + static final String RESPONSE_BINDING = "response-binding"; + static final String POST_BINDING_URL = "post-binding-url"; + static final String REDIRECT_BINDING_URL = "redirect-binding-url"; + static final String SINGLE_SIGN_ON = "single-sign-on"; + static final String SINGLE_LOGOUT = "single-logout"; + static final String IDENTITY_PROVIDER = "identity-provider"; + static final String PRINCIPAL_NAME_MAPPING_POLICY = "principal-name-mapping-policy"; + static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "principal-name-mapping-attribute-name"; + static final String SIGNATURE_ALGORITHM = "signature-algorithm"; + static final String SIGNATURE_CANONICALIZATION_METHOD = "signature-canonicalization-method"; + static final String PRIVATE_KEY_PEM = "private-key-pem"; + static final String PUBLIC_KEY_PEM = "public-key-pem"; + static final String CERTIFICATE_PEM = "certificate-pem"; + static final String TYPE = "type"; + static final String ALIAS = "alias"; + static final String FILE = "file"; + static final String SIGNATURES_REQUIRED = "signatures-required"; + } + + + static class XML { + static final String SECURE_DEPLOYMENT = "secure-deployment"; + static final String SERVICE_PROVIDER = "SP"; + + static final String NAME = "name"; + static final String ENTITY_ID = "entityID"; + static final String SSL_POLICY = "sslPolicy"; + static final String NAME_ID_POLICY_FORMAT = "nameIDPolicyFormat"; + static final String LOGOUT_PAGE = "logoutPage"; + static final String FORCE_AUTHENTICATION = "forceAuthentication"; + static final String ROLE_IDENTIFIERS = "RoleIdentifiers"; + static final String SIGNING = "signing"; + static final String ENCRYPTION = "encryption"; + static final String KEYS = "Keys"; + static final String KEY = "Key"; + static final String RESOURCE = "resource"; + static final String PASSWORD = "password"; + static final String KEY_STORE = "KeyStore"; + static final String PRIVATE_KEY = "PrivateKey"; + static final String CERTIFICATE = "Certificate"; + + static final String PRIVATE_KEY_ALIAS = "alias"; + static final String PRIVATE_KEY_PASSWORD = "password"; + static final String CERTIFICATE_ALIAS = "alias"; + static final String SIGN_REQUEST = "signRequest"; + static final String VALIDATE_RESPONSE_SIGNATURE = "validateResponseSignature"; + static final String REQUEST_BINDING = "requestBinding"; + static final String BINDING_URL = "bindingUrl"; + static final String VALIDATE_REQUEST_SIGNATURE = "validateRequestSignature"; + static final String SIGN_RESPONSE = "signResponse"; + static final String RESPONSE_BINDING = "responseBinding"; + static final String POST_BINDING_URL = "postBindingUrl"; + static final String REDIRECT_BINDING_URL = "redirectBindingUrl"; + static final String SINGLE_SIGN_ON = "SingleSignOnService"; + static final String SINGLE_LOGOUT = "SingleLogoutService"; + static final String IDENTITY_PROVIDER = "IDP"; + static final String PRINCIPAL_NAME_MAPPING = "PrincipalNameMapping"; + static final String PRINCIPAL_NAME_MAPPING_POLICY = "policy"; + static final String PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = "attribute"; + static final String ATTRIBUTE = "Attribute"; + static final String SIGNATURE_ALGORITHM = "signatureAlgorithm"; + static final String SIGNATURE_CANONICALIZATION_METHOD = "signatureCanonicalizationMethod"; + static final String PRIVATE_KEY_PEM = "PrivateKeyPem"; + static final String PUBLIC_KEY_PEM = "PublicKeyPem"; + static final String CERTIFICATE_PEM = "CertificatePem"; + static final String TYPE = "type"; + static final String ALIAS = "alias"; + static final String FILE = "file"; + static final String SIGNATURES_REQUIRED = "signaturesRequired"; + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderAddHandler.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderAddHandler.java new file mode 100644 index 0000000000..7648ba3d3d --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderAddHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.dmr.ModelNode; + +/** + * @author Marko Strukelj + */ +class IdentityProviderAddHandler extends AbstractAddStepHandler { + + IdentityProviderAddHandler() { + super(IdentityProviderDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderDefinition.java new file mode 100644 index 0000000000..1459f15987 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/IdentityProviderDefinition.java @@ -0,0 +1,106 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ObjectTypeAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class IdentityProviderDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SIGNATURES_REQUIRED = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURES_REQUIRED, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGNATURES_REQUIRED) + .build(); + + static final SimpleAttributeDefinition SIGNATURE_ALGORITHM = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_ALGORITHM, ModelType.STRING, true) + .setXmlName(Constants.XML.SIGNATURE_ALGORITHM) + .build(); + + static final SimpleAttributeDefinition SIGNATURE_CANONICALIZATION_METHOD = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNATURE_CANONICALIZATION_METHOD, ModelType.STRING, true) + .setXmlName(Constants.XML.SIGNATURE_CANONICALIZATION_METHOD) + .build(); + + static final ObjectTypeAttributeDefinition SINGLE_SIGN_ON = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_SIGN_ON, + SingleSignOnDefinition.ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final ObjectTypeAttributeDefinition SINGLE_LOGOUT = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.SINGLE_LOGOUT, + SingleLogoutDefinition.ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD}; + + static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {SIGNATURES_REQUIRED, SIGNATURE_ALGORITHM, SIGNATURE_CANONICALIZATION_METHOD, SINGLE_SIGN_ON, SINGLE_LOGOUT}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ALL_ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static final IdentityProviderDefinition INSTANCE = new IdentityProviderDefinition(); + + private IdentityProviderDefinition() { + super(PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.IDENTITY_PROVIDER), + new IdentityProviderAddHandler(), + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} \ No newline at end of file diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyAddHandler.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyAddHandler.java new file mode 100644 index 0000000000..1799290cca --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyAddHandler.java @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.dmr.ModelNode; + +/** + * @author Marko Strukelj + */ +class KeyAddHandler extends AbstractAddStepHandler { + + KeyAddHandler() { + super(KeyDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyDefinition.java new file mode 100644 index 0000000000..7d199946a0 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyDefinition.java @@ -0,0 +1,122 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ObjectTypeAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class KeyDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SIGNING = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGNING, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGNING) + .build(); + + static final SimpleAttributeDefinition ENCRYPTION = + new SimpleAttributeDefinitionBuilder(Constants.Model.ENCRYPTION, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.ENCRYPTION) + .build(); + + static final SimpleAttributeDefinition PRIVATE_KEY_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_PEM) + .build(); + + static final SimpleAttributeDefinition PUBLIC_KEY_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.PUBLIC_KEY_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.PUBLIC_KEY_PEM) + .build(); + + static final SimpleAttributeDefinition CERTIFICATE_PEM = + new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_PEM, ModelType.STRING, true) + .setXmlName(Constants.XML.CERTIFICATE_PEM) + .build(); + + static final ObjectTypeAttributeDefinition KEY_STORE = + ObjectTypeAttributeDefinition.Builder.of(Constants.Model.KEY_STORE, + KeyStoreDefinition.ALL_ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGNING, ENCRYPTION}; + static final SimpleAttributeDefinition[] ELEMENTS = {PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM}; + static final AttributeDefinition[] ALL_ATTRIBUTES = {SIGNING, ENCRYPTION, PRIVATE_KEY_PEM, PUBLIC_KEY_PEM, CERTIFICATE_PEM, KEY_STORE}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static final HashMap ELEMENT_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ELEMENTS) { + ELEMENT_MAP.put(def.getXmlName(), def); + } + } + + static final KeyDefinition INSTANCE = new KeyDefinition(); + + private KeyDefinition() { + super(PathElement.pathElement(Constants.Model.KEY), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.KEY), + new KeyAddHandler(), + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } + + static SimpleAttributeDefinition lookupElement(String xmlName) { + return ELEMENT_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreCertificateDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreCertificateDefinition.java new file mode 100644 index 0000000000..83dc0571ae --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreCertificateDefinition.java @@ -0,0 +1,36 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +/** + * @author Marko Strukelj + */ +public class KeyStoreCertificateDefinition { + + static final SimpleAttributeDefinition CERTIFICATE_ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.CERTIFICATE_ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.CERTIFICATE_ALIAS) + .build(); + + static SimpleAttributeDefinition lookup(String xmlName) { + return Constants.XML.CERTIFICATE_ALIAS.equals(xmlName) ? CERTIFICATE_ALIAS : null; + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreDefinition.java new file mode 100644 index 0000000000..a71e89acde --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStoreDefinition.java @@ -0,0 +1,73 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class KeyStoreDefinition { + + static final SimpleAttributeDefinition RESOURCE = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESOURCE, ModelType.STRING, true) + .setXmlName(Constants.XML.RESOURCE) + .build(); + + static final SimpleAttributeDefinition PASSWORD = + new SimpleAttributeDefinitionBuilder(Constants.Model.PASSWORD, ModelType.STRING, true) + .setXmlName(Constants.XML.PASSWORD) + .build(); + + static final SimpleAttributeDefinition FILE = + new SimpleAttributeDefinitionBuilder(Constants.Model.FILE, ModelType.STRING, true) + .setXmlName(Constants.XML.FILE) + .build(); + + static final SimpleAttributeDefinition TYPE = + new SimpleAttributeDefinitionBuilder(Constants.Model.TYPE, ModelType.STRING, true) + .setXmlName(Constants.XML.TYPE) + .build(); + + static final SimpleAttributeDefinition ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.ALIAS) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS}; + static final SimpleAttributeDefinition[] ALL_ATTRIBUTES = {RESOURCE, PASSWORD, FILE, TYPE, ALIAS, + KeyStorePrivateKeyDefinition.PRIVATE_KEY_ALIAS, + KeyStorePrivateKeyDefinition.PRIVATE_KEY_PASSWORD, + KeyStoreCertificateDefinition.CERTIFICATE_ALIAS + }; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStorePrivateKeyDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStorePrivateKeyDefinition.java new file mode 100644 index 0000000000..bbb398db0d --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeyStorePrivateKeyDefinition.java @@ -0,0 +1,52 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class KeyStorePrivateKeyDefinition { + static final SimpleAttributeDefinition PRIVATE_KEY_ALIAS = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_ALIAS, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_ALIAS) + .build(); + + static final SimpleAttributeDefinition PRIVATE_KEY_PASSWORD = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRIVATE_KEY_PASSWORD, ModelType.STRING, true) + .setXmlName(Constants.XML.PRIVATE_KEY_PASSWORD) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {PRIVATE_KEY_ALIAS, PRIVATE_KEY_PASSWORD}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java new file mode 100755 index 0000000000..2c0aa1e96a --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java @@ -0,0 +1,128 @@ +/* + * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ + +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.server.deployment.DeploymentPhaseContext; +import org.jboss.as.server.deployment.DeploymentUnit; +import org.jboss.as.server.deployment.DeploymentUnitProcessingException; +import org.jboss.as.server.deployment.DeploymentUnitProcessor; +import org.jboss.as.web.common.WarMetaData; +import org.jboss.dmr.ModelNode; +import org.jboss.metadata.javaee.spec.ParamValueMetaData; +import org.jboss.metadata.web.jboss.JBossWebMetaData; +import org.jboss.metadata.web.spec.LoginConfigMetaData; +import org.jboss.staxmapper.FormattingXMLStreamWriter; +import org.jboss.staxmapper.XMLExtendedStreamWriter; +import org.keycloak.adapters.saml.AdapterConstants; +import org.keycloak.subsystem.adapter.saml.extension.logging.KeycloakLogger; + +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import java.io.ByteArrayOutputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.List; + +/** + * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc. + */ +public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor { + + @Override + public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { + DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); + + String deploymentName = deploymentUnit.getName(); + if (Configuration.INSTANCE.getSecureDeployment(deploymentName) != null) { + addKeycloakSamlAuthData(phaseContext, deploymentName); + } + } + + private void addKeycloakSamlAuthData(DeploymentPhaseContext phaseContext, String deploymentName) throws DeploymentUnitProcessingException { + DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); + WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY); + if (warMetaData == null) { + throw new DeploymentUnitProcessingException("WarMetaData not found for " + deploymentName + ". Make sure you have specified a WAR as your secure-deployment in the Keycloak subsystem."); + } + + try { + addXMLData(getXML(deploymentName), warMetaData); + } catch (Exception e) { + throw new DeploymentUnitProcessingException("Failed to configure KeycloakSamlExtension from subsystem model", e); + } + JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); + if (webMetaData == null) { + webMetaData = new JBossWebMetaData(); + warMetaData.setMergedJBossWebMetaData(webMetaData); + } + + LoginConfigMetaData loginConfig = webMetaData.getLoginConfig(); + if (loginConfig == null) { + loginConfig = new LoginConfigMetaData(); + webMetaData.setLoginConfig(loginConfig); + } + loginConfig.setAuthMethod("KEYCLOAK-SAML"); + + KeycloakLogger.ROOT_LOGGER.deploymentSecured(deploymentName); + } + + private String getXML(String deploymentName) throws XMLStreamException { + ModelNode node = Configuration.INSTANCE.getSecureDeployment(deploymentName); + if (node != null) { + KeycloakSubsystemParser writer = new KeycloakSubsystemParser(); + ByteArrayOutputStream output = new ByteArrayOutputStream(); + XMLExtendedStreamWriter streamWriter = new FormattingXMLStreamWriter(XMLOutputFactory.newInstance().createXMLStreamWriter(output)); + try { + streamWriter.writeStartElement("keycloak-saml-adapter"); + writer.writeSps(streamWriter, node); + streamWriter.writeEndElement(); + } finally { + streamWriter.close(); + } + return new String(output.toByteArray(), Charset.forName("utf-8")); + } + return null; + } + + private void addXMLData(String xml, WarMetaData warMetaData) { + JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData(); + if (webMetaData == null) { + webMetaData = new JBossWebMetaData(); + warMetaData.setMergedJBossWebMetaData(webMetaData); + } + + List contextParams = webMetaData.getContextParams(); + if (contextParams == null) { + contextParams = new ArrayList<>(); + } + + ParamValueMetaData param = new ParamValueMetaData(); + param.setParamName(AdapterConstants.AUTH_DATA_PARAM_NAME); + param.setParamValue(xml); + contextParams.add(param); + + webMetaData.setContextParams(contextParams); + } + + @Override + public void undeploy(DeploymentUnit du) { + + } +} 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/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java similarity index 99% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java index 2dad307529..6d30764788 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessor.java @@ -29,7 +29,6 @@ import org.jboss.modules.ModuleIdentifier; import org.jboss.modules.ModuleLoader; /** - * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ public abstract class KeycloakDependencyProcessor implements DeploymentUnitProcessor { diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessorWildFly.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessorWildFly.java similarity index 100% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessorWildFly.java rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakDependencyProcessorWildFly.java diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java similarity index 82% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java index 0a5de82358..bddbce2bea 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSamlExtension.java @@ -37,13 +37,12 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SUB public class KeycloakSamlExtension implements Extension { public static final String SUBSYSTEM_NAME = "keycloak-saml"; - public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.6"; + public static final String NAMESPACE = "urn:jboss:domain:keycloak-saml:1.1"; private static final KeycloakSubsystemParser PARSER = new KeycloakSubsystemParser(); static final PathElement PATH_SUBSYSTEM = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); private static final String RESOURCE_NAME = KeycloakSamlExtension.class.getPackage().getName() + ".LocalDescriptions"; - private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1,1,0); + private static final ModelVersion MGMT_API_VERSION = ModelVersion.create(1, 1, 0); static final PathElement SUBSYSTEM_PATH = PathElement.pathElement(SUBSYSTEM, SUBSYSTEM_NAME); - private static final ResourceDefinition KEYCLOAK_SUBSYSTEM_RESOURCE = new KeycloakSubsystemDefinition(); public static StandardResourceDescriptionResolver getResourceDescriptionResolver(final String... keyPrefix) { StringBuilder prefix = new StringBuilder(SUBSYSTEM_NAME); @@ -68,7 +67,12 @@ public class KeycloakSamlExtension implements Extension { public void initialize(final ExtensionContext context) { final SubsystemRegistration subsystem = context.registerSubsystem(SUBSYSTEM_NAME, MGMT_API_VERSION); - ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KEYCLOAK_SUBSYSTEM_RESOURCE); + ManagementResourceRegistration registration = subsystem.registerSubsystemModel(KeycloakSubsystemDefinition.INSTANCE); + ManagementResourceRegistration secureDeploymentRegistration = registration.registerSubModel(SecureDeploymentDefinition.INSTANCE); + ManagementResourceRegistration serviceProviderRegistration = secureDeploymentRegistration.registerSubModel(ServiceProviderDefinition.INSTANCE); + serviceProviderRegistration.registerSubModel(KeyDefinition.INSTANCE); + ManagementResourceRegistration idpRegistration = serviceProviderRegistration.registerSubModel(IdentityProviderDefinition.INSTANCE); + idpRegistration.registerSubModel(KeyDefinition.INSTANCE); subsystem.registerXMLElementWriter(PARSER); } diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java similarity index 99% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java index 139432408d..dbd58b49f4 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemAdd.java @@ -16,7 +16,6 @@ */ package org.keycloak.subsystem.adapter.saml.extension; - import org.jboss.as.controller.AbstractBoottimeAddStepHandler; import org.jboss.as.controller.OperationContext; import org.jboss.as.server.AbstractDeploymentChainStep; diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java similarity index 90% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java index db02d60a8a..76ca0bc74f 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemDefinition.java @@ -14,7 +14,6 @@ * License for the specific language governing permissions and limitations under * the License. */ - package org.keycloak.subsystem.adapter.saml.extension; import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; @@ -23,12 +22,15 @@ import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler import org.jboss.as.controller.registry.ManagementResourceRegistration; /** - * Definition of subsystem=keycloak. + * Definition of subsystem=keycloak-saml. * * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. */ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { - protected KeycloakSubsystemDefinition() { + + static final KeycloakSubsystemDefinition INSTANCE = new KeycloakSubsystemDefinition(); + + private KeycloakSubsystemDefinition() { super(KeycloakSamlExtension.SUBSYSTEM_PATH, KeycloakSamlExtension.getResourceDescriptionResolver("subsystem"), KeycloakSubsystemAdd.INSTANCE, @@ -41,5 +43,4 @@ public class KeycloakSubsystemDefinition extends SimpleResourceDefinition { super.registerOperations(resourceRegistration); resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); } - } diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java new file mode 100755 index 0000000000..8b205652f8 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java @@ -0,0 +1,569 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.PathAddress; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.descriptions.ModelDescriptionConstants; +import org.jboss.as.controller.operations.common.Util; +import org.jboss.as.controller.parsing.ParseUtils; +import org.jboss.as.controller.persistence.SubsystemMarshallingContext; +import org.jboss.dmr.ModelNode; +import org.jboss.dmr.Property; +import org.jboss.staxmapper.XMLElementReader; +import org.jboss.staxmapper.XMLElementWriter; +import org.jboss.staxmapper.XMLExtendedStreamReader; +import org.jboss.staxmapper.XMLExtendedStreamWriter; + +import javax.xml.stream.XMLStreamConstants; +import javax.xml.stream.XMLStreamException; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + +/** + * The subsystem parser, which uses stax to read and write to and from xml + */ +class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter { + + /** + * {@inheritDoc} + */ + @Override + public void readElement(final XMLExtendedStreamReader reader, final List list) throws XMLStreamException { + // Require no attributes + ParseUtils.requireNoAttributes(reader); + ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM)); + list.add(addKeycloakSub); + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + if (reader.getLocalName().equals(Constants.XML.SECURE_DEPLOYMENT)) { + readSecureDeployment(reader, list); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + // used for debugging + private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException { + return reader.nextTag(); + } + + void readSecureDeployment(XMLExtendedStreamReader reader, List list) throws XMLStreamException { + String name = readRequiredAttribute(reader, Constants.XML.NAME); + + PathAddress addr = PathAddress.pathAddress( + PathElement.pathElement(ModelDescriptionConstants.SUBSYSTEM, KeycloakSamlExtension.SUBSYSTEM_NAME), + PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT, name)); + ModelNode addSecureDeployment = Util.createAddOperation(addr); + list.add(addSecureDeployment); + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (tagName.equals(Constants.XML.SERVICE_PROVIDER)) { + readServiceProvider(reader, list, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readServiceProvider(XMLExtendedStreamReader reader, List list, PathAddress parentAddr) throws XMLStreamException { + String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID); + + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.SERVICE_PROVIDER, entityId)); + ModelNode addServiceProvider = Util.createAddOperation(addr); + list.add(addServiceProvider); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + if (Constants.XML.ENTITY_ID.equals(name)) { + continue; + } + + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = ServiceProviderDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addServiceProvider, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.KEYS.equals(tagName)) { + readKeys(list, reader, addr); + } else if (Constants.XML.PRINCIPAL_NAME_MAPPING.equals(tagName)) { + readPrincipalNameMapping(addServiceProvider, reader); + } else if (Constants.XML.ROLE_IDENTIFIERS.equals(tagName)) { + readRoleIdentifiers(addServiceProvider, reader); + } else if (Constants.XML.IDENTITY_PROVIDER.equals(tagName)) { + readIdentityProvider(list, reader, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readIdentityProvider(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + String entityId = readRequiredAttribute(reader, Constants.XML.ENTITY_ID); + + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.IDENTITY_PROVIDER, entityId)); + ModelNode addIdentityProvider = Util.createAddOperation(addr); + list.add(addIdentityProvider); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + if (Constants.XML.ENTITY_ID.equals(name) + // don't break if encountering this noop attr from client-adapter/core keycloak_saml_adapter_1_6.xsd + || "encryption".equals(name)) { + continue; + } + SimpleAttributeDefinition attr = IdentityProviderDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addIdentityProvider, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.SINGLE_SIGN_ON.equals(tagName)) { + readSingleSignOn(addIdentityProvider, reader); + } else if (Constants.XML.SINGLE_LOGOUT.equals(tagName)) { + readSingleLogout(addIdentityProvider, reader); + } else if (Constants.XML.KEYS.equals(tagName)) { + readKeys(list, reader, addr); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readSingleSignOn(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode sso = addIdentityProvider.get(Constants.Model.SINGLE_SIGN_ON); + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = SingleSignOnDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, sso, reader); + } + ParseUtils.requireNoContent(reader); + } + + void readSingleLogout(ModelNode addIdentityProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode slo = addIdentityProvider.get(Constants.Model.SINGLE_LOGOUT); + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = SingleLogoutDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, slo, reader); + } + ParseUtils.requireNoContent(reader); + } + + void readKeys(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + List keyList = new LinkedList<>(); + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (!Constants.XML.KEY.equals(tagName)) { + throw ParseUtils.unexpectedElement(reader); + } + readKey(keyList, reader, parentAddr); + } + list.addAll(keyList); + } + + void readKey(List list, XMLExtendedStreamReader reader, PathAddress parentAddr) throws XMLStreamException { + PathAddress addr = PathAddress.pathAddress(parentAddr, + PathElement.pathElement(Constants.Model.KEY, "key-" + list.size())); + ModelNode addKey = Util.createAddOperation(addr); + list.add(addKey); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKey, reader); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (Constants.XML.KEY_STORE.equals(tagName)) { + readKeyStore(addKey, reader); + } else if (Constants.XML.PRIVATE_KEY_PEM.equals(tagName) + || Constants.XML.PUBLIC_KEY_PEM.equals(tagName) + || Constants.XML.CERTIFICATE_PEM.equals(tagName)) { + + readNoAttrElementContent(KeyDefinition.lookupElement(tagName), addKey, reader); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + void readNoAttrElementContent(SimpleAttributeDefinition attr, ModelNode model, XMLExtendedStreamReader reader) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + String value = reader.getElementText(); + attr.parseAndSetParameter(value, model, reader); + } + + void readKeyStore(ModelNode addKey, XMLExtendedStreamReader reader) throws XMLStreamException { + ModelNode addKeyStore = addKey.get(Constants.Model.KEY_STORE); + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStoreDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.FILE) && !addKeyStore.hasDefined(Constants.Model.RESOURCE)) { + throw new XMLStreamException("KeyStore element must have 'file' or 'resource' attribute set", reader.getLocation()); + } + if (!addKeyStore.hasDefined(Constants.Model.PASSWORD)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PASSWORD); + } + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + if (Constants.XML.PRIVATE_KEY.equals(tagName)) { + readPrivateKey(reader, addKeyStore); + } else if (Constants.XML.CERTIFICATE.equals(tagName)) { + readCertificate(reader, addKeyStore); + } else { + throw ParseUtils.unexpectedElement(reader); + } + } + } + + + void readPrivateKey(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException { + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStorePrivateKeyDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_ALIAS)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_ALIAS); + } + if (!addKeyStore.hasDefined(Constants.Model.PRIVATE_KEY_PASSWORD)) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRIVATE_KEY_PASSWORD); + } + + ParseUtils.requireNoContent(reader); + } + + void readCertificate(XMLExtendedStreamReader reader, ModelNode addKeyStore) throws XMLStreamException { + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.lookup(name); + if (attr == null) { + throw ParseUtils.unexpectedAttribute(reader, i); + } + attr.parseAndSetParameter(value, addKeyStore, reader); + } + + if (!addKeyStore.hasDefined(Constants.Model.CERTIFICATE_ALIAS)) { + throw ParseUtils.missingRequired(reader, Constants.XML.CERTIFICATE_ALIAS); + } + + ParseUtils.requireNoContent(reader); + } + + void readRoleIdentifiers(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + ParseUtils.requireNoAttributes(reader); + + while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { + String tagName = reader.getLocalName(); + + if (!Constants.XML.ATTRIBUTE.equals(tagName)) { + throw ParseUtils.unexpectedElement(reader); + } + + ParseUtils.requireSingleAttribute(reader, Constants.XML.NAME); + String name = ParseUtils.readStringAttributeElement(reader, Constants.XML.NAME); + + ServiceProviderDefinition.ROLE_ATTRIBUTES.parseAndAddParameterElement(name, addServiceProvider, reader); + } + } + + void readPrincipalNameMapping(ModelNode addServiceProvider, XMLExtendedStreamReader reader) throws XMLStreamException { + + boolean policySet = false; + + for (int i = 0; i < reader.getAttributeCount(); i++) { + String name = reader.getAttributeLocalName(i); + String value = reader.getAttributeValue(i); + + if (Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY.equals(name)) { + policySet = true; + ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_POLICY.parseAndSetParameter(value, addServiceProvider, reader); + } else if (Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.equals(name)) { + ServiceProviderDefinition.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME.parseAndSetParameter(value, addServiceProvider, reader); + } else { + throw ParseUtils.unexpectedAttribute(reader, i); + } + } + + if (!policySet) { + throw ParseUtils.missingRequired(reader, Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY); + } + ParseUtils.requireNoContent(reader); + } + + /** + * Read an attribute, and throw exception if attribute is not present + */ + String readRequiredAttribute(XMLExtendedStreamReader reader, String attrName) throws XMLStreamException { + String value = null; + for (int i = 0; i < reader.getAttributeCount(); i++) { + String attr = reader.getAttributeLocalName(i); + if (attr.equals(attrName)) { + value = reader.getAttributeValue(i); + break; + } + } + if (value == null) { + throw ParseUtils.missingRequired(reader, Collections.singleton(attrName)); + } + return value; + } + + /** + * {@inheritDoc} + */ + @Override + public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException { + context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false); + writeSecureDeployment(writer, context.getModelNode()); + writer.writeEndElement(); + } + + public void writeSecureDeployment(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.get(Constants.Model.SECURE_DEPLOYMENT).isDefined()) { + return; + } + + for (Property sp : model.get(Constants.Model.SECURE_DEPLOYMENT).asPropertyList()) { + writer.writeStartElement(Constants.XML.SECURE_DEPLOYMENT); + writer.writeAttribute(Constants.XML.NAME, sp.getName()); + + writeSps(writer, sp.getValue()); + writer.writeEndElement(); + } + } + + void writeSps(final XMLExtendedStreamWriter writer, final ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + for (Property sp : model.get(Constants.Model.SERVICE_PROVIDER).asPropertyList()) { + writer.writeStartElement(Constants.XML.SERVICE_PROVIDER); + writer.writeAttribute(Constants.XML.ENTITY_ID, sp.getName()); + ModelNode spAttributes = sp.getValue(); + for (SimpleAttributeDefinition attr : ServiceProviderDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, spAttributes, false, writer); + } + writeKeys(writer, spAttributes.get(Constants.Model.KEY)); + writePrincipalNameMapping(writer, spAttributes); + writeRoleIdentifiers(writer, spAttributes); + writeIdentityProvider(writer, spAttributes.get(Constants.Model.IDENTITY_PROVIDER)); + + writer.writeEndElement(); + } + } + + void writeIdentityProvider(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + + for (Property idp : model.asPropertyList()) { + writer.writeStartElement(Constants.XML.IDENTITY_PROVIDER); + writer.writeAttribute(Constants.XML.ENTITY_ID, idp.getName()); + + ModelNode idpAttributes = idp.getValue(); + for (SimpleAttributeDefinition attr : IdentityProviderDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, idpAttributes, false, writer); + } + + writeSingleSignOn(writer, idpAttributes.get(Constants.Model.SINGLE_SIGN_ON)); + writeSingleLogout(writer, idpAttributes.get(Constants.Model.SINGLE_LOGOUT)); + writeKeys(writer, idpAttributes.get(Constants.Model.KEY)); + } + writer.writeEndElement(); + } + + void writeSingleSignOn(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + writer.writeStartElement(Constants.XML.SINGLE_SIGN_ON); + for (SimpleAttributeDefinition attr : SingleSignOnDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer); + } + writer.writeEndElement(); + } + + void writeSingleLogout(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + writer.writeStartElement(Constants.XML.SINGLE_LOGOUT); + for (SimpleAttributeDefinition attr : SingleLogoutDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer); + } + writer.writeEndElement(); + } + + void writeKeys(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + boolean contains = false; + for (Property key : model.asPropertyList()) { + if (!contains) { + writer.writeStartElement(Constants.XML.KEYS); + contains = true; + } + writer.writeStartElement(Constants.XML.KEY); + + ModelNode keyAttributes = key.getValue(); + for (SimpleAttributeDefinition attr : KeyDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, keyAttributes, false, writer); + } + for (SimpleAttributeDefinition attr : KeyDefinition.ELEMENTS) { + attr.getAttributeMarshaller().marshallAsElement(attr, keyAttributes, false, writer); + } + writeKeyStore(writer, keyAttributes.get(Constants.Model.KEY_STORE)); + + writer.writeEndElement(); + } + if (contains) { + writer.writeEndElement(); + } + } + + void writeKeyStore(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + if (!model.isDefined()) { + return; + } + writer.writeStartElement(Constants.XML.KEY_STORE); + for (SimpleAttributeDefinition attr : KeyStoreDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer); + } + writePrivateKey(writer, model); + writeCertificate(writer, model); + writer.writeEndElement(); + } + + void writeCertificate(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + ModelNode value = model.get(Constants.Model.CERTIFICATE_ALIAS); + if (!value.isDefined()) { + return; + } + writer.writeStartElement(Constants.XML.CERTIFICATE); + SimpleAttributeDefinition attr = KeyStoreCertificateDefinition.CERTIFICATE_ALIAS; + attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer); + writer.writeEndElement(); + } + + void writePrivateKey(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + ModelNode pk_alias = model.get(Constants.Model.PRIVATE_KEY_ALIAS); + ModelNode pk_password = model.get(Constants.Model.PRIVATE_KEY_PASSWORD); + + if (!pk_alias.isDefined() && !pk_password.isDefined()) { + return; + } + writer.writeStartElement(Constants.XML.PRIVATE_KEY); + for (SimpleAttributeDefinition attr : KeyStorePrivateKeyDefinition.ATTRIBUTES) { + attr.getAttributeMarshaller().marshallAsAttribute(attr, model, false, writer); + } + writer.writeEndElement(); + } + + void writeRoleIdentifiers(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + ModelNode value = model.get(Constants.Model.ROLE_ATTRIBUTES); + if (!value.isDefined()) { + return; + } + + List items = value.asList(); + if (items.size() == 0) { + return; + } + + writer.writeStartElement(Constants.XML.ROLE_IDENTIFIERS); + for (ModelNode item : items) { + writer.writeStartElement(Constants.XML.ATTRIBUTE); + writer.writeAttribute("name", item.asString()); + writer.writeEndElement(); + } + writer.writeEndElement(); + } + + void writePrincipalNameMapping(XMLExtendedStreamWriter writer, ModelNode model) throws XMLStreamException { + writer.writeStartElement(Constants.XML.PRINCIPAL_NAME_MAPPING); + ModelNode value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY); + if (value.isDefined()) { + writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY, value.asString()); + } + value = model.get(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME); + if (value.isDefined()) { + writer.writeAttribute(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, value.asString()); + } + writer.writeEndElement(); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentAddHandler.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentAddHandler.java new file mode 100644 index 0000000000..22de93dc46 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentAddHandler.java @@ -0,0 +1,38 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.dmr.ModelNode; + +/** + * @author Marko Strukelj + */ +class SecureDeploymentAddHandler extends AbstractAddStepHandler { + + static SecureDeploymentAddHandler INSTANCE = new SecureDeploymentAddHandler(); + + private SecureDeploymentAddHandler() { + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentDefinition.java new file mode 100644 index 0000000000..bf6cab5b3d --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SecureDeploymentDefinition.java @@ -0,0 +1,47 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.*; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * Defines attributes and operations for a secure-deployment. + */ +public class SecureDeploymentDefinition extends SimpleResourceDefinition { + + static final SecureDeploymentDefinition INSTANCE = new SecureDeploymentDefinition(); + + private SecureDeploymentDefinition() { + super(PathElement.pathElement(Constants.Model.SECURE_DEPLOYMENT), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SECURE_DEPLOYMENT), + SecureDeploymentAddHandler.INSTANCE, + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderAddHandler.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderAddHandler.java new file mode 100644 index 0000000000..f9f3070ea2 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderAddHandler.java @@ -0,0 +1,39 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AbstractAddStepHandler; +import org.jboss.as.controller.OperationContext; +import org.jboss.as.controller.OperationFailedException; +import org.jboss.dmr.ModelNode; + +/** + * @author Marko Strukelj + */ +class ServiceProviderAddHandler extends AbstractAddStepHandler { + + static final ServiceProviderAddHandler INSTANCE = new ServiceProviderAddHandler(); + + ServiceProviderAddHandler() { + super(ServiceProviderDefinition.ALL_ATTRIBUTES); + } + + @Override + protected void performRuntime(OperationContext context, ModelNode operation, ModelNode model) throws OperationFailedException { + Configuration.INSTANCE.updateModel(operation, model); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderDefinition.java new file mode 100644 index 0000000000..cb84f1253d --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/ServiceProviderDefinition.java @@ -0,0 +1,125 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.AttributeDefinition; +import org.jboss.as.controller.ListAttributeDefinition; +import org.jboss.as.controller.OperationStepHandler; +import org.jboss.as.controller.PathElement; +import org.jboss.as.controller.ReloadRequiredRemoveStepHandler; +import org.jboss.as.controller.ReloadRequiredWriteAttributeHandler; +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.as.controller.SimpleResourceDefinition; +import org.jboss.as.controller.StringListAttributeDefinition; +import org.jboss.as.controller.operations.common.GenericSubsystemDescribeHandler; +import org.jboss.as.controller.registry.ManagementResourceRegistration; +import org.jboss.dmr.ModelType; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +public class ServiceProviderDefinition extends SimpleResourceDefinition { + + static final SimpleAttributeDefinition SSL_POLICY = + new SimpleAttributeDefinitionBuilder(Constants.Model.SSL_POLICY, ModelType.STRING, true) + .setXmlName(Constants.XML.SSL_POLICY) + .build(); + + static final SimpleAttributeDefinition NAME_ID_POLICY_FORMAT = + new SimpleAttributeDefinitionBuilder(Constants.Model.NAME_ID_POLICY_FORMAT, ModelType.STRING, true) + .setXmlName(Constants.XML.NAME_ID_POLICY_FORMAT) + .build(); + + static final SimpleAttributeDefinition LOGOUT_PAGE = + new SimpleAttributeDefinitionBuilder(Constants.Model.LOGOUT_PAGE, ModelType.STRING, true) + .setXmlName(Constants.XML.LOGOUT_PAGE) + .build(); + + static final SimpleAttributeDefinition FORCE_AUTHENTICATION = + new SimpleAttributeDefinitionBuilder(Constants.Model.FORCE_AUTHENTICATION, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.FORCE_AUTHENTICATION) + .build(); + + static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_POLICY = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_POLICY, ModelType.STRING, true) + .setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_POLICY) + .build(); + + static final SimpleAttributeDefinition PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME = + new SimpleAttributeDefinitionBuilder(Constants.Model.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ModelType.STRING, true) + .setXmlName(Constants.XML.PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME) + .build(); + + static final ListAttributeDefinition ROLE_ATTRIBUTES = + new StringListAttributeDefinition.Builder(Constants.Model.ROLE_ATTRIBUTES) + .setAllowNull(false) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SSL_POLICY, NAME_ID_POLICY_FORMAT, LOGOUT_PAGE, FORCE_AUTHENTICATION}; + static final AttributeDefinition[] ELEMENTS = {PRINCIPAL_NAME_MAPPING_POLICY, PRINCIPAL_NAME_MAPPING_ATTRIBUTE_NAME, ROLE_ATTRIBUTES}; + + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + static final HashMap ALL_MAP = new HashMap<>(); + static final Collection ALL_ATTRIBUTES; + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + + ALL_MAP.putAll(ATTRIBUTE_MAP); + for (AttributeDefinition def : ELEMENTS) { + ALL_MAP.put(def.getXmlName(), def); + } + ALL_ATTRIBUTES = Collections.unmodifiableCollection(ALL_MAP.values()); + } + + static final ServiceProviderDefinition INSTANCE = new ServiceProviderDefinition(); + + private ServiceProviderDefinition() { + super(PathElement.pathElement(Constants.Model.SERVICE_PROVIDER), + KeycloakSamlExtension.getResourceDescriptionResolver(Constants.Model.SERVICE_PROVIDER), + ServiceProviderAddHandler.INSTANCE, + ReloadRequiredRemoveStepHandler.INSTANCE); + } + + @Override + public void registerOperations(ManagementResourceRegistration resourceRegistration) { + super.registerOperations(resourceRegistration); + resourceRegistration.registerOperationHandler(GenericSubsystemDescribeHandler.DEFINITION, GenericSubsystemDescribeHandler.INSTANCE); + } + + @Override + public void registerAttributes(ManagementResourceRegistration resourceRegistration) { + super.registerAttributes(resourceRegistration); + + final OperationStepHandler writeHandler = new ReloadRequiredWriteAttributeHandler(ALL_ATTRIBUTES); + for (AttributeDefinition attribute : ALL_ATTRIBUTES) { + resourceRegistration.registerReadWriteAttribute(attribute, null, writeHandler); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleLogoutDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleLogoutDefinition.java new file mode 100644 index 0000000000..6ad99b159d --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleLogoutDefinition.java @@ -0,0 +1,84 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class SingleLogoutDefinition { + + static final SimpleAttributeDefinition VALIDATE_REQUEST_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_REQUEST_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_REQUEST_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition SIGN_REQUEST = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_REQUEST) + .build(); + + static final SimpleAttributeDefinition SIGN_RESPONSE = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_RESPONSE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_RESPONSE) + .build(); + + static final SimpleAttributeDefinition REQUEST_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.REQUEST_BINDING) + .build(); + + static final SimpleAttributeDefinition RESPONSE_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.RESPONSE_BINDING) + .build(); + + static final SimpleAttributeDefinition POST_BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.POST_BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.POST_BINDING_URL) + .build(); + + static final SimpleAttributeDefinition REDIRECT_BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.REDIRECT_BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.REDIRECT_BINDING_URL) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {VALIDATE_REQUEST_SIGNATURE, VALIDATE_RESPONSE_SIGNATURE, + SIGN_REQUEST, SIGN_RESPONSE, REQUEST_BINDING, RESPONSE_BINDING, POST_BINDING_URL, REDIRECT_BINDING_URL}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleSignOnDefinition.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleSignOnDefinition.java new file mode 100644 index 0000000000..fe78c023f4 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/SingleSignOnDefinition.java @@ -0,0 +1,68 @@ +/* + * Copyright 2015 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.controller.SimpleAttributeDefinition; +import org.jboss.as.controller.SimpleAttributeDefinitionBuilder; +import org.jboss.dmr.ModelType; + +import java.util.HashMap; + +/** + * @author Marko Strukelj + */ +abstract class SingleSignOnDefinition { + + static final SimpleAttributeDefinition SIGN_REQUEST = + new SimpleAttributeDefinitionBuilder(Constants.Model.SIGN_REQUEST, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.SIGN_REQUEST) + .build(); + + static final SimpleAttributeDefinition VALIDATE_RESPONSE_SIGNATURE = + new SimpleAttributeDefinitionBuilder(Constants.Model.VALIDATE_RESPONSE_SIGNATURE, ModelType.BOOLEAN, true) + .setXmlName(Constants.XML.VALIDATE_RESPONSE_SIGNATURE) + .build(); + + static final SimpleAttributeDefinition REQUEST_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.REQUEST_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.REQUEST_BINDING) + .build(); + + static final SimpleAttributeDefinition RESPONSE_BINDING = + new SimpleAttributeDefinitionBuilder(Constants.Model.RESPONSE_BINDING, ModelType.STRING, true) + .setXmlName(Constants.XML.RESPONSE_BINDING) + .build(); + + static final SimpleAttributeDefinition BINDING_URL = + new SimpleAttributeDefinitionBuilder(Constants.Model.BINDING_URL, ModelType.STRING, true) + .setXmlName(Constants.XML.BINDING_URL) + .build(); + + static final SimpleAttributeDefinition[] ATTRIBUTES = {SIGN_REQUEST, VALIDATE_RESPONSE_SIGNATURE, REQUEST_BINDING, RESPONSE_BINDING, BINDING_URL}; + + static final HashMap ATTRIBUTE_MAP = new HashMap<>(); + + static { + for (SimpleAttributeDefinition def : ATTRIBUTES) { + ATTRIBUTE_MAP.put(def.getXmlName(), def); + } + } + + static SimpleAttributeDefinition lookup(String xmlName) { + return ATTRIBUTE_MAP.get(xmlName); + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakLogger.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakLogger.java new file mode 100755 index 0000000000..d068b641ce --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakLogger.java @@ -0,0 +1,45 @@ +/* + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension.logging; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; + +import static org.jboss.logging.Logger.Level.INFO; + +/** + * This interface to be fleshed out later when error messages are fully externalized. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2013 Red Hat Inc. + */ +@MessageLogger(projectCode = "KEYCLOAK") +public interface KeycloakLogger extends BasicLogger { + + /** + * A logger with a category of the package name. + */ + KeycloakLogger ROOT_LOGGER = Logger.getMessageLogger(KeycloakLogger.class, "org.jboss.keycloak"); + + @LogMessage(level = INFO) + @Message(value = "Keycloak subsystem override for deployment %s") + void deploymentSecured(String deployment); + + +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakMessages.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakMessages.java new file mode 100755 index 0000000000..be44809a86 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/logging/KeycloakMessages.java @@ -0,0 +1,34 @@ +/* + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension.logging; + +import org.jboss.logging.Messages; +import org.jboss.logging.annotations.MessageBundle; + +/** + * This interface to be fleshed out later when error messages are fully externalized. + * + * @author Stan Silvert ssilvert@redhat.com (C) 2012 Red Hat Inc. + */ +@MessageBundle(projectCode = "KEYCLOAK") +public interface KeycloakMessages { + + /** + * The messages + */ + KeycloakMessages MESSAGES = Messages.getBundle(KeycloakMessages.class); +} diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension similarity index 100% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/META-INF/services/org.jboss.as.controller.Extension diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties new file mode 100755 index 0000000000..f8a4a11581 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/org/keycloak/subsystem/adapter/saml/extension/LocalDescriptions.properties @@ -0,0 +1,63 @@ +keycloak-saml.subsystem=Keycloak adapter subsystem +keycloak-saml.subsystem.add=Operation Adds Keycloak adapter subsystem +keycloak-saml.subsystem.remove=Operation removes Keycloak adapter subsystem +keycloak-saml.subsystem.secure-deployment=A deployment secured by Keycloak. + +keycloak-saml.secure-deployment=A deployment secured by Keycloak +keycloak-saml.secure-deployment.add=Add a deployment to be secured by Keycloak +keycloak-saml.secure-deployment.remove=Remove a deployment to be secured by Keycloak +keycloak-saml.secure-deployment.service-provider=A security provider configuration for secure deployment + +keycloak-saml.service-provider=A security provider configuration for secure deployment +keycloak-saml.service-provider.add=Add a security provider configuration to deployment secured by Keycloak SAML +keycloak-saml.service-provider.remove=Remove a security provider definition from deployment secured by Keycloak SAML +keycloak-saml.service-provider.ssl-policy=SSL Policy to use +keycloak-saml.service-provider.name-id-policy-format=Name ID policy format URN +keycloak-saml.service-provider.logout-page=URI to a logout page +keycloak-saml.service-provider.force-authentication=Redirected unauthenticated request to a login page +keycloak-saml.service-provider.role-attributes=Role identifiers +keycloak-saml.service-provider.principal-name-mapping-policy=Principal name mapping policy +keycloak-saml.service-provider.principal-name-mapping-attribute-name=Principal name mapping attribute name +keycloak-saml.service-provider.key=A key definition +keycloak-saml.service-provider.identity-provider=Identity provider definition + +keycloak-saml.key=A key configuration for service provider or identity provider +keycloak-saml.key.add=Add a key definition +keycloak-saml.key.remove=Remove a key definition +keycloak-saml.key.signing=Key can be used for signing +keycloak-saml.key.encryption=Key can be used for encryption +keycloak-saml.key.private-key-pem=Private key string in pem format +keycloak-saml.key.public-key-pem=Public key string in pem format +keycloak-saml.key.certificate-pem=Certificate key string in pem format +keycloak-saml.key.key-store=Key store definition +keycloak-saml.key.key-store.file=Key store filesystem path +keycloak-saml.key.key-store.resource=Key store resource URI +keycloak-saml.key.key-store.password=Key store password +keycloak-saml.key.key-store.type=Key store format +keycloak-saml.key.key-store.alias=Key alias +keycloak-saml.key.key-store.private-key-alias=Private key alias +keycloak-saml.key.key-store.private-key-password=Private key password +keycloak-saml.key.key-store.certificate-alias=Certificate alias + +keycloak-saml.identity-provider=An identity provider configuration +keycloak-saml.identity-provider.add=Add an identity provider +keycloak-saml.identity-provider.remove=Remove an identity provider +keycloak-saml.identity-provider.signatures-required=Require signatures for single-sign-on and single-logout +keycloak-saml.identity-provider.signature-algorithm=Signature algorithm +keycloak-saml.identity-provider.signature-canonicalization-method=Signature canonicalization method +keycloak-saml.identity-provider.single-sign-on=Single sign-on configuration +keycloak-saml.identity-provider.single-sign-on.sign-request=Sign SSO requests +keycloak-saml.identity-provider.single-sign-on.validate-response-signature=Validate an SSO response signature +keycloak-saml.identity-provider.single-sign-on.request-binding=HTTP method to use for requests +keycloak-saml.identity-provider.single-sign-on.response-binding=HTTP method to use for responses +keycloak-saml.identity-provider.single-sign-on.binding-url=SSO endpoint URL +keycloak-saml.identity-provider.single-logout=Single logout configuration +keycloak-saml.identity-provider.single-logout.validate-request-signature=Validate a single-logout request signature +keycloak-saml.identity-provider.single-logout.validate-response-signature=Validate a single-logout response signature +keycloak-saml.identity-provider.single-logout.sign-request=Sign single-logout requests +keycloak-saml.identity-provider.single-logout.sign-response=Sign single-logout responses +keycloak-saml.identity-provider.single-logout.request-binding=HTTP method to use for request +keycloak-saml.identity-provider.single-logout.response-binding=HTTP method to use for response +keycloak-saml.identity-provider.single-logout.post-binding-url=Endpoint URL for posting +keycloak-saml.identity-provider.single-logout.redirect-binding-url=Endpoint URL for redirects +keycloak-saml.identity-provider.key=Key definition for identity provider \ No newline at end of file diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd new file mode 100755 index 0000000000..725104ba36 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/schema/wildfly-keycloak-saml_1_1.xsd @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + The name of the realm. + + + + + + + + + + + + + + The entity ID for SAML service provider + + + + + The ssl policy + + + + + Name ID policy format URN + + + + + URI to a logout page + + + + + Redirected unauthenticated request to a login page + + + + + + + + + + + + The entity ID for SAML service provider + + + + + Require signatures for single-sign-on and single-logout + + + + + Algorithm used for signatures + + + + + Canonicalization method used for signatures + + + + + + + Sign the SSO requests + + + + + Validate the SSO response signature + + + + + HTTP method to use for requests + + + + + HTTP method to use for response + + + + + SSO endpoint URL + + + + + + + Validate a single-logout request signature + + + + + Validate a single-logout response signature + + + + + Sign single-logout requests + + + + + Sign single-logout responses + + + + + HTTP method to use for request + + + + + HTTP method to use for response + + + + + Endpoint URL for posting + + + + + Endpoint URL for redirects + + + + + + + + + + + + + + + + + + Key can be used for signing + + + + + Key can be used for encryption + + + + + + + + + + + Key store filesystem path + + + + + Key store resource URI + + + + + Key store password + + + + + Key store format + + + + + Key alias + + + + + + + + Private key alias + + + + + Private key password + + + + + + + + Certificate alias + + + + + + + + Principal name mapping policy. Possible values: FROM_NAME_ID + + + + + Name of the attribute to use for principal name mapping + + + + + + + + + + + + + + Role attribute + + + + diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml similarity index 68% rename from saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml rename to saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml index 8677d317e2..9696cbdda3 100755 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/resources/subsystem-templates/keycloak-adapter.xml +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/main/resources/subsystem-templates/keycloak-saml-adapter.xml @@ -1,7 +1,7 @@ - + org.keycloak.keycloak-saml-adapter-subsystem - + diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java new file mode 100755 index 0000000000..fdd8ee24a9 --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/java/org/keycloak/subsystem/adapter/saml/extension/SubsystemParsingTestCase.java @@ -0,0 +1,56 @@ +/* + * Copyright 2013 Red Hat Inc. and/or its affiliates and other contributors + * as indicated by the @author tags. All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations under + * the License. + */ +package org.keycloak.subsystem.adapter.saml.extension; + +import org.jboss.as.subsystem.test.AbstractSubsystemBaseTest; + +import java.io.IOException; + + +/** + * Tests all management expects for subsystem, parsing, marshaling, model definition and other + * Here is an example that allows you a fine grained controller over what is tested and how. So it can give you ideas what can be done and tested. + * If you have no need for advanced testing of subsystem you look at {@link SubsystemBaseParsingTestCase} that testes same stuff but most of the code + * is hidden inside of test harness + * + * @author Kabir Khan + * @author Tomaz Cerar + * @author Marko Strukelj + */ +public class SubsystemParsingTestCase extends AbstractSubsystemBaseTest { + + public SubsystemParsingTestCase() { + super(KeycloakSamlExtension.SUBSYSTEM_NAME, new KeycloakSamlExtension()); + } + + @Override + protected String getSubsystemXml() throws IOException { + return readResource("keycloak-saml-1.1.xml"); + } + + @Override + protected String getSubsystemXsdPath() throws Exception { + return "schema/wildfly-keycloak-saml_1_1.xsd"; + } + + @Override + protected String[] getSubsystemTemplatePaths() throws IOException { + return new String[]{ + "/subsystem-templates/keycloak-saml-adapter.xml" + }; + } +} diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1-err.xml b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1-err.xml new file mode 100644 index 0000000000..26d0e98dba --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1-err.xml @@ -0,0 +1,50 @@ + + + + + + + my_key.pem + my_key.pub + cert.cer + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1.xml b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1.xml new file mode 100644 index 0000000000..6f56fb057b --- /dev/null +++ b/saml/client-adapter/wildfly/wildfly-subsystem/src/test/resources/org/keycloak/subsystem/adapter/saml/extension/keycloak-saml-1.1.xml @@ -0,0 +1,50 @@ + + + + + + + my_key.pem + my_key.pub + cert.cer + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java deleted file mode 100755 index 6950fb6032..0000000000 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakAdapterConfigDeploymentProcessor.java +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ - -package org.keycloak.subsystem.adapter.saml.extension; - -import org.jboss.as.server.deployment.DeploymentPhaseContext; -import org.jboss.as.server.deployment.DeploymentUnit; -import org.jboss.as.server.deployment.DeploymentUnitProcessingException; -import org.jboss.as.server.deployment.DeploymentUnitProcessor; -import org.jboss.as.web.common.WarMetaData; -import org.jboss.logging.Logger; -import org.jboss.metadata.javaee.spec.ParamValueMetaData; -import org.jboss.metadata.web.jboss.JBossWebMetaData; -import org.jboss.metadata.web.spec.LoginConfigMetaData; - -import java.util.ArrayList; -import java.util.List; - -/** - * Pass authentication data (keycloak.json) as a servlet context param so it can be read by the KeycloakServletExtension. - * - * @author Stan Silvert ssilvert@redhat.com (C) 2014 Red Hat Inc. - */ -public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitProcessor { - protected Logger log = Logger.getLogger(KeycloakAdapterConfigDeploymentProcessor.class); - - @Override - public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException { - DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit(); - - String deploymentName = deploymentUnit.getName(); - } - - @Override - public void undeploy(DeploymentUnit du) { - - } - -} diff --git a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java b/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java deleted file mode 100755 index 46d089eb4b..0000000000 --- a/saml/client-adapter/wildfly/wildfly9-subsystem/src/main/java/org/keycloak/subsystem/adapter/saml/extension/KeycloakSubsystemParser.java +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2014 Red Hat Inc. and/or its affiliates and other contributors - * as indicated by the @author tags. All rights reserved. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not - * use this file except in compliance with the License. You may obtain a copy of - * the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the - * License for the specific language governing permissions and limitations under - * the License. - */ -package org.keycloak.subsystem.adapter.saml.extension; - -import org.jboss.as.controller.AttributeDefinition; -import org.jboss.as.controller.PathAddress; -import org.jboss.as.controller.PathElement; -import org.jboss.as.controller.SimpleAttributeDefinition; -import org.jboss.as.controller.descriptions.ModelDescriptionConstants; -import org.jboss.as.controller.operations.common.Util; -import org.jboss.as.controller.parsing.ParseUtils; -import org.jboss.as.controller.persistence.SubsystemMarshallingContext; -import org.jboss.dmr.ModelNode; -import org.jboss.dmr.Property; -import org.jboss.staxmapper.XMLElementReader; -import org.jboss.staxmapper.XMLElementWriter; -import org.jboss.staxmapper.XMLExtendedStreamReader; -import org.jboss.staxmapper.XMLExtendedStreamWriter; - -import javax.xml.stream.XMLStreamConstants; -import javax.xml.stream.XMLStreamException; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -/** - * The subsystem parser, which uses stax to read and write to and from xml - */ -class KeycloakSubsystemParser implements XMLStreamConstants, XMLElementReader>, XMLElementWriter { - - /** - * {@inheritDoc} - */ - @Override - public void readElement(final XMLExtendedStreamReader reader, final List list) throws XMLStreamException { - // Require no attributes - ParseUtils.requireNoAttributes(reader); - ModelNode addKeycloakSub = Util.createAddOperation(PathAddress.pathAddress(KeycloakSamlExtension.PATH_SUBSYSTEM)); - list.add(addKeycloakSub); - - while (reader.hasNext() && nextTag(reader) != END_ELEMENT) { - } - } - - // used for debugging - private int nextTag(XMLExtendedStreamReader reader) throws XMLStreamException { - return reader.nextTag(); - } - - - /** - * {@inheritDoc} - */ - @Override - public void writeContent(final XMLExtendedStreamWriter writer, final SubsystemMarshallingContext context) throws XMLStreamException { - context.startSubsystemElement(KeycloakSamlExtension.NAMESPACE, false); - writer.writeEndElement(); - } - - - - // code taken from org.jboss.as.controller.AttributeMarshaller - private void writeCharacters(XMLExtendedStreamWriter writer, String content) throws XMLStreamException { - if (content.indexOf('\n') > -1) { - // Multiline content. Use the overloaded variant that staxmapper will format - writer.writeCharacters(content); - } else { - // Staxmapper will just output the chars without adding newlines if this is used - char[] chars = content.toCharArray(); - writer.writeCharacters(chars, 0, chars.length); - } - } - -}