keycloak-aplcache
Changes
adapters/oidc/pom.xml 1(+1 -0)
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/SecurityInfoHelper.java 0(+0 -0)
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyAuthenticationMechanism.java 0(+0 -0)
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyKeycloakServletExtension.java 0(+0 -0)
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyRequestAuthenticator.java 0(+0 -0)
adapters/oidc/wildfly/wildfly-adapter/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension 0(+0 -0)
adapters/oidc/wildfly-elytron/pom.xml 99(+99 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronAccount.java 103(+103 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java 164(+164 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java 394(+394 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronRequestAuthenticator.java 86(+86 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronSessionTokenStore.java 202(+202 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronTokeStore.java 26(+26 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakConfigurationServletListener.java 109(+109 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanism.java 168(+168 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java 67(+67 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java 103(+103 -0)
adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/SecurityIdentityUtil.java 82(+82 -0)
adapters/oidc/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory 19(+19 -0)
adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java 7(+7 -0)
adapters/saml/pom.xml 1(+1 -0)
adapters/saml/wildfly-elytron/pom.xml 102(+102 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronHttpFacade.java 377(+377 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlAuthenticator.java 51(+51 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlEndpoint.java 39(+39 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlSessionStore.java 226(+226 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronTokeStore.java 24(+24 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java 121(+121 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanism.java 155(+155 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java 70(+70 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java 109(+109 -0)
adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/SecurityIdentityUtil.java 80(+80 -0)
adapters/saml/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory 19(+19 -0)
distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-elytron-oidc-adapter/main/module.xml 51(+51 -0)
distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-elytron-adapter/main/module.xml 52(+52 -0)
elytron/standalone-elytron.xml 558(+558 -0)
elytron/standalone-oauth2-sasl-with-elytron-only.xml 547(+547 -0)
elytron/wildfly-config.xml 71(+71 -0)
pom.xml 22(+22 -0)
Details
adapters/oidc/pom.xml 1(+1 -0)
diff --git a/adapters/oidc/pom.xml b/adapters/oidc/pom.xml
index 91a99a5..ef9e79e 100755
--- a/adapters/oidc/pom.xml
+++ b/adapters/oidc/pom.xml
@@ -45,5 +45,6 @@
<module>tomcat</module>
<module>undertow</module>
<module>wildfly</module>
+ <module>wildfly-elytron</module>
</modules>
</project>
diff --git a/adapters/oidc/wildfly/wildfly-adapter/pom.xml b/adapters/oidc/wildfly/wildfly-adapter/pom.xml
old mode 100755
new mode 100644
diff --git a/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/SecurityInfoHelper.java b/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/SecurityInfoHelper.java
old mode 100755
new mode 100644
diff --git a/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyAuthenticationMechanism.java b/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyAuthenticationMechanism.java
old mode 100755
new mode 100644
diff --git a/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyKeycloakServletExtension.java b/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyKeycloakServletExtension.java
old mode 100755
new mode 100644
diff --git a/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyRequestAuthenticator.java b/adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyRequestAuthenticator.java
old mode 100755
new mode 100644
diff --git a/adapters/oidc/wildfly/wildfly-adapter/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension b/adapters/oidc/wildfly/wildfly-adapter/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
old mode 100755
new mode 100644
adapters/oidc/wildfly-elytron/pom.xml 99(+99 -0)
diff --git a/adapters/oidc/wildfly-elytron/pom.xml b/adapters/oidc/wildfly-elytron/pom.xml
new file mode 100755
index 0000000..5aefb70
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/pom.xml
@@ -0,0 +1,99 @@
+<?xml version="1.0"?>
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2016 Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>3.1.0.CR1-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
+ <name>Keycloak Wildfly Elytron OIDC Adapter</name>
+ <description/>
+
+ <properties>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.wildfly.common</groupId>
+ <artifactId>wildfly-common</artifactId>
+ <version>1.2.0.Beta1</version>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly.security</groupId>
+ <artifactId>wildfly-elytron</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly.security.elytron-web</groupId>
+ <artifactId>undertow-server</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <version>${jboss.logging.version}</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.bouncycastle</groupId>
+ <artifactId>bcprov-jdk15on</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-core</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ </dependencies>
+</project>
\ No newline at end of file
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronAccount.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronAccount.java
new file mode 100644
index 0000000..c8db009
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronAccount.java
@@ -0,0 +1,103 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import org.jboss.logging.Logger;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.wildfly.security.auth.server.SecurityIdentity;
+
+import javax.security.auth.callback.CallbackHandler;
+import java.security.Principal;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronAccount implements OidcKeycloakAccount {
+
+ protected static Logger log = Logger.getLogger(ElytronAccount.class);
+
+ private final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal;
+
+ public ElytronAccount(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ this.principal = principal;
+ }
+
+ @Override
+ public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
+ return principal.getKeycloakSecurityContext();
+ }
+
+ @Override
+ public Principal getPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public Set<String> getRoles() {
+ Set<String> roles = new HashSet<>();
+
+ return roles;
+ }
+
+ void setCurrentRequestInfo(KeycloakDeployment deployment, AdapterTokenStore tokenStore) {
+ principal.getKeycloakSecurityContext().setCurrentRequestInfo(deployment, tokenStore);
+ }
+
+ public boolean checkActive() {
+ RefreshableKeycloakSecurityContext session = getKeycloakSecurityContext();
+
+ if (session.isActive() && !session.getDeployment().isAlwaysRefreshToken()) {
+ log.debug("session is active");
+ return true;
+ }
+
+ log.debug("session not active");
+
+ return false;
+ }
+
+ boolean tryRefresh(CallbackHandler callbackHandler) {
+ log.debug("Trying to refresh");
+
+ RefreshableKeycloakSecurityContext securityContext = getKeycloakSecurityContext();
+
+ if (securityContext == null) {
+ log.debug("No security context. Aborting refresh.");
+ }
+
+ if (securityContext.refreshExpiredToken(false)) {
+ SecurityIdentity securityIdentity = SecurityIdentityUtil.authorize(callbackHandler, principal);
+
+ if (securityIdentity != null) {
+ log.debug("refresh succeeded");
+ return true;
+ }
+
+ return false;
+ }
+
+ return checkActive();
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java
new file mode 100644
index 0000000..eda7d17
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronCookieTokenStore.java
@@ -0,0 +1,164 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import java.security.Principal;
+
+import org.jboss.logging.Logger;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.CookieTokenStore;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.Scope;
+
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronCookieTokenStore implements ElytronTokeStore {
+
+ protected static Logger log = Logger.getLogger(ElytronCookieTokenStore.class);
+
+ private final ElytronHttpFacade httpFacade;
+ private final CallbackHandler callbackHandler;
+
+ public ElytronCookieTokenStore(ElytronHttpFacade httpFacade, CallbackHandler callbackHandler) {
+ this.httpFacade = httpFacade;
+ this.callbackHandler = callbackHandler;
+ }
+
+ @Override
+ public void checkCurrentToken() {
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+ KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(deployment, httpFacade, this);
+
+ if (principal == null) {
+ return;
+ }
+
+ RefreshableKeycloakSecurityContext securityContext = principal.getKeycloakSecurityContext();
+
+ if (securityContext.isActive() && !securityContext.getDeployment().isAlwaysRefreshToken()) return;
+
+ // FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
+ // not be updated
+ boolean success = securityContext.refreshExpiredToken(false);
+ if (success && securityContext.isActive()) return;
+
+ saveAccountInfo(new ElytronAccount(principal));
+ }
+
+ @Override
+ public boolean isCached(RequestAuthenticator authenticator) {
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+ KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(deployment, httpFacade, this);
+ if (principal == null) {
+ log.debug("Account was not in cookie or was invalid, returning null");
+ return false;
+ }
+ ElytronAccount account = new ElytronAccount(principal);
+
+ if (!deployment.getRealm().equals(account.getKeycloakSecurityContext().getRealm())) {
+ log.debug("Account in session belongs to a different realm than for this request.");
+ return false;
+ }
+
+ boolean active = account.checkActive();
+
+ if (!active) {
+ active = account.tryRefresh(this.callbackHandler);
+ }
+
+ if (active) {
+ log.debug("Cached account found");
+ restoreRequest();
+ httpFacade.authenticationComplete(account, true);
+ return true;
+ } else {
+ log.debug("Account was not active, removing cookie and returning false");
+ CookieTokenStore.removeCookie(httpFacade);
+ return false;
+ }
+ }
+
+ @Override
+ public void saveAccountInfo(OidcKeycloakAccount account) {
+ RefreshableKeycloakSecurityContext secContext = (RefreshableKeycloakSecurityContext)account.getKeycloakSecurityContext();
+ CookieTokenStore.setTokenCookie(this.httpFacade.getDeployment(), this.httpFacade, secContext);
+ HttpScope exchange = this.httpFacade.getScope(Scope.EXCHANGE);
+
+ exchange.registerForNotification(httpServerScopes -> logout());
+
+ exchange.setAttachment(ElytronAccount.class.getName(), account);
+ exchange.setAttachment(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
+
+ restoreRequest();
+ }
+
+ @Override
+ public void logout() {
+ logout(false);
+ }
+
+ @Override
+ public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
+ CookieTokenStore.setTokenCookie(this.httpFacade.getDeployment(), httpFacade, securityContext);
+ }
+
+ @Override
+ public void saveRequest() {
+
+ }
+
+ @Override
+ public boolean restoreRequest() {
+ return false;
+ }
+
+ @Override
+ public void logout(boolean glo) {
+ KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = CookieTokenStore.getPrincipalFromCookie(this.httpFacade.getDeployment(), this.httpFacade, this);
+
+ if (principal == null) {
+ return;
+ }
+
+ CookieTokenStore.removeCookie(this.httpFacade);
+
+ if (glo) {
+ KeycloakSecurityContext ksc = (KeycloakSecurityContext) principal.getKeycloakSecurityContext();
+
+ if (ksc == null) {
+ return;
+ }
+
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+
+ if (!deployment.isBearerOnly() && ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
+ ((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
+ }
+ }
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java
new file mode 100644
index 0000000..bc2e903
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronHttpFacade.java
@@ -0,0 +1,394 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import org.bouncycastle.asn1.cmp.Challenge;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OIDCHttpFacade;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthenticationError;
+import org.keycloak.adapters.spi.LogoutError;
+import org.keycloak.enums.TokenStore;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.HttpServerCookie;
+import org.wildfly.security.http.HttpServerMechanismsResponder;
+import org.wildfly.security.http.HttpServerRequest;
+import org.wildfly.security.http.HttpServerResponse;
+import org.wildfly.security.http.Scope;
+
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.cert.X509Certificate;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class ElytronHttpFacade implements OIDCHttpFacade {
+
+ private final HttpServerRequest request;
+ private final CallbackHandler callbackHandler;
+ private final AdapterTokenStore tokenStore;
+ private final AdapterDeploymentContext deploymentContext;
+ private Consumer<HttpServerResponse> responseConsumer;
+ private ElytronAccount account;
+ private SecurityIdentity securityIdentity;
+ private boolean restored;
+
+ public ElytronHttpFacade(HttpServerRequest request, AdapterDeploymentContext deploymentContext, CallbackHandler handler) {
+ this.request = request;
+ this.deploymentContext = deploymentContext;
+ this.callbackHandler = handler;
+ this.tokenStore = createTokenStore();
+ this.responseConsumer = response -> {};
+ }
+
+ void authenticationComplete(ElytronAccount account, boolean storeToken) {
+ this.securityIdentity = SecurityIdentityUtil.authorize(this.callbackHandler, account.getPrincipal());
+
+ if (securityIdentity != null) {
+ this.account = account;
+ RefreshableKeycloakSecurityContext keycloakSecurityContext = account.getKeycloakSecurityContext();
+ account.setCurrentRequestInfo(keycloakSecurityContext.getDeployment(), this.tokenStore);
+ if (storeToken) {
+ this.tokenStore.saveAccountInfo(account);
+ }
+ }
+ }
+
+ void authenticationComplete() {
+ if (securityIdentity != null) {
+ this.request.authenticationComplete(response -> {
+ if (!restored) {
+ responseConsumer.accept(response);
+ }
+ }, () -> ((ElytronTokeStore) tokenStore).logout(true));
+ }
+ }
+
+ void authenticationFailed() {
+ this.request.authenticationFailed("Authentication Failed", response -> responseConsumer.accept(response));
+ }
+
+ void noAuthenticationInProgress() {
+ this.request.noAuthenticationInProgress();
+ }
+
+ void noAuthenticationInProgress(AuthChallenge challenge) {
+ if (challenge != null) {
+ challenge.challenge(this);
+ }
+ this.request.noAuthenticationInProgress(response -> responseConsumer.accept(response));
+ }
+
+ void authenticationInProgress() {
+ this.request.authenticationInProgress(response -> responseConsumer.accept(response));
+ }
+
+ HttpScope getScope(Scope scope) {
+ return request.getScope(scope);
+ }
+
+ HttpScope getScope(Scope scope, String id) {
+ return request.getScope(scope, id);
+ }
+
+ Collection<String> getScopeIds(Scope scope) {
+ return request.getScopeIds(scope);
+ }
+
+ AdapterTokenStore getTokenStore() {
+ return this.tokenStore;
+ }
+
+ KeycloakDeployment getDeployment() {
+ return deploymentContext.resolveDeployment(this);
+ }
+
+ private AdapterTokenStore createTokenStore() {
+ KeycloakDeployment deployment = getDeployment();
+
+ if (TokenStore.SESSION.equals(deployment.getTokenStore())) {
+ return new ElytronSessionTokenStore(this, this.callbackHandler);
+ } else {
+ return new ElytronCookieTokenStore(this, this.callbackHandler);
+ }
+ }
+
+ @Override
+ public Request getRequest() {
+ return new Request() {
+ @Override
+ public String getMethod() {
+ return request.getRequestMethod();
+ }
+
+ @Override
+ public String getURI() {
+ try {
+ return URLDecoder.decode(request.getRequestURI().toString(), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Failed to decode request URI", e);
+ }
+ }
+
+ @Override
+ public String getRelativePath() {
+ return request.getRequestPath();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return request.getRequestURI().getScheme().equals("https");
+ }
+
+ @Override
+ public String getFirstParam(String param) {
+ throw new RuntimeException("Not implemented.");
+ }
+
+ @Override
+ public String getQueryParamValue(String param) {
+ URI requestURI = request.getRequestURI();
+ String query = requestURI.getQuery();
+ if (query != null) {
+ String[] parameters = query.split("&");
+ for (String parameter : parameters) {
+ String[] keyValue = parameter.split("=");
+ if (keyValue[0].equals(param)) {
+ return keyValue[1];
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public Cookie getCookie(final String cookieName) {
+ List<HttpServerCookie> cookies = request.getCookies();
+
+ if (cookies != null) {
+ for (HttpServerCookie cookie : cookies) {
+ if (cookie.getName().equals(cookieName)) {
+ return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return request.getFirstRequestHeaderValue(name);
+ }
+
+ @Override
+ public List<String> getHeaders(String name) {
+ return request.getRequestHeaderValues(name);
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return request.getInputStream();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ InetSocketAddress sourceAddress = request.getSourceAddress();
+ if (sourceAddress == null) {
+ return "";
+ }
+ InetAddress address = sourceAddress.getAddress();
+ if (address == null) {
+ // this is unresolved, so we just return the host name not exactly spec, but if the name should be
+ // resolved then a PeerNameResolvingHandler should be used and this is probably better than just
+ // returning null
+ return sourceAddress.getHostString();
+ }
+ return address.getHostAddress();
+ }
+
+ @Override
+ public void setError(AuthenticationError error) {
+ request.getScope(Scope.EXCHANGE).setAttachment(AuthenticationError.class.getName(), error);
+ }
+
+ @Override
+ public void setError(LogoutError error) {
+ request.getScope(Scope.EXCHANGE).setAttachment(LogoutError.class.getName(), error);
+ }
+ };
+ }
+
+ @Override
+ public Response getResponse() {
+ return new Response() {
+ @Override
+ public void setStatus(final int status) {
+ responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
+ }
+
+ @Override
+ public void addHeader(final String name, final String value) {
+ responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ addHeader(name, value);
+ }
+
+ @Override
+ public void resetCookie(final String name, final String path) {
+ responseConsumer = responseConsumer.andThen(response -> setCookie(name, "", path, null, 0, false, false, response));
+ }
+
+ @Override
+ public void setCookie(final String name, final String value, final String path, final String domain, final int maxAge, final boolean secure, final boolean httpOnly) {
+ responseConsumer = responseConsumer.andThen(response -> setCookie(name, value, path, domain, maxAge, secure, httpOnly, response));
+ }
+
+ private void setCookie(final String name, final String value, final String path, final String domain, final int maxAge, final boolean secure, final boolean httpOnly, HttpServerResponse response) {
+ response.setResponseCookie(new HttpServerCookie() {
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getDomain() {
+ return domain;
+ }
+
+ @Override
+ public int getMaxAge() {
+ return maxAge;
+ }
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+ });
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
+ @Override
+ public void accept(HttpServerResponse httpServerResponse) {
+ try {
+ httpServerResponse.getOutputStream().write(stream.toByteArray());
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to write to response output stream", e);
+ }
+ }
+ });
+ return stream;
+ }
+
+ @Override
+ public void sendError(int code) {
+ setStatus(code);
+ }
+
+ @Override
+ public void sendError(final int code, final String message) {
+ responseConsumer = responseConsumer.andThen(response -> {
+ response.setStatusCode(code);
+ response.addResponseHeader("Content-Type", "text/html");
+ try {
+ response.getOutputStream().write(message.getBytes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void end() {
+
+ }
+ };
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain() {
+ return new X509Certificate[0];
+ }
+
+ @Override
+ public KeycloakSecurityContext getSecurityContext() {
+ if (account == null) {
+ return null;
+ }
+ return this.account.getKeycloakSecurityContext();
+ }
+
+ public boolean restoreRequest() {
+ restored = this.request.resumeRequest();
+ return restored;
+ }
+
+ public void suspendRequest() {
+ responseConsumer = responseConsumer.andThen(httpServerResponse -> request.suspendRequest());
+ }
+
+ public boolean isAuthorized() {
+ return this.securityIdentity != null;
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronRequestAuthenticator.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronRequestAuthenticator.java
new file mode 100644
index 0000000..643a716
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronRequestAuthenticator.java
@@ -0,0 +1,86 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.BearerTokenRequestAuthenticator;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OAuthRequestAuthenticator;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.Scope;
+
+import javax.security.auth.callback.CallbackHandler;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronRequestAuthenticator extends RequestAuthenticator {
+
+ public ElytronRequestAuthenticator(CallbackHandler callbackHandler, ElytronHttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort) {
+ super(facade, deployment, facade.getTokenStore(), sslRedirectPort);
+ }
+
+ @Override
+ public AuthOutcome authenticate() {
+ AuthOutcome authenticate = super.authenticate();
+
+ if (AuthOutcome.AUTHENTICATED.equals(authenticate)) {
+ if (!getElytronHttpFacade().isAuthorized()) {
+ return AuthOutcome.FAILED;
+ }
+ }
+
+ return authenticate;
+ }
+
+ @Override
+ protected OAuthRequestAuthenticator createOAuthAuthenticator() {
+ return new OAuthRequestAuthenticator(this, facade, deployment, sslRedirectPort, tokenStore);
+ }
+
+ @Override
+ protected void completeOAuthAuthentication(final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal) {
+ getElytronHttpFacade().authenticationComplete(new ElytronAccount(principal), true);
+ }
+
+ @Override
+ protected void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method) {
+ getElytronHttpFacade().authenticationComplete(new ElytronAccount(principal), false);
+ }
+
+ @Override
+ protected String changeHttpSessionId(boolean create) {
+ HttpScope session = getElytronHttpFacade().getScope(Scope.SESSION);
+
+ if (create) {
+ if (!session.exists()) {
+ session.create();
+ }
+ }
+
+ return session != null ? session.getID() : null;
+ }
+
+ private ElytronHttpFacade getElytronHttpFacade() {
+ return (ElytronHttpFacade) facade;
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronSessionTokenStore.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronSessionTokenStore.java
new file mode 100644
index 0000000..385a8a6
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronSessionTokenStore.java
@@ -0,0 +1,202 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import java.util.function.Consumer;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.jboss.logging.Logger;
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.KeycloakSecurityContext;
+import org.keycloak.adapters.AdapterTokenStore;
+import org.keycloak.adapters.AdapterUtils;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.OidcKeycloakAccount;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.HttpScopeNotification;
+import org.wildfly.security.http.Scope;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronSessionTokenStore implements ElytronTokeStore {
+
+ private static Logger log = Logger.getLogger(ElytronSessionTokenStore.class);
+
+ private final ElytronHttpFacade httpFacade;
+ private final CallbackHandler callbackHandler;
+
+ public ElytronSessionTokenStore(ElytronHttpFacade httpFacade, CallbackHandler callbackHandler) {
+ this.httpFacade = httpFacade;
+ this.callbackHandler = callbackHandler;
+ }
+
+ @Override
+ public void checkCurrentToken() {
+ HttpScope session = httpFacade.getScope(Scope.SESSION);
+ if (!session.exists()) return;
+ RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) session.getAttachment(KeycloakSecurityContext.class.getName());
+ if (securityContext == null) return;
+
+ // just in case session got serialized
+ if (securityContext.getDeployment() == null) securityContext.setCurrentRequestInfo(httpFacade.getDeployment(), this);
+
+ if (securityContext.isActive() && !securityContext.getDeployment().isAlwaysRefreshToken()) return;
+
+ // FYI: A refresh requires same scope, so same roles will be set. Otherwise, refresh will fail and token will
+ // not be updated
+ boolean success = securityContext.refreshExpiredToken(false);
+ if (success && securityContext.isActive()) return;
+
+ // Refresh failed, so user is already logged out from keycloak. Cleanup and expire our session
+ session.setAttachment(KeycloakSecurityContext.class.getName(), null);
+ session.invalidate();
+ }
+
+ @Override
+ public boolean isCached(RequestAuthenticator authenticator) {
+ HttpScope session = this.httpFacade.getScope(Scope.SESSION);
+
+ if (session == null) {
+ log.debug("session was null, returning null");
+ return false;
+ }
+
+ ElytronAccount account;
+
+ try {
+ account = (ElytronAccount) session.getAttachment(ElytronAccount.class.getName());
+ } catch (IllegalStateException e) {
+ log.debug("session was invalidated. Return false.");
+ return false;
+ }
+ if (account == null) {
+ log.debug("Account was not in session, returning null");
+ return false;
+ }
+
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+
+ if (!deployment.getRealm().equals(account.getKeycloakSecurityContext().getRealm())) {
+ log.debug("Account in session belongs to a different realm than for this request.");
+ return false;
+ }
+
+ boolean active = account.checkActive();
+
+ if (!active) {
+ active = account.tryRefresh(this.callbackHandler);
+ }
+
+ if (active) {
+ log.debug("Cached account found");
+ restoreRequest();
+ httpFacade.authenticationComplete(account, true);
+ return true;
+ } else {
+ log.debug("Refresh failed. Account was not active. Returning null and invalidating Http session");
+ try {
+ session.setAttachment(KeycloakSecurityContext.class.getName(), null);
+ session.setAttachment(ElytronAccount.class.getName(), null);
+ session.invalidate();
+ } catch (Exception e) {
+ log.debug("Failed to invalidate session, might already be invalidated");
+ }
+ return false;
+ }
+ }
+
+ @Override
+ public void saveAccountInfo(OidcKeycloakAccount account) {
+ HttpScope session = this.httpFacade.getScope(Scope.SESSION);
+
+ if (!session.exists()) {
+ session.create();
+ }
+
+ session.setAttachment(ElytronAccount.class.getName(), account);
+ session.setAttachment(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
+
+ session.registerForNotification(httpScopeNotification -> {
+ if (!httpScopeNotification.isOfType(HttpScopeNotification.SessionNotificationType.UNDEPLOY)) {
+ logout();
+ }
+ });
+
+ HttpScope scope = this.httpFacade.getScope(Scope.EXCHANGE);
+
+ scope.setAttachment(KeycloakSecurityContext.class.getName(), account.getKeycloakSecurityContext());
+ }
+
+ @Override
+ public void logout() {
+ logout(false);
+ }
+
+ @Override
+ public void refreshCallback(RefreshableKeycloakSecurityContext securityContext) {
+ KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(this.httpFacade.getDeployment(), securityContext.getToken()), securityContext);
+ saveAccountInfo(new ElytronAccount(principal));
+ }
+
+ @Override
+ public void saveRequest() {
+ this.httpFacade.suspendRequest();
+ }
+
+ @Override
+ public boolean restoreRequest() {
+ return this.httpFacade.restoreRequest();
+ }
+
+ @Override
+ public void logout(boolean glo) {
+ HttpScope session = this.httpFacade.getScope(Scope.SESSION);
+
+ if (!session.exists()) {
+ return;
+ }
+
+ try {
+ if (glo) {
+ KeycloakSecurityContext ksc = (KeycloakSecurityContext) session.getAttachment(KeycloakSecurityContext.class.getName());
+
+ if (ksc == null) {
+ return;
+ }
+
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+
+ if (!deployment.isBearerOnly() && ksc != null && ksc instanceof RefreshableKeycloakSecurityContext) {
+ ((RefreshableKeycloakSecurityContext) ksc).logout(deployment);
+ }
+ }
+
+ session.setAttachment(KeycloakSecurityContext.class.getName(), null);
+ session.setAttachment(ElytronAccount.class.getName(), null);
+ session.invalidate();
+ } catch (IllegalStateException ise) {
+ // Session may be already logged-out in case that app has adminUrl
+ log.debugf("Session %s logged-out already", session.getID());
+ }
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronTokeStore.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronTokeStore.java
new file mode 100644
index 0000000..dc1486e
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/ElytronTokeStore.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.elytron;
+
+import org.keycloak.adapters.AdapterTokenStore;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ElytronTokeStore extends AdapterTokenStore {
+ void logout(boolean glo);
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakConfigurationServletListener.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakConfigurationServletListener.java
new file mode 100644
index 0000000..ad8e9d5
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakConfigurationServletListener.java
@@ -0,0 +1,109 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.KeycloakDeploymentBuilder;
+import org.keycloak.constants.AdapterConstants;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+/**
+ * <p>A {@link ServletContextListener} that parses the keycloak adapter configuration and set the same configuration
+ * as a {@link ServletContext} attribute in order to provide to {@link KeycloakHttpServerAuthenticationMechanism} a way
+ * to obtain the configuration when processing requests.
+ *
+ * <p>This listener should be automatically registered to a deployment using the subsystem.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakConfigurationServletListener implements ServletContextListener {
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext servletContext = sce.getServletContext();
+ String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
+ KeycloakConfigResolver configResolver;
+ AdapterDeploymentContext deploymentContext;
+
+ if (configResolverClass != null) {
+ try {
+ configResolver = (KeycloakConfigResolver) servletContext.getClassLoader().loadClass(configResolverClass).newInstance();
+ deploymentContext = new AdapterDeploymentContext(configResolver);
+ } catch (Exception ex) {
+ deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ InputStream is = getConfigInputStream(servletContext);
+
+ KeycloakDeployment deployment;
+
+ if (is == null) {
+ deployment = new KeycloakDeployment();
+ } else {
+ deployment = KeycloakDeploymentBuilder.build(is);
+ }
+
+ deploymentContext = new AdapterDeploymentContext(deployment);
+ }
+
+ servletContext.setAttribute(AdapterDeploymentContext.class.getName(), deploymentContext);
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+
+ }
+
+ private InputStream getConfigInputStream(ServletContext servletContext) {
+ InputStream is = getJSONFromServletContext(servletContext);
+
+ if (is == null) {
+ String path = servletContext.getInitParameter("keycloak.config.file");
+
+ if (path == null) {
+ is = servletContext.getResourceAsStream("/WEB-INF/keycloak.json");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return is;
+ }
+
+ private InputStream getJSONFromServletContext(ServletContext servletContext) {
+ String json = servletContext.getInitParameter(AdapterConstants.AUTH_DATA_PARAM_NAME);
+
+ if (json == null) {
+ return null;
+ }
+
+ return new ByteArrayInputStream(json.getBytes());
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanism.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanism.java
new file mode 100644
index 0000000..3fcf9bf
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanism.java
@@ -0,0 +1,168 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.keycloak.adapters.AuthenticatedActionsHandler;
+import org.keycloak.adapters.KeycloakDeployment;
+import org.keycloak.adapters.NodesRegistrationManagement;
+import org.keycloak.adapters.PreAuthActionsHandler;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.keycloak.adapters.RequestAuthenticator;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.UserSessionManagement;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerRequest;
+import org.wildfly.security.http.Scope;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticationMechanism {
+
+ static Logger LOGGER = Logger.getLogger(KeycloakHttpServerAuthenticationMechanismFactory.class);
+ static final String NAME = "KEYCLOAK";
+
+ private final Map<String, ?> properties;
+ private final CallbackHandler callbackHandler;
+ private final AdapterDeploymentContext deploymentContext;
+
+ public KeycloakHttpServerAuthenticationMechanism(Map<String, ?> properties, CallbackHandler callbackHandler, AdapterDeploymentContext deploymentContext) {
+ this.properties = properties;
+ this.callbackHandler = callbackHandler;
+ this.deploymentContext = deploymentContext;
+ }
+
+ @Override
+ public String getMechanismName() {
+ return NAME;
+ }
+
+ @Override
+ public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
+ LOGGER.debugf("Evaluating request for path [%s]", request.getRequestURI());
+ AdapterDeploymentContext deploymentContext = getDeploymentContext(request);
+
+ if (deploymentContext == null) {
+ LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI());
+ request.noAuthenticationInProgress();
+ return;
+ }
+
+ ElytronHttpFacade httpFacade = new ElytronHttpFacade(request, deploymentContext, callbackHandler);
+ KeycloakDeployment deployment = httpFacade.getDeployment();
+
+ if (!deployment.isConfigured()) {
+ request.noAuthenticationInProgress();
+ return;
+ }
+
+ RequestAuthenticator authenticator = createRequestAuthenticator(request, httpFacade, deployment);
+
+ httpFacade.getTokenStore().checkCurrentToken();
+
+ if (preActions(httpFacade, deploymentContext)) {
+ LOGGER.debugf("Pre-actions has aborted the evaluation of [%s]", request.getRequestURI());
+ httpFacade.authenticationInProgress();
+ return;
+ }
+
+ AuthOutcome outcome = authenticator.authenticate();
+
+ if (AuthOutcome.AUTHENTICATED.equals(outcome)) {
+ if (new AuthenticatedActionsHandler(deployment, httpFacade).handledRequest()) {
+ httpFacade.authenticationInProgress();
+ } else {
+ httpFacade.authenticationComplete();
+ }
+ return;
+ }
+
+ AuthChallenge challenge = authenticator.getChallenge();
+
+ if (challenge != null) {
+ httpFacade.noAuthenticationInProgress(challenge);
+ return;
+ }
+
+ if (AuthOutcome.FAILED.equals(outcome)) {
+ httpFacade.getResponse().setStatus(403);
+ httpFacade.authenticationFailed();
+ return;
+ }
+
+ httpFacade.noAuthenticationInProgress();
+ }
+
+ private ElytronRequestAuthenticator createRequestAuthenticator(HttpServerRequest request, ElytronHttpFacade httpFacade, KeycloakDeployment deployment) {
+ return new ElytronRequestAuthenticator(this.callbackHandler, httpFacade, deployment, getConfidentialPort(request));
+ }
+
+ private AdapterDeploymentContext getDeploymentContext(HttpServerRequest request) {
+ if (this.deploymentContext == null) {
+ return (AdapterDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(AdapterDeploymentContext.class.getName());
+ }
+
+ return this.deploymentContext;
+ }
+
+ private boolean preActions(ElytronHttpFacade httpFacade, AdapterDeploymentContext deploymentContext) {
+ NodesRegistrationManagement nodesRegistrationManagement = new NodesRegistrationManagement();
+
+ nodesRegistrationManagement.tryRegister(httpFacade.getDeployment());
+
+ PreAuthActionsHandler preActions = new PreAuthActionsHandler(new UserSessionManagement() {
+ @Override
+ public void logoutAll() {
+ Collection<String> sessions = httpFacade.getScopeIds(Scope.SESSION);
+ logoutHttpSessions(new ArrayList<>(sessions));
+ }
+
+ @Override
+ public void logoutHttpSessions(List<String> ids) {
+ for (String id : ids) {
+ HttpScope session = httpFacade.getScope(Scope.SESSION, id);
+
+ if (session != null) {
+ session.invalidate();
+ }
+ }
+
+ }
+ }, deploymentContext, httpFacade);
+
+ return preActions.handleRequest();
+ }
+
+ // TODO: obtain confidential port from Elytron
+ private int getConfidentialPort(HttpServerRequest request) {
+ return 8443;
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java
new file mode 100644
index 0000000..eb6b333
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java
@@ -0,0 +1,67 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import org.keycloak.adapters.AdapterDeploymentContext;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+
+import javax.security.auth.callback.CallbackHandler;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakHttpServerAuthenticationMechanismFactory implements HttpServerAuthenticationMechanismFactory {
+
+ private final AdapterDeploymentContext deploymentContext;
+
+ /**
+ * <p>Creates a new instance.
+ *
+ * <p>A default constructor is necessary in order to allow this factory to be loaded via {@link java.util.ServiceLoader}.
+ */
+ public KeycloakHttpServerAuthenticationMechanismFactory() {
+ this(null);
+ }
+
+ public KeycloakHttpServerAuthenticationMechanismFactory(AdapterDeploymentContext deploymentContext) {
+ this.deploymentContext = deploymentContext;
+ }
+
+ @Override
+ public String[] getMechanismNames(Map<String, ?> properties) {
+ return new String[] {KeycloakHttpServerAuthenticationMechanism.NAME};
+ }
+
+ @Override
+ public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map<String, ?> properties, CallbackHandler callbackHandler) throws HttpAuthenticationException {
+ Map<String, Object> mechanismProperties = new HashMap();
+
+ mechanismProperties.putAll(properties);
+
+ if (KeycloakHttpServerAuthenticationMechanism.NAME.equals(mechanismName)) {
+ return new KeycloakHttpServerAuthenticationMechanism(properties, callbackHandler, this.deploymentContext);
+ }
+
+ return null;
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java
new file mode 100644
index 0000000..6042ec8
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/KeycloakSecurityRealm.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.elytron;
+
+import java.security.Principal;
+import java.util.Set;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.AdapterUtils;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.wildfly.security.auth.SupportLevel;
+import org.wildfly.security.auth.server.RealmIdentity;
+import org.wildfly.security.auth.server.RealmUnavailableException;
+import org.wildfly.security.auth.server.SecurityRealm;
+import org.wildfly.security.authz.Attributes;
+import org.wildfly.security.authz.AuthorizationIdentity;
+import org.wildfly.security.authz.MapAttributes;
+import org.wildfly.security.authz.RoleDecoder;
+import org.wildfly.security.credential.Credential;
+import org.wildfly.security.evidence.Evidence;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakSecurityRealm implements SecurityRealm {
+
+ @Override
+ public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException {
+ if (principal instanceof KeycloakPrincipal) {
+ return createRealmIdentity((KeycloakPrincipal) principal);
+ }
+ return RealmIdentity.NON_EXISTENT;
+ }
+
+ private RealmIdentity createRealmIdentity(KeycloakPrincipal principal) {
+ return new RealmIdentity() {
+ @Override
+ public Principal getRealmIdentityPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ @Override
+ public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException {
+ return null;
+ }
+
+ @Override
+ public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.SUPPORTED;
+ }
+
+ @Override
+ public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
+ return principal != null;
+ }
+
+ @Override
+ public boolean exists() throws RealmUnavailableException {
+ return principal != null;
+ }
+
+ @Override
+ public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException {
+ RefreshableKeycloakSecurityContext securityContext = (RefreshableKeycloakSecurityContext) principal.getKeycloakSecurityContext();
+ Attributes attributes = new MapAttributes();
+ Set<String> roles = AdapterUtils.getRolesFromSecurityContext(securityContext);
+
+ attributes.addAll(RoleDecoder.KEY_ROLES, roles);
+
+ return AuthorizationIdentity.basicIdentity(attributes);
+ }
+ };
+ }
+
+ @Override
+ public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ @Override
+ public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.POSSIBLY_SUPPORTED;
+ }
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/SecurityIdentityUtil.java b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/SecurityIdentityUtil.java
new file mode 100644
index 0000000..28f6eb9
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/java/org/keycloak/adapters/elytron/SecurityIdentityUtil.java
@@ -0,0 +1,82 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2016 Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.elytron;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+
+import org.keycloak.KeycloakPrincipal;
+import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
+import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
+import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
+import org.wildfly.security.auth.callback.SecurityIdentityCallback;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.evidence.Evidence;
+import org.wildfly.security.http.HttpAuthenticationException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+final class SecurityIdentityUtil {
+
+ static final SecurityIdentity authorize(CallbackHandler callbackHandler, Principal principal) {
+ try {
+ EvidenceVerifyCallback evidenceVerifyCallback = new EvidenceVerifyCallback(new Evidence() {
+ @Override
+ public Principal getPrincipal() {
+ return principal;
+ }
+ });
+
+ callbackHandler.handle(new Callback[]{evidenceVerifyCallback});
+
+ if (evidenceVerifyCallback.isVerified()) {
+ AuthorizeCallback authorizeCallback = new AuthorizeCallback(null, null);
+
+ try {
+ callbackHandler.handle(new Callback[] {authorizeCallback});
+
+ authorizeCallback.isAuthorized();
+ } catch (Exception e) {
+ throw new HttpAuthenticationException(e);
+ }
+
+ SecurityIdentityCallback securityIdentityCallback = new SecurityIdentityCallback();
+
+ callbackHandler.handle(new Callback[]{AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback});
+
+ SecurityIdentity securityIdentity = securityIdentityCallback.getSecurityIdentity();
+
+ return securityIdentity;
+ }
+ } catch (UnsupportedCallbackException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/adapters/oidc/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory b/adapters/oidc/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory
new file mode 100644
index 0000000..96a0441
--- /dev/null
+++ b/adapters/oidc/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual contributors
+# as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.adapters.elytron.KeycloakHttpServerAuthenticationMechanismFactory
\ No newline at end of file
diff --git a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
index cb9b4d9..550eeeb 100644
--- a/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
+++ b/adapters/saml/core/src/main/java/org/keycloak/adapters/saml/profile/AbstractSamlAuthenticationHandler.java
@@ -17,6 +17,8 @@
package org.keycloak.adapters.saml.profile;
+import static org.keycloak.adapters.saml.SamlPrincipal.DEFAULT_ROLE_ATTRIBUTE_NAME;
+
import org.jboss.logging.Logger;
import org.keycloak.adapters.saml.AbstractInitiateLogin;
import org.keycloak.adapters.saml.OnSessionCreated;
@@ -422,6 +424,11 @@ public abstract class AbstractSamlAuthenticationHandler implements SamlAuthentic
}
}
}
+
+ // roles should also be there as regular attributes
+ // this mainly required for elytron and its ABAC nature
+ attributes.put(DEFAULT_ROLE_ATTRIBUTE_NAME, new ArrayList<>(roles));
+
if (deployment.getPrincipalNamePolicy() == SamlDeployment.PrincipalNamePolicy.FROM_ATTRIBUTE) {
if (deployment.getPrincipalAttributeName() != null) {
String attribute = attributes.getFirst(deployment.getPrincipalAttributeName());
diff --git a/adapters/saml/core-public/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java b/adapters/saml/core-public/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
index f9a1e77..280c3fe 100755
--- a/adapters/saml/core-public/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
+++ b/adapters/saml/core-public/src/main/java/org/keycloak/adapters/saml/SamlPrincipal.java
@@ -24,6 +24,7 @@ import java.io.Serializable;
import java.security.Principal;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
import java.util.Set;
/**
@@ -31,6 +32,9 @@ import java.util.Set;
* @version $Revision: 1 $
*/
public class SamlPrincipal implements Serializable, Principal {
+
+ public static final String DEFAULT_ROLE_ATTRIBUTE_NAME = "Roles";
+
private MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
private MultivaluedHashMap<String, String> friendlyAttributes = new MultivaluedHashMap<>();
private String name;
@@ -99,6 +103,15 @@ public class SamlPrincipal implements Serializable, Principal {
}
/**
+ * Convenience function that gets the attributes associated with this principal
+ *
+ * @return attributes associated with this principal
+ */
+ public Map<String, List<String>> getAttributes() {
+ return Collections.unmodifiableMap(attributes);
+ }
+
+ /**
* Convenience function that gets Attribute value by attribute friendly name
*
* @param friendlyName
adapters/saml/pom.xml 1(+1 -0)
diff --git a/adapters/saml/pom.xml b/adapters/saml/pom.xml
index 614646d..7ca4c17 100755
--- a/adapters/saml/pom.xml
+++ b/adapters/saml/pom.xml
@@ -39,5 +39,6 @@
<module>wildfly</module>
<module>as7-eap6</module>
<module>servlet-filter</module>
+ <module>wildfly-elytron</module>
</modules>
</project>
adapters/saml/wildfly-elytron/pom.xml 102(+102 -0)
diff --git a/adapters/saml/wildfly-elytron/pom.xml b/adapters/saml/wildfly-elytron/pom.xml
new file mode 100755
index 0000000..51af380
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/pom.xml
@@ -0,0 +1,102 @@
+<?xml version="1.0"?>
+<!--
+ ~ Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ and other contributors as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+ <parent>
+ <artifactId>keycloak-parent</artifactId>
+ <groupId>org.keycloak</groupId>
+ <version>3.1.0.CR1-SNAPSHOT</version>
+ <relativePath>../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
+ <name>Keycloak WildFly Elytron SAML Adapter</name>
+ <description/>
+
+ <properties>
+ <maven.compiler.target>1.8</maven.compiler.target>
+ <maven.compiler.source>1.8</maven.compiler.source>
+ </properties>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-adapter-spi</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-common</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-api-public</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-adapter-core</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.logging</groupId>
+ <artifactId>jboss-logging</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.jboss.spec.javax.servlet</groupId>
+ <artifactId>jboss-servlet-api_3.0_spec</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly.security</groupId>
+ <artifactId>wildfly-elytron</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <scope>test</scope>
+ </dependency>
+ </dependencies>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <configuration>
+ <source>${maven.compiler.source}</source>
+ <target>${maven.compiler.target}</target>
+ </configuration>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronHttpFacade.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronHttpFacade.java
new file mode 100644
index 0000000..88e96f8
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronHttpFacade.java
@@ -0,0 +1,377 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.InetAddress;
+import java.net.InetSocketAddress;
+import java.net.URI;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Consumer;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.cert.X509Certificate;
+
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthenticationError;
+import org.keycloak.adapters.spi.HttpFacade;
+import org.keycloak.adapters.spi.LogoutError;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback;
+import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
+import org.wildfly.security.auth.callback.SecurityIdentityCallback;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.HttpServerCookie;
+import org.wildfly.security.http.HttpServerMechanismsResponder;
+import org.wildfly.security.http.HttpServerRequest;
+import org.wildfly.security.http.HttpServerResponse;
+import org.wildfly.security.http.Scope;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class ElytronHttpFacade implements HttpFacade {
+
+ private final HttpServerRequest request;
+ private final CallbackHandler callbackHandler;
+ private final SamlDeploymentContext deploymentContext;
+ private final SamlSessionStore sessionStore;
+ private Consumer<HttpServerResponse> responseConsumer;
+ private SecurityIdentity securityIdentity;
+ private boolean restored;
+ private SamlSession samlSession;
+
+ public ElytronHttpFacade(HttpServerRequest request, SessionIdMapper idMapper, SamlDeploymentContext deploymentContext, CallbackHandler handler) {
+ this.request = request;
+ this.deploymentContext = deploymentContext;
+ this.callbackHandler = handler;
+ this.responseConsumer = response -> {};
+ this.sessionStore = createTokenStore(idMapper);
+ }
+
+ private SamlSessionStore createTokenStore(SessionIdMapper idMapper) {
+ return new ElytronSamlSessionStore(this, idMapper, getDeployment());
+ }
+
+ void authenticationComplete(SamlSession samlSession) {
+ this.samlSession = samlSession;
+ }
+
+ void authenticationComplete() {
+ this.securityIdentity = SecurityIdentityUtil.authorize(this.callbackHandler, samlSession.getPrincipal());
+ this.request.authenticationComplete(response -> {
+ if (!restored) {
+ responseConsumer.accept(response);
+ }
+ }, () -> ((ElytronTokeStore) sessionStore).logout(true));
+ }
+
+ void authenticationCompleteAnonymous() {
+ try {
+ AnonymousAuthorizationCallback anonymousAuthorizationCallback = new AnonymousAuthorizationCallback(null);
+
+ callbackHandler.handle(new Callback[]{anonymousAuthorizationCallback});
+
+ if (anonymousAuthorizationCallback.isAuthorized()) {
+ callbackHandler.handle(new Callback[]{AuthenticationCompleteCallback.SUCCEEDED, new SecurityIdentityCallback()});
+ }
+
+ request.authenticationComplete(response -> response.forward(getRequest().getRelativePath()));
+ } catch (Exception e) {
+ throw new RuntimeException("Unexpected error processing callbacks during logout.", e);
+ }
+ }
+
+ void authenticationFailed() {
+ this.request.authenticationFailed("Authentication Failed", response -> responseConsumer.accept(response));
+ }
+
+ void noAuthenticationInProgress(AuthChallenge challenge) {
+ if (challenge != null) {
+ challenge.challenge(this);
+ }
+ this.request.noAuthenticationInProgress(response -> responseConsumer.accept(response));
+ }
+
+ void authenticationInProgress() {
+ this.request.authenticationInProgress(response -> responseConsumer.accept(response));
+ }
+
+ HttpScope getScope(Scope scope) {
+ return request.getScope(scope);
+ }
+
+ HttpScope getScope(Scope scope, String id) {
+ return request.getScope(scope, id);
+ }
+
+ Collection<String> getScopeIds(Scope scope) {
+ return request.getScopeIds(scope);
+ }
+
+ SamlDeployment getDeployment() {
+ return deploymentContext.resolveDeployment(this);
+ }
+
+ @Override
+ public Request getRequest() {
+ return new Request() {
+ @Override
+ public String getMethod() {
+ return request.getRequestMethod();
+ }
+
+ @Override
+ public String getURI() {
+ try {
+ return URLDecoder.decode(request.getRequestURI().toString(), "UTF-8");
+ } catch (UnsupportedEncodingException e) {
+ throw new RuntimeException("Failed to decode request URI", e);
+ }
+ }
+
+ @Override
+ public String getRelativePath() {
+ return request.getRequestPath();
+ }
+
+ @Override
+ public boolean isSecure() {
+ return request.getRequestURI().getScheme().equals("https");
+ }
+
+ @Override
+ public String getFirstParam(String param) {
+ return request.getFirstParameterValue(param);
+ }
+
+ @Override
+ public String getQueryParamValue(String param) {
+ return request.getFirstParameterValue(param);
+ }
+
+ @Override
+ public Cookie getCookie(final String cookieName) {
+ List<HttpServerCookie> cookies = request.getCookies();
+
+ if (cookies != null) {
+ for (HttpServerCookie cookie : cookies) {
+ if (cookie.getName().equals(cookieName)) {
+ return new Cookie(cookie.getName(), cookie.getValue(), cookie.getVersion(), cookie.getDomain(), cookie.getPath());
+ }
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public String getHeader(String name) {
+ return request.getFirstRequestHeaderValue(name);
+ }
+
+ @Override
+ public List<String> getHeaders(String name) {
+ return request.getRequestHeaderValues(name);
+ }
+
+ @Override
+ public InputStream getInputStream() {
+ return request.getInputStream();
+ }
+
+ @Override
+ public String getRemoteAddr() {
+ InetSocketAddress sourceAddress = request.getSourceAddress();
+ if (sourceAddress == null) {
+ return "";
+ }
+ InetAddress address = sourceAddress.getAddress();
+ if (address == null) {
+ // this is unresolved, so we just return the host name not exactly spec, but if the name should be
+ // resolved then a PeerNameResolvingHandler should be used and this is probably better than just
+ // returning null
+ return sourceAddress.getHostString();
+ }
+ return address.getHostAddress();
+ }
+
+ @Override
+ public void setError(AuthenticationError error) {
+ request.getScope(Scope.EXCHANGE).setAttachment(AuthenticationError.class.getName(), error);
+ }
+
+ @Override
+ public void setError(LogoutError error) {
+ request.getScope(Scope.EXCHANGE).setAttachment(LogoutError.class.getName(), error);
+ }
+ };
+ }
+
+ @Override
+ public Response getResponse() {
+ return new Response() {
+ @Override
+ public void setStatus(final int status) {
+ responseConsumer = responseConsumer.andThen(response -> response.setStatusCode(status));
+ }
+
+ @Override
+ public void addHeader(final String name, final String value) {
+ responseConsumer = responseConsumer.andThen(response -> response.addResponseHeader(name, value));
+ }
+
+ @Override
+ public void setHeader(String name, String value) {
+ addHeader(name, value);
+ }
+
+ @Override
+ public void resetCookie(final String name, final String path) {
+ responseConsumer = responseConsumer.andThen(response -> setCookie(name, "", path, null, 0, false, false, response));
+ }
+
+ @Override
+ public void setCookie(final String name, final String value, final String path, final String domain, final int maxAge, final boolean secure, final boolean httpOnly) {
+ responseConsumer = responseConsumer.andThen(response -> setCookie(name, value, path, domain, maxAge, secure, httpOnly, response));
+ }
+
+ private void setCookie(final String name, final String value, final String path, final String domain, final int maxAge, final boolean secure, final boolean httpOnly, HttpServerResponse response) {
+ response.setResponseCookie(new HttpServerCookie() {
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public String getDomain() {
+ return domain;
+ }
+
+ @Override
+ public int getMaxAge() {
+ return maxAge;
+ }
+
+ @Override
+ public String getPath() {
+ return path;
+ }
+
+ @Override
+ public boolean isSecure() {
+ return secure;
+ }
+
+ @Override
+ public int getVersion() {
+ return 0;
+ }
+
+ @Override
+ public boolean isHttpOnly() {
+ return httpOnly;
+ }
+ });
+ }
+
+ @Override
+ public OutputStream getOutputStream() {
+ ByteArrayOutputStream stream = new ByteArrayOutputStream();
+ responseConsumer = responseConsumer.andThen(new Consumer<HttpServerResponse>() {
+ @Override
+ public void accept(HttpServerResponse httpServerResponse) {
+ try {
+ httpServerResponse.getOutputStream().write(stream.toByteArray());
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to write to response output stream", e);
+ }
+ }
+ });
+ return stream;
+ }
+
+ @Override
+ public void sendError(int code) {
+ setStatus(code);
+ }
+
+ @Override
+ public void sendError(final int code, final String message) {
+ responseConsumer = responseConsumer.andThen(response -> {
+ response.setStatusCode(code);
+ response.addResponseHeader("Content-Type", "text/html");
+ try {
+ response.getOutputStream().write(message.getBytes());
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ });
+ }
+
+ @Override
+ public void end() {
+
+ }
+ };
+ }
+
+ @Override
+ public X509Certificate[] getCertificateChain() {
+ return new X509Certificate[0];
+ }
+
+ public boolean restoreRequest() {
+ restored = this.request.resumeRequest();
+ return restored;
+ }
+
+ public void suspendRequest() {
+ responseConsumer = responseConsumer.andThen(httpServerResponse -> request.suspendRequest());
+ }
+
+ public boolean isAuthorized() {
+ return this.securityIdentity != null;
+ }
+
+ public URI getURI() {
+ return request.getRequestURI();
+ }
+
+ public SamlSessionStore getSessionStore() {
+ return sessionStore;
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlAuthenticator.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlAuthenticator.java
new file mode 100644
index 0000000..29975ed
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlAuthenticator.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.saml.elytron;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.saml.profile.SamlAuthenticationHandler;
+import org.keycloak.adapters.saml.profile.webbrowsersso.BrowserHandler;
+import org.keycloak.adapters.spi.HttpFacade;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronSamlAuthenticator extends SamlAuthenticator {
+ private final CallbackHandler callbackHandler;
+ private final ElytronHttpFacade facade;
+
+ public ElytronSamlAuthenticator(ElytronHttpFacade facade, SamlDeployment samlDeployment, CallbackHandler callbackHandler) {
+ super(facade, samlDeployment, facade.getSessionStore());
+ this.callbackHandler = callbackHandler;
+ this.facade = facade;
+ }
+
+ @Override
+ protected void completeAuthentication(SamlSession samlSession) {
+ facade.authenticationComplete(samlSession);
+ }
+
+ @Override
+ protected SamlAuthenticationHandler createBrowserHandler(HttpFacade facade, SamlDeployment deployment, SamlSessionStore sessionStore) {
+ return new BrowserHandler(facade, deployment, sessionStore);
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlEndpoint.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlEndpoint.java
new file mode 100644
index 0000000..17997e5
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlEndpoint.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.saml.elytron;
+
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSession;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class ElytronSamlEndpoint extends SamlAuthenticator {
+
+ private final ElytronHttpFacade facade;
+
+ public ElytronSamlEndpoint(ElytronHttpFacade facade, SamlDeployment samlDeployment) {
+ super(facade, samlDeployment, facade.getSessionStore());
+ this.facade = facade;
+ }
+
+ @Override
+ protected void completeAuthentication(SamlSession samlSession) {
+ facade.authenticationComplete(samlSession);
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlSessionStore.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlSessionStore.java
new file mode 100644
index 0000000..2ce6292
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronSamlSessionStore.java
@@ -0,0 +1,226 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.net.URI;
+import java.security.Principal;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Set;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpSession;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlSession;
+import org.keycloak.adapters.saml.SamlSessionStore;
+import org.keycloak.adapters.saml.SamlUtil;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.keycloak.common.util.KeycloakUriBuilder;
+import org.wildfly.security.http.HttpScope;
+import org.wildfly.security.http.Scope;
+
+/**
+ * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
+ * @version $Revision: 1 $
+ */
+public class ElytronSamlSessionStore implements SamlSessionStore, ElytronTokeStore {
+ protected static Logger log = Logger.getLogger(SamlSessionStore.class);
+ public static final String SAML_REDIRECT_URI = "SAML_REDIRECT_URI";
+
+ private final SessionIdMapper idMapper;
+ protected final SamlDeployment deployment;
+ private final ElytronHttpFacade exchange;
+
+
+ public ElytronSamlSessionStore(ElytronHttpFacade exchange, SessionIdMapper idMapper, SamlDeployment deployment) {
+ this.exchange = exchange;
+ this.idMapper = idMapper;
+ this.deployment = deployment;
+ }
+
+ @Override
+ public void setCurrentAction(CurrentAction action) {
+ if (action == CurrentAction.NONE && !exchange.getScope(Scope.SESSION).exists()) return;
+ exchange.getScope(Scope.SESSION).setAttachment(CURRENT_ACTION, action);
+ }
+
+ @Override
+ public boolean isLoggingIn() {
+ HttpScope session = exchange.getScope(Scope.SESSION);
+ if (!session.exists()) return false;
+ CurrentAction action = (CurrentAction) session.getAttachment(CURRENT_ACTION);
+ return action == CurrentAction.LOGGING_IN;
+ }
+
+ @Override
+ public boolean isLoggingOut() {
+ HttpScope session = exchange.getScope(Scope.SESSION);
+ if (!session.exists()) return false;
+ CurrentAction action = (CurrentAction) session.getAttachment(CURRENT_ACTION);
+ return action == CurrentAction.LOGGING_OUT;
+ }
+
+ @Override
+ public void logoutAccount() {
+ HttpScope session = getSession(false);
+ if (session.exists()) {
+ SamlSession samlSession = (SamlSession)session.getAttachment(SamlSession.class.getName());
+ if (samlSession != null) {
+ if (samlSession.getSessionIndex() != null) {
+ idMapper.removeSession(session.getID());
+ }
+ session.setAttachment(SamlSession.class.getName(), null);
+ }
+ session.setAttachment(SAML_REDIRECT_URI, null);
+ }
+ }
+
+ @Override
+ public void logoutByPrincipal(String principal) {
+ Set<String> sessions = idMapper.getUserSessions(principal);
+ if (sessions != null) {
+ List<String> ids = new LinkedList<>();
+ ids.addAll(sessions);
+ logoutSessionIds(ids);
+ for (String id : ids) {
+ idMapper.removeSession(id);
+ }
+ }
+
+ }
+
+ @Override
+ public void logoutBySsoId(List<String> ssoIds) {
+ if (ssoIds == null) return;
+ List<String> sessionIds = new LinkedList<>();
+ for (String id : ssoIds) {
+ String sessionId = idMapper.getSessionFromSSO(id);
+ if (sessionId != null) {
+ sessionIds.add(sessionId);
+ idMapper.removeSession(sessionId);
+ }
+
+ }
+ logoutSessionIds(sessionIds);
+ }
+
+ protected void logoutSessionIds(List<String> sessionIds) {
+ sessionIds.forEach(id -> {
+ HttpScope scope = exchange.getScope(Scope.SESSION, id);
+
+ if (scope.exists()) {
+ scope.invalidate();
+ }
+ });
+ }
+
+ @Override
+ public boolean isLoggedIn() {
+ HttpScope session = getSession(false);
+ if (!session.exists()) {
+ log.debug("session was null, returning null");
+ return false;
+ }
+ final SamlSession samlSession = (SamlSession)session.getAttachment(SamlSession.class.getName());
+ if (samlSession == null) {
+ log.debug("SamlSession was not in session, returning null");
+ return false;
+ }
+
+ exchange.authenticationComplete(samlSession);
+ restoreRequest();
+ return true;
+ }
+
+ @Override
+ public void saveAccount(SamlSession account) {
+ HttpScope session = getSession(true);
+ session.setAttachment(SamlSession.class.getName(), account);
+ String sessionId = changeSessionId(session);
+ idMapper.map(account.getSessionIndex(), account.getPrincipal().getSamlSubject(), sessionId);
+
+ }
+
+ protected String changeSessionId(HttpScope session) {
+ if (!deployment.turnOffChangeSessionIdOnLogin()) return session.getID();
+ else return session.getID();
+ }
+
+ @Override
+ public SamlSession getAccount() {
+ HttpScope session = getSession(true);
+ return (SamlSession)session.getAttachment(SamlSession.class.getName());
+ }
+
+ @Override
+ public String getRedirectUri() {
+ HttpScope session = exchange.getScope(Scope.SESSION);
+ String redirect = (String) session.getAttachment(SAML_REDIRECT_URI);
+ if (redirect == null) {
+ URI uri = exchange.getURI();
+ String path = uri.getPath();
+ String relativePath = exchange.getRequest().getRelativePath();
+ String contextPath = path.substring(0, path.indexOf(relativePath));
+
+ if (!contextPath.isEmpty()) {
+ contextPath = contextPath + "/";
+ }
+
+ String baseUri = KeycloakUriBuilder.fromUri(path).replacePath(contextPath).build().toString();
+ return SamlUtil.getRedirectTo(exchange, contextPath, baseUri);
+ }
+ return redirect;
+ }
+
+ @Override
+ public void saveRequest() {
+ exchange.suspendRequest();
+ HttpScope scope = exchange.getScope(Scope.SESSION);
+
+ if (!scope.exists()) {
+ scope.create();
+ }
+
+ KeycloakUriBuilder uriBuilder = KeycloakUriBuilder.fromUri(exchange.getURI()).replaceQuery(exchange.getURI().getQuery());
+ String uri = uriBuilder.build().toString();
+
+ scope.setAttachment(SAML_REDIRECT_URI, uri);
+ }
+
+ @Override
+ public boolean restoreRequest() {
+ return exchange.restoreRequest();
+ }
+
+ protected HttpScope getSession(boolean create) {
+ HttpScope scope = exchange.getScope(Scope.SESSION);
+
+ if (!scope.exists() && create) {
+ scope.create();
+ }
+
+ return scope;
+ }
+
+ @Override
+ public void logout(boolean glo) {
+ logoutAccount();
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronTokeStore.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronTokeStore.java
new file mode 100644
index 0000000..a658464
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/ElytronTokeStore.java
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.saml.elytron;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public interface ElytronTokeStore {
+ void logout(boolean glo);
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java
new file mode 100644
index 0000000..94ae592
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakConfigurationServletListener.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+
+import javax.servlet.ServletContext;
+import javax.servlet.ServletContextEvent;
+import javax.servlet.ServletContextListener;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.AdapterConstants;
+import org.keycloak.adapters.saml.DefaultSamlDeployment;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.saml.config.parsers.DeploymentBuilder;
+import org.keycloak.adapters.saml.config.parsers.ResourceLoader;
+import org.keycloak.saml.common.exceptions.ParsingException;
+
+/**
+ * <p>A {@link ServletContextListener} that parses the keycloak adapter configuration and set the same configuration
+ * as a {@link ServletContext} attribute in order to provide to {@link KeycloakHttpServerAuthenticationMechanism} a way
+ * to obtain the configuration when processing requests.
+ *
+ * <p>This listener should be automatically registered to a deployment using the subsystem.
+ *
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakConfigurationServletListener implements ServletContextListener {
+
+ protected static Logger log = Logger.getLogger(KeycloakConfigurationServletListener.class);
+
+ @Override
+ public void contextInitialized(ServletContextEvent sce) {
+ ServletContext servletContext = sce.getServletContext();
+ String configResolverClass = servletContext.getInitParameter("keycloak.config.resolver");
+ SamlDeploymentContext deploymentContext = null;
+ if (configResolverClass != null) {
+ try {
+ throw new RuntimeException("Not implemented yet");
+ //configResolver = (SamlConfigResolver) deploymentInfo.getClassLoader().loadClass(configResolverClass).newInstance();
+ //deploymentContext = new AdapterDeploymentContext(configResolver);
+ //log.info("Using " + configResolverClass + " to resolve Keycloak configuration on a per-request basis.");
+ } catch (Exception ex) {
+ log.warn("The specified resolver " + configResolverClass + " could NOT be loaded. Keycloak is unconfigured and will deny all requests. Reason: " + ex.getMessage());
+ //deploymentContext = new AdapterDeploymentContext(new KeycloakDeployment());
+ }
+ } else {
+ InputStream is = getConfigInputStream(servletContext);
+ final SamlDeployment deployment;
+ if (is == null) {
+ log.warn("No adapter configuration. Keycloak is unconfigured and will deny all requests.");
+ deployment = new DefaultSamlDeployment();
+ } else {
+ try {
+ ResourceLoader loader = new ResourceLoader() {
+ @Override
+ public InputStream getResourceAsStream(String resource) {
+ return servletContext.getResourceAsStream(resource);
+ }
+ };
+ deployment = new DeploymentBuilder().build(is, loader);
+ } catch (ParsingException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ deploymentContext = new SamlDeploymentContext(deployment);
+ servletContext.setAttribute(SamlDeploymentContext.class.getName(), deploymentContext);
+ log.debug("Keycloak is using a per-deployment configuration.");
+ }
+ }
+
+ @Override
+ public void contextDestroyed(ServletContextEvent sce) {
+
+ }
+
+ private static InputStream getConfigInputStream(ServletContext context) {
+ InputStream is = getXMLFromServletContext(context);
+ if (is == null) {
+ String path = context.getInitParameter("keycloak.config.file");
+ if (path == null) {
+ log.debug("using /WEB-INF/keycloak-saml.xml");
+ is = context.getResourceAsStream("/WEB-INF/keycloak-saml.xml");
+ } else {
+ try {
+ is = new FileInputStream(path);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+ return is;
+ }
+
+ 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());
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanism.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanism.java
new file mode 100644
index 0000000..9fce501
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanism.java
@@ -0,0 +1,155 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.net.URI;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.saml.SamlAuthenticator;
+import org.keycloak.adapters.saml.SamlDeployment;
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.spi.AuthChallenge;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerRequest;
+import org.wildfly.security.http.Scope;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+class KeycloakHttpServerAuthenticationMechanism implements HttpServerAuthenticationMechanism {
+
+ static Logger LOGGER = Logger.getLogger(KeycloakHttpServerAuthenticationMechanismFactory.class);
+ static final String NAME = "KEYCLOAK-SAML";
+
+ private final Map<String, ?> properties;
+ private final CallbackHandler callbackHandler;
+ private final SamlDeploymentContext deploymentContext;
+ private final SessionIdMapper idMapper;
+
+ public KeycloakHttpServerAuthenticationMechanism(Map<String, ?> properties, CallbackHandler callbackHandler, SamlDeploymentContext deploymentContext, SessionIdMapper idMapper) {
+ this.properties = properties;
+ this.callbackHandler = callbackHandler;
+ this.deploymentContext = deploymentContext;
+ this.idMapper = idMapper;
+ }
+
+ @Override
+ public String getMechanismName() {
+ return NAME;
+ }
+
+ @Override
+ public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException {
+ LOGGER.debugf("Evaluating request for path [%s]", request.getRequestURI());
+ SamlDeploymentContext deploymentContext = getDeploymentContext(request);
+
+ if (deploymentContext == null) {
+ LOGGER.debugf("Ignoring request for path [%s] from mechanism [%s]. No deployment context found.", request.getRequestURI());
+ request.noAuthenticationInProgress();
+ return;
+ }
+
+ ElytronHttpFacade httpFacade = new ElytronHttpFacade(request, idMapper, deploymentContext, callbackHandler);
+ SamlDeployment deployment = httpFacade.getDeployment();
+
+ if (!deployment.isConfigured()) {
+ request.noAuthenticationInProgress();
+ return;
+ }
+
+ if (httpFacade.getRequest().getRelativePath().contains(deployment.getLogoutPage())) {
+ LOGGER.debugf("Ignoring request for [%s] and logout page [%s].", request.getRequestURI(), deployment.getLogoutPage());
+ httpFacade.authenticationCompleteAnonymous();
+ return;
+ }
+
+ SamlAuthenticator authenticator;
+
+ if (httpFacade.getRequest().getRelativePath().endsWith("/saml")) {
+ authenticator = new ElytronSamlEndpoint(httpFacade, deployment);
+ } else {
+ authenticator = new ElytronSamlAuthenticator(httpFacade, deployment, callbackHandler);
+
+ }
+
+ AuthOutcome outcome = authenticator.authenticate();
+
+ if (outcome == AuthOutcome.AUTHENTICATED) {
+ httpFacade.authenticationComplete();
+ return;
+ }
+
+ if (outcome == AuthOutcome.NOT_AUTHENTICATED) {
+ httpFacade.noAuthenticationInProgress(null);
+ return;
+ }
+
+ if (outcome == AuthOutcome.LOGGED_OUT) {
+ if (deployment.getLogoutPage() != null) {
+ redirectLogout(deployment, httpFacade);
+ }
+ httpFacade.authenticationInProgress();
+ return;
+ }
+
+ AuthChallenge challenge = authenticator.getChallenge();
+
+ if (challenge != null) {
+ httpFacade.noAuthenticationInProgress(challenge);
+ return;
+ }
+
+ if (outcome == AuthOutcome.FAILED) {
+ httpFacade.authenticationFailed();
+ return;
+ }
+
+ httpFacade.authenticationInProgress();
+ }
+
+ private SamlDeploymentContext getDeploymentContext(HttpServerRequest request) {
+ if (this.deploymentContext == null) {
+ return (SamlDeploymentContext) request.getScope(Scope.APPLICATION).getAttachment(SamlDeploymentContext.class.getName());
+ }
+
+ return this.deploymentContext;
+ }
+
+ protected void redirectLogout(SamlDeployment deployment, ElytronHttpFacade exchange) {
+ String page = deployment.getLogoutPage();
+ sendRedirect(exchange, page);
+ exchange.getResponse().setStatus(302);
+ }
+
+ static void sendRedirect(final ElytronHttpFacade exchange, final String location) {
+ // TODO - String concatenation to construct URLS is extremely error prone - switch to a URI which will better
+ // handle this.
+ URI uri = exchange.getURI();
+ String path = uri.getPath();
+ String relativePath = exchange.getRequest().getRelativePath();
+ String contextPath = path.substring(0, path.indexOf(relativePath));
+ String loc = exchange.getURI().getScheme() + "://" + exchange.getURI().getHost() + ":" + exchange.getURI().getPort() + contextPath + location;
+ exchange.getResponse().setHeader("Location", loc);
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java
new file mode 100644
index 0000000..c1b69a4
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakHttpServerAuthenticationMechanismFactory.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import javax.security.auth.callback.CallbackHandler;
+
+import org.keycloak.adapters.saml.SamlDeploymentContext;
+import org.keycloak.adapters.spi.InMemorySessionIdMapper;
+import org.keycloak.adapters.spi.SessionIdMapper;
+import org.wildfly.security.http.HttpAuthenticationException;
+import org.wildfly.security.http.HttpServerAuthenticationMechanism;
+import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakHttpServerAuthenticationMechanismFactory implements HttpServerAuthenticationMechanismFactory {
+
+ private SessionIdMapper idMapper = new InMemorySessionIdMapper();
+ private final SamlDeploymentContext deploymentContext;
+
+ /**
+ * <p>Creates a new instance.
+ *
+ * <p>A default constructor is necessary in order to allow this factory to be loaded via {@link java.util.ServiceLoader}.
+ */
+ public KeycloakHttpServerAuthenticationMechanismFactory() {
+ this(null);
+ }
+
+ public KeycloakHttpServerAuthenticationMechanismFactory(SamlDeploymentContext deploymentContext) {
+ this.deploymentContext = deploymentContext;
+ }
+
+ @Override
+ public String[] getMechanismNames(Map<String, ?> properties) {
+ return new String[] {KeycloakHttpServerAuthenticationMechanism.NAME};
+ }
+
+ @Override
+ public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map<String, ?> properties, CallbackHandler callbackHandler) throws HttpAuthenticationException {
+ Map<String, Object> mechanismProperties = new HashMap();
+
+ mechanismProperties.putAll(properties);
+
+ if (KeycloakHttpServerAuthenticationMechanism.NAME.equals(mechanismName)) {
+ return new KeycloakHttpServerAuthenticationMechanism(properties, callbackHandler, this.deploymentContext, idMapper);
+ }
+
+ return null;
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java
new file mode 100644
index 0000000..3207835
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/KeycloakSecurityRealm.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters.saml.elytron;
+
+import java.security.Principal;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.keycloak.adapters.saml.SamlPrincipal;
+import org.wildfly.security.auth.SupportLevel;
+import org.wildfly.security.auth.server.RealmIdentity;
+import org.wildfly.security.auth.server.RealmUnavailableException;
+import org.wildfly.security.auth.server.SecurityRealm;
+import org.wildfly.security.authz.AuthorizationIdentity;
+import org.wildfly.security.authz.MapAttributes;
+import org.wildfly.security.credential.Credential;
+import org.wildfly.security.evidence.BearerTokenEvidence;
+import org.wildfly.security.evidence.Evidence;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+public class KeycloakSecurityRealm implements SecurityRealm {
+
+ @Override
+ public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException {
+ if (principal instanceof SamlPrincipal) {
+ return createRealmIdentity((SamlPrincipal) principal);
+ }
+ return RealmIdentity.NON_EXISTENT;
+ }
+
+ private RealmIdentity createRealmIdentity(SamlPrincipal principal) {
+ return new RealmIdentity() {
+ @Override
+ public Principal getRealmIdentityPrincipal() {
+ return principal;
+ }
+
+ @Override
+ public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ @Override
+ public <C extends Credential> C getCredential(Class<C> credentialType) throws RealmUnavailableException {
+ return null;
+ }
+
+ @Override
+ public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
+ if (isBearerTokenEvidence(evidenceType)) {
+ return SupportLevel.SUPPORTED;
+ }
+
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ @Override
+ public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException {
+ return principal != null;
+ }
+
+ @Override
+ public boolean exists() throws RealmUnavailableException {
+ return principal != null;
+ }
+
+ @Override
+ public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException {
+ Map<String, List<String>> attributes = new HashMap<>(principal.getAttributes());
+ return AuthorizationIdentity.basicIdentity(new MapAttributes(attributes));
+ }
+ };
+ }
+
+ @Override
+ public SupportLevel getCredentialAcquireSupport(Class<? extends Credential> credentialType, String algorithmName) throws RealmUnavailableException {
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ @Override
+ public SupportLevel getEvidenceVerifySupport(Class<? extends Evidence> evidenceType, String algorithmName) throws RealmUnavailableException {
+ if (isBearerTokenEvidence(evidenceType)) {
+ return SupportLevel.POSSIBLY_SUPPORTED;
+ }
+
+ return SupportLevel.UNSUPPORTED;
+ }
+
+ private boolean isBearerTokenEvidence(Class<?> evidenceType) {
+ return evidenceType != null && evidenceType.equals(BearerTokenEvidence.class);
+ }
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/SecurityIdentityUtil.java b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/SecurityIdentityUtil.java
new file mode 100644
index 0000000..ce45db6
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/java/org/keycloak/adapters/saml/elytron/SecurityIdentityUtil.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.keycloak.adapters.saml.elytron;
+
+import java.io.IOException;
+import java.security.Principal;
+
+import javax.security.auth.callback.Callback;
+import javax.security.auth.callback.CallbackHandler;
+import javax.security.auth.callback.UnsupportedCallbackException;
+import javax.security.sasl.AuthorizeCallback;
+
+import org.keycloak.adapters.saml.SamlPrincipal;
+import org.wildfly.security.auth.callback.AuthenticationCompleteCallback;
+import org.wildfly.security.auth.callback.EvidenceVerifyCallback;
+import org.wildfly.security.auth.callback.SecurityIdentityCallback;
+import org.wildfly.security.auth.server.SecurityIdentity;
+import org.wildfly.security.evidence.Evidence;
+import org.wildfly.security.http.HttpAuthenticationException;
+
+/**
+ * @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
+ */
+final class SecurityIdentityUtil {
+
+ static final SecurityIdentity authorize(CallbackHandler callbackHandler, SamlPrincipal principal) {
+ try {
+ EvidenceVerifyCallback evidenceVerifyCallback = new EvidenceVerifyCallback(new Evidence() {
+ @Override
+ public Principal getPrincipal() {
+ return principal;
+ }
+ });
+
+ callbackHandler.handle(new Callback[]{evidenceVerifyCallback});
+
+ if (evidenceVerifyCallback.isVerified()) {
+ AuthorizeCallback authorizeCallback = new AuthorizeCallback(null, null);
+
+ try {
+ callbackHandler.handle(new Callback[] {authorizeCallback});
+ } catch (Exception e) {
+ throw new HttpAuthenticationException(e);
+ }
+
+ if (authorizeCallback.isAuthorized()) {
+ SecurityIdentityCallback securityIdentityCallback = new SecurityIdentityCallback();
+
+ callbackHandler.handle(new Callback[]{AuthenticationCompleteCallback.SUCCEEDED, securityIdentityCallback});
+
+ SecurityIdentity securityIdentity = securityIdentityCallback.getSecurityIdentity();
+
+ return securityIdentity;
+ }
+ }
+ } catch (UnsupportedCallbackException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ return null;
+ }
+
+}
diff --git a/adapters/saml/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory b/adapters/saml/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory
new file mode 100644
index 0000000..a41c127
--- /dev/null
+++ b/adapters/saml/wildfly-elytron/src/main/resources/META-INF/services/org.wildfly.security.http.HttpServerAuthenticationMechanismFactory
@@ -0,0 +1,19 @@
+#
+# JBoss, Home of Professional Open Source.
+# Copyright 2016 Red Hat, Inc., and individual contributors
+# as indicated by the @author tags.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+org.keycloak.adapters.saml.elytron.KeycloakHttpServerAuthenticationMechanismFactory
\ No newline at end of file
diff --git a/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml b/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
index ece320b..527750f 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
@@ -35,6 +35,7 @@
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-undertow-adapter/**</include>
<include>org/keycloak/keycloak-wildfly-adapter/**</include>
+ <include>org/keycloak/keycloak-wildfly-elytron-oidc-adapter/**</include>
<include>org/keycloak/keycloak-wildfly-subsystem/**</include>
<include>org/keycloak/keycloak-adapter-subsystem/**</include>
<include>org/keycloak/keycloak-servlet-oauth-client/**</include>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
index a534b4f..8e608a5 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/build.xml
@@ -77,6 +77,10 @@
<module-def name="org.keycloak.keycloak-authz-client">
<maven-resource group="org.keycloak" artifact="keycloak-authz-client"/>
</module-def>
+
+ <module-def name="org.keycloak.keycloak-wildfly-elytron-oidc-adapter">
+ <maven-resource group="org.keycloak" artifact="keycloak-wildfly-elytron-oidc-adapter"/>
+ </module-def>
</target>
<target name="clean-target">
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
index b5a3d5e..00eef58 100755
--- a/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/pom.xml
@@ -68,6 +68,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-subsystem</artifactId>
</dependency>
<dependency>
diff --git a/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-elytron-oidc-adapter/main/module.xml b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-elytron-oidc-adapter/main/module.xml
new file mode 100755
index 0000000..1ca9839
--- /dev/null
+++ b/distribution/adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-wildfly-elytron-oidc-adapter/main/module.xml
@@ -0,0 +1,51 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2016 Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-elytron-oidc-adapter">
+ <properties>
+ <property name="jboss.api" value="private"/>
+ </properties>
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.bouncycastle" />
+ <module name="com.fasterxml.jackson.core.jackson-annotations"/>
+ <module name="com.fasterxml.jackson.core.jackson-core"/>
+ <module name="com.fasterxml.jackson.core.jackson-databind"/>
+ <module name="com.fasterxml.jackson.jaxrs.jackson-jaxrs-json-provider"/>
+ <module name="org.apache.httpcomponents"/>
+ <module name="javax.servlet.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ <module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-undertow-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
+ <module name="org.keycloak.keycloak-adapter-core"/>
+ <module name="org.keycloak.keycloak-core"/>
+ <module name="org.keycloak.keycloak-common"/>
+ <module name="org.wildfly.security.elytron"/>
+ </dependencies>
+
+</module>
diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
index b91b2dc..ff57870 100755
--- a/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
+++ b/distribution/saml-adapters/wildfly-adapter/wildfly-adapter-zip/assembly.xml
@@ -37,6 +37,7 @@
<include>org/keycloak/keycloak-jboss-adapter-core/**</include>
<include>org/keycloak/keycloak-saml-undertow-adapter/**</include>
<include>org/keycloak/keycloak-saml-wildfly-adapter/**</include>
+ <include>org/keycloak/keycloak-saml-wildfly-elytron-adapter/**</include>
<include>org/keycloak/keycloak-saml-wildfly-subsystem/**</include>
<include>org/keycloak/keycloak-saml-adapter-subsystem/**</include>
</includes>
diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml
index abbe9d3..885ed81 100755
--- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml
+++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/build.xml
@@ -76,6 +76,10 @@
<maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-subsystem"/>
</module-def>
+ <module-def name="org.keycloak.keycloak-saml-wildfly-elytron-adapter">
+ <maven-resource group="org.keycloak" artifact="keycloak-saml-wildfly-elytron-adapter"/>
+ </module-def>
+
</target>
<target name="clean-target">
diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml
index 06f0ded..026d869 100755
--- a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml
+++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/pom.xml
@@ -72,6 +72,10 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-saml-wildfly-subsystem</artifactId>
</dependency>
</dependencies>
diff --git a/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-elytron-adapter/main/module.xml b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-elytron-adapter/main/module.xml
new file mode 100755
index 0000000..393eac9
--- /dev/null
+++ b/distribution/saml-adapters/wildfly-adapter/wildfly-modules/src/main/resources/modules/org/keycloak/keycloak-saml-wildfly-elytron-adapter/main/module.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+
+
+<!--
+ ~ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ * and other contributors as indicated by the @author tags.
+ ~ *
+ ~ * Licensed under the Apache License, Version 2.0 (the "License");
+ ~ * you may not use this file except in compliance with the License.
+ ~ * You may obtain a copy of the License at
+ ~ *
+ ~ * http://www.apache.org/licenses/LICENSE-2.0
+ ~ *
+ ~ * Unless required by applicable law or agreed to in writing, software
+ ~ * distributed under the License is distributed on an "AS IS" BASIS,
+ ~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ * See the License for the specific language governing permissions and
+ ~ * limitations under the License.
+ -->
+
+<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-saml-wildfly-elytron-adapter">
+ <properties>
+ <property name="jboss.api" value="private"/>
+ </properties>
+ <resources>
+ <!-- Insert resources here -->
+ </resources>
+ <dependencies>
+ <module name="javax.api"/>
+ <module name="org.bouncycastle" />
+ <module name="javax.servlet.api"/>
+ <module name="org.jboss.logging"/>
+ <module name="io.undertow.core"/>
+ <module name="io.undertow.servlet"/>
+ <module name="org.picketbox"/>
+ <module name="org.keycloak.keycloak-saml-undertow-adapter"/>
+ <module name="org.keycloak.keycloak-adapter-spi"/>
+ <module name="org.keycloak.keycloak-saml-core-public"/>
+ <module name="org.keycloak.keycloak-saml-core"/>
+ <module name="org.keycloak.keycloak-saml-adapter-api-public"/>
+ <module name="org.keycloak.keycloak-saml-adapter-core"/>
+ <module name="org.keycloak.keycloak-common"/>
+ <module name="org.apache.httpcomponents"/>
+ <module name="org.infinispan"/>
+ <module name="org.infinispan.commons"/>
+ <module name="org.infinispan.cachestore.remote"/>
+ <module name="org.infinispan.client.hotrod"/>
+ <module name="org.wildfly.security.elytron"/>
+ </dependencies>
+
+</module>
elytron/standalone-elytron.xml 558(+558 -0)
diff --git a/elytron/standalone-elytron.xml b/elytron/standalone-elytron.xml
new file mode 100644
index 0000000..4237b02
--- /dev/null
+++ b/elytron/standalone-elytron.xml
@@ -0,0 +1,558 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<server xmlns="urn:jboss:domain:5.0">
+
+ <extensions>
+ <extension module="org.jboss.as.clustering.infinispan"/>
+ <extension module="org.jboss.as.connector"/>
+ <extension module="org.jboss.as.deployment-scanner"/>
+ <extension module="org.jboss.as.ee"/>
+ <extension module="org.jboss.as.ejb3"/>
+ <extension module="org.jboss.as.jaxrs"/>
+ <extension module="org.jboss.as.jdr"/>
+ <extension module="org.jboss.as.jmx"/>
+ <extension module="org.jboss.as.jpa"/>
+ <extension module="org.jboss.as.jsf"/>
+ <extension module="org.jboss.as.logging"/>
+ <extension module="org.jboss.as.mail"/>
+ <extension module="org.jboss.as.naming"/>
+ <extension module="org.jboss.as.pojo"/>
+ <extension module="org.jboss.as.remoting"/>
+ <extension module="org.jboss.as.sar"/>
+ <extension module="org.jboss.as.security"/>
+ <extension module="org.jboss.as.transactions"/>
+ <extension module="org.jboss.as.webservices"/>
+ <extension module="org.jboss.as.weld"/>
+ <extension module="org.wildfly.extension.batch.jberet"/>
+ <extension module="org.wildfly.extension.bean-validation"/>
+ <extension module="org.wildfly.extension.core-management"/>
+ <extension module="org.wildfly.extension.elytron"/>
+ <extension module="org.wildfly.extension.io"/>
+ <extension module="org.wildfly.extension.request-controller"/>
+ <extension module="org.wildfly.extension.security.manager"/>
+ <extension module="org.wildfly.extension.undertow"/>
+ <extension module="org.keycloak.keycloak-adapter-subsystem"/>
+ </extensions>
+
+
+ <management>
+ <security-realms>
+ <security-realm name="ManagementRealm">
+ <authentication>
+ <local default-user="$local" skip-group-loading="true"/>
+ <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
+ </authorization>
+ </security-realm>
+ <security-realm name="ApplicationRealm">
+ <server-identities>
+ <ssl>
+ <keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
+ </ssl>
+ </server-identities>
+ <authentication>
+ <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
+ <properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <formatters>
+ <json-formatter name="json-formatter"/>
+ </formatters>
+ <handlers>
+ <file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="file"/>
+ </handlers>
+ </logger>
+ </audit-log>
+ <management-interfaces>
+ <http-interface security-realm="ManagementRealm">
+ <http-upgrade enabled="true" sasl-authentication-factory="keycloak-sasl-authentication"/>
+ <socket-binding http="management-http"/>
+ </http-interface>
+ </management-interfaces>
+ <access-control provider="rbac" use-identity-roles="true"/>
+ </management>
+
+ <profile>
+ <subsystem xmlns="urn:jboss:domain:logging:3.0">
+ <console-handler name="CONSOLE">
+ <level name="INFO"/>
+ <formatter>
+ <named-formatter name="COLOR-PATTERN"/>
+ </formatter>
+ </console-handler>
+ <periodic-rotating-file-handler name="FILE" autoflush="true">
+ <formatter>
+ <named-formatter name="PATTERN"/>
+ </formatter>
+ <file relative-to="jboss.server.log.dir" path="server.log"/>
+ <suffix value=".yyyy-MM-dd"/>
+ <append value="true"/>
+ </periodic-rotating-file-handler>
+ <logger category="com.arjuna">
+ <level name="WARN"/>
+ </logger>
+ <logger category="org.jboss.as.config">
+ <level name="DEBUG"/>
+ </logger>
+ <logger category="sun.rmi">
+ <level name="WARN"/>
+ </logger>
+ <root-logger>
+ <level name="INFO"/>
+ <handlers>
+ <handler name="CONSOLE"/>
+ <handler name="FILE"/>
+ </handlers>
+ </root-logger>
+ <formatter name="PATTERN">
+ <pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
+ </formatter>
+ <formatter name="COLOR-PATTERN">
+ <pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
+ </formatter>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:batch-jberet:2.0">
+ <default-job-repository name="in-memory"/>
+ <default-thread-pool name="batch"/>
+ <job-repository name="in-memory">
+ <in-memory/>
+ </job-repository>
+ <thread-pool name="batch">
+ <max-threads count="10"/>
+ <keepalive-time time="30" unit="seconds"/>
+ </thread-pool>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:datasources:5.0">
+ <datasources>
+ <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
+ <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
+ <driver>h2</driver>
+ <security>
+ <user-name>sa</user-name>
+ <password>sa</password>
+ </security>
+ </datasource>
+ <drivers>
+ <driver name="h2" module="com.h2database.h2">
+ <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
+ </driver>
+ </drivers>
+ </datasources>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
+ <deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ee:4.0">
+ <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
+ <concurrent>
+ <context-services>
+ <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
+ </context-services>
+ <managed-thread-factories>
+ <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
+ </managed-thread-factories>
+ <managed-executor-services>
+ <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
+ </managed-executor-services>
+ <managed-scheduled-executor-services>
+ <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
+ </managed-scheduled-executor-services>
+ </concurrent>
+ <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ejb3:5.0">
+ <session-bean>
+ <stateless>
+ <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
+ </stateless>
+ <stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
+ <singleton default-access-timeout="5000"/>
+ </session-bean>
+ <pools>
+ <bean-instance-pools>
+ <strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ <strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ </bean-instance-pools>
+ </pools>
+ <caches>
+ <cache name="simple"/>
+ <cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
+ </caches>
+ <passivation-stores>
+ <passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
+ </passivation-stores>
+ <async thread-pool-name="default"/>
+ <timer-service thread-pool-name="default" default-data-store="default-file-store">
+ <data-stores>
+ <file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
+ </data-stores>
+ </timer-service>
+ <remote connector-ref="http-remoting-connector" thread-pool-name="default">
+ <channel-creation-options>
+ <option name="READ_TIMEOUT" value="${prop.remoting-connector.read.timeout:20}" type="xnio"/>
+ <option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
+ </channel-creation-options>
+ </remote>
+ <thread-pools>
+ <thread-pool name="default">
+ <max-threads count="10"/>
+ <keepalive-time time="100" unit="milliseconds"/>
+ </thread-pool>
+ </thread-pools>
+ <default-security-domain value="other"/>
+ <default-missing-method-permissions-deny-access value="true"/>
+ <log-system-exceptions value="true"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:io:1.1">
+ <worker name="default"/>
+ <buffer-pool name="default"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:infinispan:4.0">
+ <cache-container name="server" default-cache="default" module="org.wildfly.clustering.server">
+ <local-cache name="default">
+ <transaction mode="BATCH"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="web" default-cache="passivation" module="org.wildfly.clustering.web.infinispan">
+ <local-cache name="passivation">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ <local-cache name="persistent">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="false" purge="false"/>
+ </local-cache>
+ <local-cache name="concurrent">
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="ejb" aliases="sfsb" default-cache="passivation" module="org.wildfly.clustering.ejb.infinispan">
+ <local-cache name="passivation">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ <local-cache name="persistent">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="false" purge="false"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="hibernate" module="org.hibernate.infinispan">
+ <local-cache name="entity">
+ <transaction mode="NON_XA"/>
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </local-cache>
+ <local-cache name="local-query">
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </local-cache>
+ <local-cache name="timestamps"/>
+ </cache-container>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jaxrs:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jca:5.0">
+ <archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
+ <bean-validation enabled="true"/>
+ <default-workmanager>
+ <short-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </short-running-threads>
+ <long-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </long-running-threads>
+ </default-workmanager>
+ <cached-connection-manager/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jdr:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jmx:1.3">
+ <expose-resolved-model/>
+ <expose-expression-model/>
+ <remoting-connector/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jpa:1.1">
+ <jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jsf:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:mail:2.1">
+ <mail-session name="default" jndi-name="java:jboss/mail/Default">
+ <smtp-server outbound-socket-binding-ref="mail-smtp"/>
+ </mail-session>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:naming:2.0">
+ <remote-naming/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:pojo:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:remoting:4.0">
+ <endpoint/>
+ <http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:resource-adapters:5.0"/>
+ <subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:sar:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:security-manager:3.0">
+ <deployment-permissions>
+ <maximum-set>
+ <permission class="java.security.AllPermission"/>
+ </maximum-set>
+ </deployment-permissions>
+ </subsystem>
+ <subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers">
+ <providers>
+ <aggregate-providers name="combined-providers">
+ <providers name="elytron"/>
+ <providers name="openssl"/>
+ </aggregate-providers>
+ <provider-loader name="elytron" module="org.wildfly.security.elytron"/>
+ <provider-loader name="openssl" module="org.wildfly.openssl"/>
+ </providers>
+ <audit-logging>
+ <file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
+ </audit-logging>
+ <security-domains>
+ <security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
+ <realm name="local"/>
+ </security-domain>
+ <security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="ManagementRealm" role-decoder="groups-to-roles"/>
+ <realm name="local" role-mapper="super-user-mapper"/>
+ </security-domain>
+ <security-domain name="KeycloakSecurityDomain" default-realm="wildfly-cli" permission-mapper="default-permission-mapper">
+ <realm name="wildfly-cli" role-decoder="groups-to-roles"/>
+ </security-domain>
+ </security-domains>
+ <security-realms>
+ <identity-realm name="local" identity="$local"/>
+ <properties-realm name="ApplicationRealm">
+ <users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
+ <groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
+ </properties-realm>
+ <properties-realm name="ManagementRealm">
+ <users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
+ <groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
+ </properties-realm>
+ <custom-realm name="KeycloakRealm" class-name="org.keycloak.adapters.elytron.KeycloakSecurityRealm" module="org.keycloak.keycloak-wildfly-elytron-oidc-adapter" />
+ </security-realms>
+ <mappers>
+ <constant-permission-mapper name="default-permission-mapper">
+ <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
+ <permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
+ <permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
+ <permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
+ </constant-permission-mapper>
+ <constant-realm-mapper name="local" realm-name="local"/>
+ <simple-role-decoder name="groups-to-roles" attribute="groups"/>
+ <constant-role-mapper name="super-user-mapper">
+ <role name="SuperUser"/>
+ </constant-role-mapper>
+ </mappers>
+ <http>
+ <http-authentication-factory name="management-http-authentication" http-server-mechanism-factory="global" security-domain="ManagementDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="DIGEST">
+ <mechanism-realm realm-name="ManagementRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </http-authentication-factory>
+ <http-authentication-factory name="application-http-authentication" http-server-mechanism-factory="global" security-domain="ApplicationDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="BASIC">
+ <mechanism-realm realm-name="Application Realm"/>
+ </mechanism>
+ <mechanism mechanism-name="FORM"/>
+ </mechanism-configuration>
+ </http-authentication-factory>
+ <provider-http-server-mechanism-factory name="global"/>
+ </http>
+ <sasl>
+ <sasl-authentication-factory name="keycloak-sasl-authentication" sasl-server-factory="configured" security-domain="KeycloakSecurityDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
+ <mechanism mechanism-name="DIGEST-MD5">
+ <mechanism-realm realm-name="ManagementRealm"/>
+ </mechanism>
+ <mechanism mechanism-name="OAUTHBEARER"/>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
+ <mechanism mechanism-name="DIGEST-MD5">
+ <mechanism-realm realm-name="ManagementRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
+ <mechanism mechanism-name="DIGEST-MD5">
+ <mechanism-realm realm-name="ApplicationRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
+ <properties>
+ <property name="wildfly.sasl.local-user.default-user" value="$local"/>
+ </properties>
+ <filters>
+ <filter>
+ <pattern-filter value="JBOSS-LOCAL-USER"/>
+ </filter>
+ <filter>
+ <pattern-filter value="DIGEST-MD5"/>
+ </filter>
+ <filter>
+ <pattern-filter value="OAUTHBEARER"/>
+ </filter>
+ </filters>
+ </configurable-sasl-server-factory>
+ <mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
+ <filters>
+ <filter provider-name="WildFlyElytron"/>
+ </filters>
+ </mechanism-provider-filtering-sasl-server-factory>
+ <provider-sasl-server-factory name="global"/>
+ </sasl>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:security:3.0">
+ <security-domains>
+ <security-domain name="other" cache-type="default">
+ <authentication>
+ <login-module code="Remoting" flag="optional">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ <login-module code="RealmDirect" flag="required">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ </authentication>
+ </security-domain>
+ <security-domain name="jboss-web-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ <security-domain name="jboss-ejb-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ <security-domain name="jaspitest" cache-type="default">
+ <authentication-jaspi>
+ <login-module-stack name="dummy">
+ <login-module code="Dummy" flag="optional"/>
+ </login-module-stack>
+ <auth-module code="Dummy"/>
+ </authentication-jaspi>
+ </security-domain>
+ <security-domain name="keycloak">
+ <authentication>
+ <login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
+ </authentication>
+ </security-domain>
+ </security-domains>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:transactions:3.1">
+ <core-environment>
+ <process-id>
+ <uuid/>
+ </process-id>
+ </core-environment>
+ <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
+ <object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:undertow:4.0">
+ <buffer-cache name="default"/>
+ <server name="default-server">
+ <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
+ <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
+ <host name="default-host" alias="localhost">
+ <location name="/" handler="welcome-content"/>
+ <filter-ref name="server-header"/>
+ <filter-ref name="x-powered-by-header"/>
+ <http-invoker http-authentication-factory="application-http-authentication"/>
+ </host>
+ </server>
+ <servlet-container name="default">
+ <jsp-config/>
+ <websockets/>
+ </servlet-container>
+ <handlers>
+ <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
+ </handlers>
+ <filters>
+ <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
+ <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
+ </filters>
+ <application-security-domains>
+ <application-security-domain name="other" http-authentication-factory="keycloak-http-authentication-factory"/>
+ </application-security-domains>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:webservices:2.0">
+ <wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host>
+ <endpoint-config name="Standard-Endpoint-Config"/>
+ <endpoint-config name="Recording-Endpoint-Config">
+ <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
+ <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
+ </pre-handler-chain>
+ </endpoint-config>
+ <client-config name="Standard-Client-Config"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:weld:4.0"/>
+ <subsystem xmlns="urn:jboss:domain:keycloak:1.1">
+ <realm name="wildfly">
+ <realm-public-key>MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqVOcTeth98Fi9T/9GMK9q7w6Wvft1Xc+aMFTru5vcsqh0NL1hYRuSdqxTK0lpbaTfDLF+bh1QP+1ZArjLEshNoddsc39Lf4xDh9smh1xOp/GcFQSDmSz9dQ8FmQUagnNtwIWSXVphGyK5yOznqIzrV/TNHuGvUA5MsPNkm99LrQlODLYr6hsE/kPoKMybi8z/tYkLJXtXS8ZM5O/2rOrPNcqvw58Fb1pJ0OXO59zK96qw/eqRnPPbi3N0FRQLKCG51DpQu6xe8zKHwEtUXDGdtgSceA6jKynmAG/dWrBEARczgAPbUlEIq3HByrtB1DHR0cZKUYVj5PwkGEg6IgXhwIDAQAB</realm-public-key>
+ <auth-server-url>http://localhost:8180/auth</auth-server-url>
+ <ssl-required>none</ssl-required>
+ </realm>
+ <secure-deployment name="wildfly-cli">
+ <realm>wildfly</realm>
+ <resource>wildfly-cli</resource>
+ </secure-deployment>
+ </subsystem>
+ </profile>
+
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+ </interfaces>
+
+ <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
+ <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
+ <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
+ <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
+ <socket-binding name="http" port="${jboss.http.port:8080}"/>
+ <socket-binding name="https" port="${jboss.https.port:8443}"/>
+ <socket-binding name="txn-recovery-environment" port="4712"/>
+ <socket-binding name="txn-status-manager" port="4713"/>
+ <outbound-socket-binding name="mail-smtp">
+ <remote-destination host="localhost" port="25"/>
+ </outbound-socket-binding>
+ </socket-binding-group>
+</server>
\ No newline at end of file
elytron/standalone-oauth2-sasl-with-elytron-only.xml 547(+547 -0)
diff --git a/elytron/standalone-oauth2-sasl-with-elytron-only.xml b/elytron/standalone-oauth2-sasl-with-elytron-only.xml
new file mode 100644
index 0000000..dde9d8b
--- /dev/null
+++ b/elytron/standalone-oauth2-sasl-with-elytron-only.xml
@@ -0,0 +1,547 @@
+<?xml version='1.0' encoding='UTF-8'?>
+
+<!--
+ ~ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ * and other contributors as indicated by the @author tags.
+ ~ *
+ ~ * Licensed under the Apache License, Version 2.0 (the "License");
+ ~ * you may not use this file except in compliance with the License.
+ ~ * You may obtain a copy of the License at
+ ~ *
+ ~ * http://www.apache.org/licenses/LICENSE-2.0
+ ~ *
+ ~ * Unless required by applicable law or agreed to in writing, software
+ ~ * distributed under the License is distributed on an "AS IS" BASIS,
+ ~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ * See the License for the specific language governing permissions and
+ ~ * limitations under the License.
+ -->
+
+<server xmlns="urn:jboss:domain:5.0">
+ <extensions>
+ <extension module="org.jboss.as.clustering.infinispan"/>
+ <extension module="org.jboss.as.connector"/>
+ <extension module="org.jboss.as.deployment-scanner"/>
+ <extension module="org.jboss.as.ee"/>
+ <extension module="org.jboss.as.ejb3"/>
+ <extension module="org.jboss.as.jaxrs"/>
+ <extension module="org.jboss.as.jdr"/>
+ <extension module="org.jboss.as.jmx"/>
+ <extension module="org.jboss.as.jpa"/>
+ <extension module="org.jboss.as.jsf"/>
+ <extension module="org.jboss.as.logging"/>
+ <extension module="org.jboss.as.mail"/>
+ <extension module="org.jboss.as.naming"/>
+ <extension module="org.jboss.as.pojo"/>
+ <extension module="org.jboss.as.remoting"/>
+ <extension module="org.jboss.as.sar"/>
+ <extension module="org.jboss.as.security"/>
+ <extension module="org.jboss.as.transactions"/>
+ <extension module="org.jboss.as.webservices"/>
+ <extension module="org.jboss.as.weld"/>
+ <extension module="org.wildfly.extension.batch.jberet"/>
+ <extension module="org.wildfly.extension.bean-validation"/>
+ <extension module="org.wildfly.extension.core-management"/>
+ <extension module="org.wildfly.extension.elytron"/>
+ <extension module="org.wildfly.extension.io"/>
+ <extension module="org.wildfly.extension.request-controller"/>
+ <extension module="org.wildfly.extension.security.manager"/>
+ <extension module="org.wildfly.extension.undertow"/>
+ </extensions>
+ <management>
+ <security-realms>
+ <security-realm name="ManagementRealm">
+ <authentication>
+ <local default-user="$local" skip-group-loading="true"/>
+ <properties path="mgmt-users.properties" relative-to="jboss.server.config.dir"/>
+ </authentication>
+ <authorization map-groups-to-roles="false">
+ <properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
+ </authorization>
+ </security-realm>
+ <security-realm name="ApplicationRealm">
+ <server-identities>
+ <ssl>
+ <keystore path="application.keystore" relative-to="jboss.server.config.dir" keystore-password="password" alias="server" key-password="password" generate-self-signed-certificate-host="localhost"/>
+ </ssl>
+ </server-identities>
+ <authentication>
+ <local default-user="$local" allowed-users="*" skip-group-loading="true"/>
+ <properties path="application-users.properties" relative-to="jboss.server.config.dir"/>
+ </authentication>
+ <authorization>
+ <properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
+ </authorization>
+ </security-realm>
+ </security-realms>
+ <audit-log>
+ <formatters>
+ <json-formatter name="json-formatter"/>
+ </formatters>
+ <handlers>
+ <file-handler name="file" formatter="json-formatter" path="audit-log.log" relative-to="jboss.server.data.dir"/>
+ </handlers>
+ <logger log-boot="true" log-read-only="false" enabled="false">
+ <handlers>
+ <handler name="file"/>
+ </handlers>
+ </logger>
+ </audit-log>
+ <management-interfaces>
+ <http-interface security-realm="ManagementRealm">
+ <http-upgrade enabled="true" sasl-authentication-factory="oauth2-sasl-authentication"/>
+ <socket-binding http="management-http"/>
+ </http-interface>
+ </management-interfaces>
+ <access-control provider="rbac" use-identity-roles="true"/>
+ </management>
+ <profile>
+ <subsystem xmlns="urn:jboss:domain:logging:3.0">
+ <console-handler name="CONSOLE">
+ <level name="INFO"/>
+ <formatter>
+ <named-formatter name="COLOR-PATTERN"/>
+ </formatter>
+ </console-handler>
+ <periodic-rotating-file-handler name="FILE" autoflush="true">
+ <formatter>
+ <named-formatter name="PATTERN"/>
+ </formatter>
+ <file relative-to="jboss.server.log.dir" path="server.log"/>
+ <suffix value=".yyyy-MM-dd"/>
+ <append value="true"/>
+ </periodic-rotating-file-handler>
+ <logger category="com.arjuna">
+ <level name="WARN"/>
+ </logger>
+ <logger category="org.jboss.as.config">
+ <level name="DEBUG"/>
+ </logger>
+ <logger category="sun.rmi">
+ <level name="WARN"/>
+ </logger>
+ <root-logger>
+ <level name="INFO"/>
+ <handlers>
+ <handler name="CONSOLE"/>
+ <handler name="FILE"/>
+ </handlers>
+ </root-logger>
+ <formatter name="PATTERN">
+ <pattern-formatter pattern="%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
+ </formatter>
+ <formatter name="COLOR-PATTERN">
+ <pattern-formatter pattern="%K{level}%d{HH:mm:ss,SSS} %-5p [%c] (%t) %s%e%n"/>
+ </formatter>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:batch-jberet:2.0">
+ <default-job-repository name="in-memory"/>
+ <default-thread-pool name="batch"/>
+ <job-repository name="in-memory">
+ <in-memory/>
+ </job-repository>
+ <thread-pool name="batch">
+ <max-threads count="10"/>
+ <keepalive-time time="30" unit="seconds"/>
+ </thread-pool>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:bean-validation:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:core-management:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:datasources:5.0">
+ <datasources>
+ <datasource jndi-name="java:jboss/datasources/ExampleDS" pool-name="ExampleDS" enabled="true" use-java-context="true">
+ <connection-url>jdbc:h2:mem:test;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE</connection-url>
+ <driver>h2</driver>
+ <security>
+ <user-name>sa</user-name>
+ <password>sa</password>
+ </security>
+ </datasource>
+ <drivers>
+ <driver name="h2" module="com.h2database.h2">
+ <xa-datasource-class>org.h2.jdbcx.JdbcDataSource</xa-datasource-class>
+ </driver>
+ </drivers>
+ </datasources>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:deployment-scanner:2.0">
+ <deployment-scanner path="deployments" relative-to="jboss.server.base.dir" scan-interval="5000" runtime-failure-causes-rollback="${jboss.deployment.scanner.rollback.on.failure:false}"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ee:4.0">
+ <spec-descriptor-property-replacement>false</spec-descriptor-property-replacement>
+ <concurrent>
+ <context-services>
+ <context-service name="default" jndi-name="java:jboss/ee/concurrency/context/default" use-transaction-setup-provider="true"/>
+ </context-services>
+ <managed-thread-factories>
+ <managed-thread-factory name="default" jndi-name="java:jboss/ee/concurrency/factory/default" context-service="default"/>
+ </managed-thread-factories>
+ <managed-executor-services>
+ <managed-executor-service name="default" jndi-name="java:jboss/ee/concurrency/executor/default" context-service="default" hung-task-threshold="60000" keepalive-time="5000"/>
+ </managed-executor-services>
+ <managed-scheduled-executor-services>
+ <managed-scheduled-executor-service name="default" jndi-name="java:jboss/ee/concurrency/scheduler/default" context-service="default" hung-task-threshold="60000" keepalive-time="3000"/>
+ </managed-scheduled-executor-services>
+ </concurrent>
+ <default-bindings context-service="java:jboss/ee/concurrency/context/default" datasource="java:jboss/datasources/ExampleDS" managed-executor-service="java:jboss/ee/concurrency/executor/default" managed-scheduled-executor-service="java:jboss/ee/concurrency/scheduler/default" managed-thread-factory="java:jboss/ee/concurrency/factory/default"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:ejb3:5.0">
+ <session-bean>
+ <stateless>
+ <bean-instance-pool-ref pool-name="slsb-strict-max-pool"/>
+ </stateless>
+ <stateful default-access-timeout="5000" cache-ref="simple" passivation-disabled-cache-ref="simple"/>
+ <singleton default-access-timeout="5000"/>
+ </session-bean>
+ <pools>
+ <bean-instance-pools>
+ <strict-max-pool name="slsb-strict-max-pool" derive-size="from-worker-pools" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ <strict-max-pool name="mdb-strict-max-pool" derive-size="from-cpu-count" instance-acquisition-timeout="5" instance-acquisition-timeout-unit="MINUTES"/>
+ </bean-instance-pools>
+ </pools>
+ <caches>
+ <cache name="simple"/>
+ <cache name="distributable" passivation-store-ref="infinispan" aliases="passivating clustered"/>
+ </caches>
+ <passivation-stores>
+ <passivation-store name="infinispan" cache-container="ejb" max-size="10000"/>
+ </passivation-stores>
+ <async thread-pool-name="default"/>
+ <timer-service thread-pool-name="default" default-data-store="default-file-store">
+ <data-stores>
+ <file-data-store name="default-file-store" path="timer-service-data" relative-to="jboss.server.data.dir"/>
+ </data-stores>
+ </timer-service>
+ <remote connector-ref="http-remoting-connector" thread-pool-name="default">
+ <channel-creation-options>
+ <option name="READ_TIMEOUT" value="${prop.remoting-connector.read.timeout:20}" type="xnio"/>
+ <option name="MAX_OUTBOUND_MESSAGES" value="1234" type="remoting"/>
+ </channel-creation-options>
+ </remote>
+ <thread-pools>
+ <thread-pool name="default">
+ <max-threads count="10"/>
+ <keepalive-time time="100" unit="milliseconds"/>
+ </thread-pool>
+ </thread-pools>
+ <default-security-domain value="other"/>
+ <default-missing-method-permissions-deny-access value="true"/>
+ <log-system-exceptions value="true"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:io:1.1">
+ <worker name="default"/>
+ <buffer-pool name="default"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:infinispan:4.0">
+ <cache-container name="server" default-cache="default" module="org.wildfly.clustering.server">
+ <local-cache name="default">
+ <transaction mode="BATCH"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="web" default-cache="passivation" module="org.wildfly.clustering.web.infinispan">
+ <local-cache name="passivation">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ <local-cache name="persistent">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="false" purge="false"/>
+ </local-cache>
+ <local-cache name="concurrent">
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="ejb" aliases="sfsb" default-cache="passivation" module="org.wildfly.clustering.ejb.infinispan">
+ <local-cache name="passivation">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="true" purge="false"/>
+ </local-cache>
+ <local-cache name="persistent">
+ <locking isolation="REPEATABLE_READ"/>
+ <transaction mode="BATCH"/>
+ <file-store passivation="false" purge="false"/>
+ </local-cache>
+ </cache-container>
+ <cache-container name="hibernate" module="org.hibernate.infinispan">
+ <local-cache name="entity">
+ <transaction mode="NON_XA"/>
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </local-cache>
+ <local-cache name="local-query">
+ <eviction strategy="LRU" max-entries="10000"/>
+ <expiration max-idle="100000"/>
+ </local-cache>
+ <local-cache name="timestamps"/>
+ </cache-container>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jaxrs:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jca:5.0">
+ <archive-validation enabled="true" fail-on-error="true" fail-on-warn="false"/>
+ <bean-validation enabled="true"/>
+ <default-workmanager>
+ <short-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </short-running-threads>
+ <long-running-threads>
+ <core-threads count="50"/>
+ <queue-length count="50"/>
+ <max-threads count="50"/>
+ <keepalive-time time="10" unit="seconds"/>
+ </long-running-threads>
+ </default-workmanager>
+ <cached-connection-manager/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jdr:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:jmx:1.3">
+ <expose-resolved-model/>
+ <expose-expression-model/>
+ <remoting-connector/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jpa:1.1">
+ <jpa default-datasource="" default-extended-persistence-inheritance="DEEP"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:jsf:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:mail:2.1">
+ <mail-session name="default" jndi-name="java:jboss/mail/Default">
+ <smtp-server outbound-socket-binding-ref="mail-smtp"/>
+ </mail-session>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:naming:2.0">
+ <remote-naming/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:pojo:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:remoting:4.0">
+ <endpoint/>
+ <http-connector name="http-remoting-connector" connector-ref="default" security-realm="ApplicationRealm"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:resource-adapters:5.0"/>
+ <subsystem xmlns="urn:jboss:domain:request-controller:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:sar:1.0"/>
+ <subsystem xmlns="urn:jboss:domain:security-manager:3.0">
+ <deployment-permissions>
+ <maximum-set>
+ <permission class="java.security.AllPermission"/>
+ </maximum-set>
+ </deployment-permissions>
+ </subsystem>
+ <subsystem xmlns="urn:wildfly:elytron:1.0" final-providers="combined-providers">
+ <providers>
+ <provider-loader name="elytron" module="org.wildfly.security.elytron"/>
+ <provider-loader name="openssl" module="org.wildfly.openssl"/>
+ <aggregate-providers name="combined-providers">
+ <providers name="elytron"/>
+ <providers name="openssl"/>
+ </aggregate-providers>
+ </providers>
+ <audit-logging>
+ <file-audit-log name="local-audit" path="audit.log" relative-to="jboss.server.log.dir" format="JSON"/>
+ </audit-logging>
+ <security-domains>
+ <security-domain name="ApplicationDomain" default-realm="ApplicationRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="ApplicationRealm" role-decoder="groups-to-roles"/>
+ <realm name="local"/>
+ </security-domain>
+ <security-domain name="ManagementDomain" default-realm="ManagementRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="ManagementRealm" role-decoder="groups-to-roles"/>
+ <realm name="local" role-mapper="super-user-mapper"/>
+ </security-domain>
+ <security-domain name="OAuth2Domain" default-realm="JwtRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="JwtRealm" role-decoder="groups-to-roles"/>
+ </security-domain>
+ </security-domains>
+ <security-realms>
+ <identity-realm name="local" identity="$local"/>
+ <properties-realm name="ApplicationRealm">
+ <users-properties path="application-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ApplicationRealm"/>
+ <groups-properties path="application-roles.properties" relative-to="jboss.server.config.dir"/>
+ </properties-realm>
+ <properties-realm name="ManagementRealm">
+ <users-properties path="mgmt-users.properties" relative-to="jboss.server.config.dir" digest-realm-name="ManagementRealm"/>
+ <groups-properties path="mgmt-groups.properties" relative-to="jboss.server.config.dir"/>
+ </properties-realm>
+ <token-realm name="JwtRealm" principal-claim="preferred_username">
+ <jwt/>
+ </token-realm>
+ </security-realms>
+ <mappers>
+ <constant-permission-mapper name="default-permission-mapper">
+ <permission class-name="org.wildfly.security.auth.permission.LoginPermission"/>
+ <permission class-name="org.wildfly.extension.batch.jberet.deployment.BatchPermission" module="org.wildfly.extension.batch.jberet" target-name="*"/>
+ <permission class-name="org.wildfly.transaction.client.RemoteTransactionPermission" module="org.wildfly.transaction.client"/>
+ <permission class-name="org.jboss.ejb.client.RemoteEJBPermission" module="org.jboss.ejb-client"/>
+ </constant-permission-mapper>
+ <constant-realm-mapper name="local" realm-name="local"/>
+ <simple-role-decoder name="groups-to-roles" attribute="groups"/>
+ <constant-role-mapper name="super-user-mapper">
+ <role name="SuperUser"/>
+ </constant-role-mapper>
+ </mappers>
+ <http>
+ <http-authentication-factory name="management-http-authentication" http-server-mechanism-factory="global" security-domain="ManagementDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="DIGEST">
+ <mechanism-realm realm-name="ManagementRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </http-authentication-factory>
+ <http-authentication-factory name="application-http-authentication" http-server-mechanism-factory="global" security-domain="ApplicationDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="BASIC">
+ <mechanism-realm realm-name="Application Realm"/>
+ </mechanism>
+ <mechanism mechanism-name="FORM"/>
+ </mechanism-configuration>
+ </http-authentication-factory>
+ <provider-http-server-mechanism-factory name="global"/>
+ </http>
+ <sasl>
+ <sasl-authentication-factory name="oauth2-sasl-authentication" sasl-server-factory="configured" security-domain="OAuth2Domain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="OAUTHBEARER"/>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <sasl-authentication-factory name="management-sasl-authentication" sasl-server-factory="configured" security-domain="ManagementDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
+ <mechanism mechanism-name="DIGEST-MD5">
+ <mechanism-realm realm-name="ManagementRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <sasl-authentication-factory name="application-sasl-authentication" sasl-server-factory="configured" security-domain="ApplicationDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="JBOSS-LOCAL-USER" realm-mapper="local"/>
+ <mechanism mechanism-name="DIGEST-MD5">
+ <mechanism-realm realm-name="ApplicationRealm"/>
+ </mechanism>
+ </mechanism-configuration>
+ </sasl-authentication-factory>
+ <provider-sasl-server-factory name="global"/>
+ <mechanism-provider-filtering-sasl-server-factory name="elytron" sasl-server-factory="global">
+ <filters>
+ <filter provider-name="WildFlyElytron"/>
+ </filters>
+ </mechanism-provider-filtering-sasl-server-factory>
+ <configurable-sasl-server-factory name="configured" sasl-server-factory="elytron">
+ <filters>
+ <filter>
+ <pattern-filter value="JBOSS-LOCAL-USER"/>
+ </filter>
+ <filter>
+ <pattern-filter value="DIGEST-MD5"/>
+ </filter>
+ <filter>
+ <pattern-filter value="OAUTHBEARER"/>
+ </filter>
+ </filters>
+ <properties>
+ <property name="wildfly.sasl.local-user.default-user" value="$local"/>
+ </properties>
+ </configurable-sasl-server-factory>
+ </sasl>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:security:3.0">
+ <security-domains>
+ <security-domain name="other" cache-type="default">
+ <authentication>
+ <login-module code="Remoting" flag="optional">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ <login-module code="RealmDirect" flag="required">
+ <module-option name="password-stacking" value="useFirstPass"/>
+ </login-module>
+ </authentication>
+ </security-domain>
+ <security-domain name="jboss-web-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ <security-domain name="jboss-ejb-policy" cache-type="default">
+ <authorization>
+ <policy-module code="Delegating" flag="required"/>
+ </authorization>
+ </security-domain>
+ <security-domain name="jaspitest" cache-type="default">
+ <authentication-jaspi>
+ <login-module-stack name="dummy">
+ <login-module code="Dummy" flag="optional"/>
+ </login-module-stack>
+ <auth-module code="Dummy"/>
+ </authentication-jaspi>
+ </security-domain>
+ </security-domains>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:transactions:3.1">
+ <core-environment>
+ <process-id>
+ <uuid/>
+ </process-id>
+ </core-environment>
+ <recovery-environment socket-binding="txn-recovery-environment" status-socket-binding="txn-status-manager"/>
+ <object-store path="tx-object-store" relative-to="jboss.server.data.dir"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:undertow:4.0">
+ <buffer-cache name="default"/>
+ <server name="default-server">
+ <http-listener name="default" socket-binding="http" redirect-socket="https" enable-http2="true"/>
+ <https-listener name="https" socket-binding="https" security-realm="ApplicationRealm" enable-http2="true"/>
+ <host name="default-host" alias="localhost">
+ <location name="/" handler="welcome-content"/>
+ <filter-ref name="server-header"/>
+ <filter-ref name="x-powered-by-header"/>
+ <http-invoker http-authentication-factory="application-http-authentication"/>
+ </host>
+ </server>
+ <servlet-container name="default">
+ <jsp-config/>
+ <websockets/>
+ </servlet-container>
+ <handlers>
+ <file name="welcome-content" path="${jboss.home.dir}/welcome-content"/>
+ </handlers>
+ <filters>
+ <response-header name="server-header" header-name="Server" header-value="WildFly/10"/>
+ <response-header name="x-powered-by-header" header-name="X-Powered-By" header-value="Undertow/1"/>
+ </filters>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:webservices:2.0">
+ <wsdl-host>${jboss.bind.address:127.0.0.1}</wsdl-host>
+ <endpoint-config name="Standard-Endpoint-Config"/>
+ <endpoint-config name="Recording-Endpoint-Config">
+ <pre-handler-chain name="recording-handlers" protocol-bindings="##SOAP11_HTTP ##SOAP11_HTTP_MTOM ##SOAP12_HTTP ##SOAP12_HTTP_MTOM">
+ <handler name="RecordingHandler" class="org.jboss.ws.common.invocation.RecordingServerHandler"/>
+ </pre-handler-chain>
+ </endpoint-config>
+ <client-config name="Standard-Client-Config"/>
+ </subsystem>
+ <subsystem xmlns="urn:jboss:domain:weld:4.0"/>
+ </profile>
+ <interfaces>
+ <interface name="management">
+ <inet-address value="${jboss.bind.address.management:127.0.0.1}"/>
+ </interface>
+ <interface name="public">
+ <inet-address value="${jboss.bind.address:127.0.0.1}"/>
+ </interface>
+ </interfaces>
+ <socket-binding-group name="standard-sockets" default-interface="public" port-offset="${jboss.socket.binding.port-offset:0}">
+ <socket-binding name="management-http" interface="management" port="${jboss.management.http.port:9990}"/>
+ <socket-binding name="management-https" interface="management" port="${jboss.management.https.port:9993}"/>
+ <socket-binding name="ajp" port="${jboss.ajp.port:8009}"/>
+ <socket-binding name="http" port="${jboss.http.port:8080}"/>
+ <socket-binding name="https" port="${jboss.https.port:8443}"/>
+ <socket-binding name="txn-recovery-environment" port="4712"/>
+ <socket-binding name="txn-status-manager" port="4713"/>
+ <outbound-socket-binding name="mail-smtp">
+ <remote-destination host="localhost" port="25"/>
+ </outbound-socket-binding>
+ </socket-binding-group>
+</server>
\ No newline at end of file
elytron/wildfly-config.xml 71(+71 -0)
diff --git a/elytron/wildfly-config.xml b/elytron/wildfly-config.xml
new file mode 100644
index 0000000..7fcf544
--- /dev/null
+++ b/elytron/wildfly-config.xml
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<!--
+ ~ JBoss, Home of Professional Open Source.
+ ~ Copyright 2016 Red Hat, Inc., and individual contributors
+ ~ as indicated by the @author tags.
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+
+<configuration>
+ <authentication-client xmlns="urn:elytron:1.0">
+ <authentication-rules>
+ <rule use-configuration="default">
+ <match-host name="localhost"/>
+ <match-port number="9990"/>
+ </rule>
+ </authentication-rules>
+ <!--<ssl-context-rules>-->
+ <!--<rule use-ssl-context="oauth2-authorization-server-ssl">-->
+ <!--<match-host name="localhost"/>-->
+ <!--<match-port number="8543"/>-->
+ <!--</rule>-->
+ <!--</ssl-context-rules>-->
+ <!--<ssl-contexts>-->
+ <!--<ssl-context name="oauth2-authorization-server-ssl">-->
+ <!--<key-store-ssl-certificate key-store-name="default-keystore" alias="server"/>-->
+ <!--</ssl-context>-->
+ <!--</ssl-contexts>-->
+ <!--<key-stores>-->
+ <!--<key-store name="default-keystore" type="JKS">-->
+ <!--<file name="/pedroigor/development/workspace/jboss/keycloak/keycloak/distribution/demo-dist/target/keycloak-demo-3.1.0.CR1-SNAPSHOT/keycloak/standalone/configuration/application.truststore"/>-->
+ <!--<key-store-clear-password password="password"/>-->
+ <!--</key-store>-->
+ <!--</key-stores>-->
+ <authentication-configurations>
+ <configuration name="default">
+ <credentials>
+ <oauth2-bearer-token token-endpoint-uri="http://localhost:8180/auth/realms/wildfly/protocol/openid-connect/token">
+ <client-credentials client-id="wildfly-cli" client-secret="826e2750-dd70-4ff8-8fd0-05f6b2e871d1"/>
+ </oauth2-bearer-token>
+ </credentials>
+ <allow-sasl-mechanisms names="JBOSS-LOCAL-USER EXTERNAL DIGEST-MD5 PLAIN ANONYMOUS OAUTHBEARER"/>
+ <set-mechanism-properties>
+ <property key="wildfly.sasl.local-user.quiet-auth" value="true"/>
+ </set-mechanism-properties>
+ <use-service-loader-providers/>
+ </configuration>
+ <configuration name="using-client-credentials-from-callback-handler">
+ <credentials>
+ <oauth2-bearer-token token-endpoint-uri="http://localhost:8180/auth/realms/wildfly/protocol/openid-connect/token"/>
+ </credentials>
+ <allow-sasl-mechanisms names="JBOSS-LOCAL-USER EXTERNAL DIGEST-MD5 PLAIN ANONYMOUS OAUTHBEARER"/>
+ <set-mechanism-properties>
+ <property key="wildfly.sasl.local-user.quiet-auth" value="true"/>
+ </set-mechanism-properties>
+ <use-service-loader-providers/>
+ </configuration>
+ </authentication-configurations>
+ </authentication-client>
+</configuration>
pom.xml 22(+22 -0)
diff --git a/pom.xml b/pom.xml
index e545c8e..deb92ab 100755
--- a/pom.xml
+++ b/pom.xml
@@ -46,6 +46,8 @@
<eap.version>7.0.0.Beta</eap.version>
<jboss.as.version>7.2.0.Final</jboss.as.version>
<wildfly.version>10.0.0.Final</wildfly.version>
+ <version.org.wildfly.security.wildfly-elytron>1.1.0.Beta32</version.org.wildfly.security.wildfly-elytron>
+ <version.org.wildfly.security.elytron-web.undertow-server>1.0.0.Beta14</version.org.wildfly.security.elytron-web.undertow-server>
<aesh.version>0.66.12</aesh.version>
<apache.httpcomponents.version>4.5</apache.httpcomponents.version>
@@ -622,6 +624,16 @@
<version>${wildfly.version}</version>
</dependency>
<dependency>
+ <groupId>org.wildfly.security</groupId>
+ <artifactId>wildfly-elytron</artifactId>
+ <version>${version.org.wildfly.security.wildfly-elytron}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.wildfly.security.elytron-web</groupId>
+ <artifactId>undertow-server</artifactId>
+ <version>${version.org.wildfly.security.elytron-web.undertow-server}</version>
+ </dependency>
+ <dependency>
<groupId>org.infinispan</groupId>
<artifactId>infinispan-core</artifactId>
<version>${infinispan.version}</version>
@@ -923,6 +935,16 @@
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
+ <artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
+ <artifactId>keycloak-saml-wildfly-elytron-adapter</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>org.keycloak</groupId>
<artifactId>keycloak-wildfly-adduser</artifactId>
<version>${project.version}</version>
</dependency>
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/common/configure-elytron.xsl b/testsuite/integration-arquillian/servers/app-server/jboss/common/configure-elytron.xsl
new file mode 100644
index 0000000..96edcfe
--- /dev/null
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/common/configure-elytron.xsl
@@ -0,0 +1,90 @@
+<!--
+ ~ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ ~ * and other contributors as indicated by the @author tags.
+ ~ *
+ ~ * Licensed under the Apache License, Version 2.0 (the "License");
+ ~ * you may not use this file except in compliance with the License.
+ ~ * You may obtain a copy of the License at
+ ~ *
+ ~ * http://www.apache.org/licenses/LICENSE-2.0
+ ~ *
+ ~ * Unless required by applicable law or agreed to in writing, software
+ ~ * distributed under the License is distributed on an "AS IS" BASIS,
+ ~ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ * See the License for the specific language governing permissions and
+ ~ * limitations under the License.
+ -->
+
+<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
+
+ <xsl:variable name="undertowNamespace" select="'urn:jboss:domain:undertow:'"/>
+ <xsl:variable name="elytronNamespace" select="'urn:wildfly:elytron:'"/>
+ <xsl:variable name="securityNamespace" select="'urn:jboss:domain:security:'"/>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $elytronNamespace)]/*[local-name()='security-realms']">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | *"/>
+ <custom-realm name="KeycloakSAMLRealm" module="org.keycloak.keycloak-saml-wildfly-elytron-adapter" class-name="org.keycloak.adapters.saml.elytron.KeycloakSecurityRealm"/>
+ <custom-realm name="KeycloakOIDCRealm" module="org.keycloak.keycloak-wildfly-elytron-oidc-adapter" class-name="org.keycloak.adapters.elytron.KeycloakSecurityRealm"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $elytronNamespace)]/*[local-name()='security-domains']">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | *"/>
+ <security-domain name="KeycloakDomain" default-realm="KeycloakOIDCRealm" permission-mapper="default-permission-mapper" security-event-listener="local-audit">
+ <realm name="KeycloakOIDCRealm"/>
+ <realm name="KeycloakSAMLRealm"/>
+ </security-domain>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $elytronNamespace)]/*[local-name()='mappers']">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | *"/>
+ <constant-realm-mapper name="keycloak-saml-realm-mapper" realm-name="KeycloakSAMLRealm"/>
+ <constant-realm-mapper name="keycloak-oidc-realm-mapper" realm-name="KeycloakOIDCRealm"/>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $elytronNamespace)]/*[local-name()='http']">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | *"/>
+ <http-authentication-factory name="keycloak-http-authentication" http-server-mechanism-factory="keycloak-http-server-mechanism-factory" security-domain="KeycloakDomain">
+ <mechanism-configuration>
+ <mechanism mechanism-name="KEYCLOAK">
+ <mechanism-realm realm-name="KeycloakOIDCRealm" realm-mapper="keycloak-oidc-realm-mapper"/>
+ </mechanism>
+ <mechanism mechanism-name="KEYCLOAK-SAML">
+ <mechanism-realm realm-name="KeycloakSAMLRealm" realm-mapper="keycloak-saml-realm-mapper"/>
+ </mechanism>
+ </mechanism-configuration>
+ </http-authentication-factory>
+ <service-loader-http-server-mechanism-factory name="keycloak-oidc-http-server-mechanism-factory" module="org.keycloak.keycloak-wildfly-elytron-oidc-adapter"/>
+ <service-loader-http-server-mechanism-factory name="keycloak-saml-http-server-mechanism-factory" module="org.keycloak.keycloak-saml-wildfly-elytron-adapter"/>
+ <aggregate-http-server-mechanism-factory name="keycloak-http-server-mechanism-factory">
+ <http-server-mechanism-factory name="keycloak-oidc-http-server-mechanism-factory"/>
+ <http-server-mechanism-factory name="keycloak-saml-http-server-mechanism-factory"/>
+ </aggregate-http-server-mechanism-factory>
+ </xsl:copy>
+ </xsl:template>
+
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $undertowNamespace)]">
+ <xsl:copy>
+ <xsl:apply-templates select="@* | *"/>
+ <application-security-domains>
+ <application-security-domain name="other" http-authentication-factory="keycloak-http-authentication"/>
+ </application-security-domains>
+ </xsl:copy>
+ </xsl:template>
+
+ <!-- Need to remove the legacy security-domain otherwise Elytron will not be enabled to deployments -->
+ <xsl:template match="//*[local-name()='subsystem' and starts-with(namespace-uri(), $securityNamespace)]/*[local-name()='security-domains']/*[local-name()='security-domain'][@name='keycloak']"/>
+
+ <xsl:template match="@*|node()">
+ <xsl:copy>
+ <xsl:apply-templates select="@*|node()"/>
+ </xsl:copy>
+ </xsl:template>
+
+</xsl:stylesheet>
\ No newline at end of file
diff --git a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
index 658fb39..90a3952 100644
--- a/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
+++ b/testsuite/integration-arquillian/servers/app-server/jboss/pom.xml
@@ -444,6 +444,45 @@
<module>eap6-fuse</module>
</modules>
</profile>
+
+ <profile>
+ <id>app-server-wildfly-elytron</id>
+ <activation>
+ <activeByDefault>false</activeByDefault>
+ </activation>
+ <properties>
+ <wildfly.version>${elytron.wildfly.version}</wildfly.version>
+ </properties>
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>xml-maven-plugin</artifactId>
+ <executions>
+ <execution>
+ <id>configure-adapter-debug-log</id>
+ <phase>process-test-resources</phase>
+ <goals>
+ <goal>transform</goal>
+ </goals>
+ <configuration>
+ <transformationSets>
+ <transformationSet>
+ <dir>${app.server.jboss.home}/standalone/configuration</dir>
+ <includes>
+ <include>standalone.xml</include>
+ </includes>
+ <stylesheet>${common.resources}/configure-elytron.xsl</stylesheet>
+ <outputDir>${app.server.jboss.home}/standalone/configuration</outputDir>
+ </transformationSet>
+ </transformationSets>
+ </configuration>
+ </execution>
+ </executions>
+ </plugin>
+ </plugins>
+ </build>
+ </profile>
</profiles>
</project>