Merge pull request #283 from patriot1burke/master
adapter security context propagation
This commit is contained in:
commit
53f671e8af
32 changed files with 907 additions and 33 deletions
|
@ -38,6 +38,18 @@
|
||||||
</xsl:copy>
|
</xsl:copy>
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
|
<xsl:template match="node()[name(.)='security-domains']">
|
||||||
|
<xsl:copy>
|
||||||
|
<xsl:apply-templates select="node()[name(.)='security-domain']"/>
|
||||||
|
<security-domain name="keycloak">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule" flag="required"/>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
</xsl:copy>
|
||||||
|
</xsl:template>
|
||||||
|
|
||||||
|
|
||||||
<!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
|
<!-- for some reason, Wildfly 8 final decided to turn off management-native which means jboss-as-maven-plugin no
|
||||||
longer works -->
|
longer works -->
|
||||||
<xsl:template match="node()[name(.)='management-interfaces']">
|
<xsl:template match="node()[name(.)='management-interfaces']">
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
<excludes>
|
<excludes>
|
||||||
<exclude>org/keycloak/keycloak-undertow-adapter/**</exclude>
|
<exclude>org/keycloak/keycloak-undertow-adapter/**</exclude>
|
||||||
<exclude>org/keycloak/keycloak-wildfly-subsystem/**</exclude>
|
<exclude>org/keycloak/keycloak-wildfly-subsystem/**</exclude>
|
||||||
|
<exclude>org/keycloak/keycloak-wildfly-adapter/**</exclude>
|
||||||
</excludes>
|
</excludes>
|
||||||
<outputDirectory>modules</outputDirectory>
|
<outputDirectory>modules</outputDirectory>
|
||||||
</fileSet>
|
</fileSet>
|
||||||
|
|
|
@ -59,6 +59,10 @@
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-adapter-core"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
|
<module-def name="org.keycloak.keycloak-jboss-adapter-core">
|
||||||
|
<maven-resource group="org.keycloak" artifact="keycloak-jboss-adapter-core"/>
|
||||||
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-as7-adapter">
|
<module-def name="org.keycloak.keycloak-as7-adapter">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-as7-adapter"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-as7-adapter"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
@ -67,6 +71,10 @@
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-undertow-adapter"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
||||||
|
<module-def name="org.keycloak.keycloak-wildfly-adapter">
|
||||||
|
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-adapter"/>
|
||||||
|
</module-def>
|
||||||
|
|
||||||
<module-def name="org.keycloak.keycloak-wildfly-subsystem">
|
<module-def name="org.keycloak.keycloak-wildfly-subsystem">
|
||||||
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-subsystem"/>
|
<maven-resource group="org.keycloak" artifact="keycloak-wildfly-subsystem"/>
|
||||||
</module-def>
|
</module-def>
|
||||||
|
|
|
@ -49,6 +49,11 @@
|
||||||
<artifactId>keycloak-adapter-core</artifactId>
|
<artifactId>keycloak-adapter-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jboss-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-as7-adapter</artifactId>
|
<artifactId>keycloak-as7-adapter</artifactId>
|
||||||
|
@ -59,6 +64,11 @@
|
||||||
<artifactId>keycloak-undertow-adapter</artifactId>
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.keycloak</groupId>
|
<groupId>org.keycloak</groupId>
|
||||||
<artifactId>keycloak-wildfly-subsystem</artifactId>
|
<artifactId>keycloak-wildfly-subsystem</artifactId>
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ JBoss, Home of Professional Open Source.
|
||||||
|
~ Copyright 2010, Red Hat, Inc., and individual contributors
|
||||||
|
~ as indicated by the @author tags. See the copyright.txt file in the
|
||||||
|
~ distribution for a full listing of individual contributors.
|
||||||
|
~
|
||||||
|
~ This is free software; you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU Lesser General Public License as
|
||||||
|
~ published by the Free Software Foundation; either version 2.1 of
|
||||||
|
~ the License, or (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This software is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
~ Lesser General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Lesser General Public
|
||||||
|
~ License along with this software; if not, write to the Free
|
||||||
|
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-jboss-adapter-core">
|
||||||
|
<resources>
|
||||||
|
<!-- Insert resources here -->
|
||||||
|
</resources>
|
||||||
|
<dependencies>
|
||||||
|
<module name="javax.api"/>
|
||||||
|
<module name="org.jboss.logging"/>
|
||||||
|
<module name="org.picketbox"/>
|
||||||
|
<module name="org.keycloak.keycloak-adapter-core"/>
|
||||||
|
<module name="org.keycloak.keycloak-core"/>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</module>
|
|
@ -0,0 +1,46 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
~ JBoss, Home of Professional Open Source.
|
||||||
|
~ Copyright 2010, Red Hat, Inc., and individual contributors
|
||||||
|
~ as indicated by the @author tags. See the copyright.txt file in the
|
||||||
|
~ distribution for a full listing of individual contributors.
|
||||||
|
~
|
||||||
|
~ This is free software; you can redistribute it and/or modify it
|
||||||
|
~ under the terms of the GNU Lesser General Public License as
|
||||||
|
~ published by the Free Software Foundation; either version 2.1 of
|
||||||
|
~ the License, or (at your option) any later version.
|
||||||
|
~
|
||||||
|
~ This software is distributed in the hope that it will be useful,
|
||||||
|
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
~ Lesser General Public License for more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU Lesser General Public
|
||||||
|
~ License along with this software; if not, write to the Free
|
||||||
|
~ Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
|
||||||
|
~ 02110-1301 USA, or see the FSF site: http://www.fsf.org.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<module xmlns="urn:jboss:module:1.1" name="org.keycloak.keycloak-wildfly-adapter">
|
||||||
|
<resources>
|
||||||
|
<!-- Insert resources here -->
|
||||||
|
</resources>
|
||||||
|
<dependencies>
|
||||||
|
<module name="javax.api"/>
|
||||||
|
<module name="org.bouncycastle"/>
|
||||||
|
<module name="org.codehaus.jackson.jackson-core-asl"/>
|
||||||
|
<module name="org.codehaus.jackson.jackson-mapper-asl"/>
|
||||||
|
<module name="org.codehaus.jackson.jackson-xc"/>
|
||||||
|
<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-core"/>
|
||||||
|
<module name="org.keycloak.keycloak-core"/>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
</module>
|
|
@ -73,6 +73,66 @@ $ unzip keycloak-as7-adapter-dist.zip
|
||||||
]]>
|
]]>
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</para>
|
</para>
|
||||||
|
<para>
|
||||||
|
Finally, for both AS7, EAP 6.x, and Wildfly installations you must specify a shared keycloak security domain.
|
||||||
|
This security domain should be used with EJBs and other components when you need the security context created
|
||||||
|
in the secured web tier to be propagated to the EJBs (other EE component) you are invoking. Otherwise
|
||||||
|
this configuration is optional.
|
||||||
|
</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
<server xmlns="urn:jboss:domain:1.4">
|
||||||
|
<subsystem xmlns="urn:jboss:domain:security:1.2">
|
||||||
|
<security-domains>
|
||||||
|
...
|
||||||
|
<security-domain name="keycloak">
|
||||||
|
<authentication>
|
||||||
|
<login-module code="org.keycloak.adapters.jboss.KeycloakLoginModule"
|
||||||
|
flag="required"/>
|
||||||
|
</authentication>
|
||||||
|
</security-domain>
|
||||||
|
</security-domains>
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
For example, if you have a JAX-RS service that is an EJB within your WEB-INF/classes directory, you'll want
|
||||||
|
to annotate it with the @SecurityDomain annotation as follows:
|
||||||
|
</para>
|
||||||
|
<programlisting><![CDATA[
|
||||||
|
import org.jboss.ejb3.annotation.SecurityDomain;
|
||||||
|
import org.jboss.resteasy.annotations.cache.NoCache;
|
||||||
|
|
||||||
|
import javax.annotation.security.RolesAllowed;
|
||||||
|
import javax.ejb.EJB;
|
||||||
|
import javax.ejb.Stateless;
|
||||||
|
import javax.ws.rs.GET;
|
||||||
|
import javax.ws.rs.Path;
|
||||||
|
import javax.ws.rs.Produces;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Path("customers")
|
||||||
|
@Stateless
|
||||||
|
@SecurityDomain("keycloak")
|
||||||
|
public class CustomerService {
|
||||||
|
|
||||||
|
@EJB
|
||||||
|
CustomerDB db;
|
||||||
|
|
||||||
|
@GET
|
||||||
|
@Produces("application/json")
|
||||||
|
@NoCache
|
||||||
|
@RolesAllowed("db_user")
|
||||||
|
public List<String> getCustomers() {
|
||||||
|
return db.getCustomers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</programlisting>
|
||||||
|
<para>
|
||||||
|
We hope to improve our integration in the future so that you don't have to specify the @SecurityDomain
|
||||||
|
annotation when you want to propagate a keycloak security context to the EJB tier.
|
||||||
|
</para>
|
||||||
|
|
||||||
</section>
|
</section>
|
||||||
<section>
|
<section>
|
||||||
<title>Per WAR Configuration</title>
|
<title>Per WAR Configuration</title>
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
package org.keycloak.adapters;
|
||||||
|
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
import org.keycloak.representations.AccessToken;
|
||||||
|
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public interface KeycloakAccount {
|
||||||
|
Principal getPrincipal();
|
||||||
|
Set<String> getRoles();
|
||||||
|
KeycloakSecurityContext getKeycloakSecurityContext();
|
||||||
|
}
|
|
@ -48,6 +48,7 @@ public class KeycloakDeploymentBuilder {
|
||||||
deployment.setSslRequired(!adapterConfig.isSslNotRequired());
|
deployment.setSslRequired(!adapterConfig.isSslNotRequired());
|
||||||
deployment.setResourceCredentials(adapterConfig.getCredentials());
|
deployment.setResourceCredentials(adapterConfig.getCredentials());
|
||||||
deployment.setPublicClient(adapterConfig.isPublicClient());
|
deployment.setPublicClient(adapterConfig.isPublicClient());
|
||||||
|
deployment.setUseResourceRoleMappings(adapterConfig.isUseResourceRoleMappings());
|
||||||
|
|
||||||
if (adapterConfig.isBearerOnly()) {
|
if (adapterConfig.isBearerOnly()) {
|
||||||
deployment.setBearerOnly(true);
|
deployment.setBearerOnly(true);
|
||||||
|
|
|
@ -46,6 +46,10 @@ public class RefreshableKeycloakSecurityContext extends KeycloakSecurityContext
|
||||||
return this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
|
return this.token.isActive() && this.token.getIssuedAt() > deployment.getNotBefore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public KeycloakDeployment getDeployment() {
|
||||||
|
return deployment;
|
||||||
|
}
|
||||||
|
|
||||||
public void setDeployment(KeycloakDeployment deployment) {
|
public void setDeployment(KeycloakDeployment deployment) {
|
||||||
this.deployment = deployment;
|
this.deployment = deployment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ import org.keycloak.KeycloakPrincipal;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public abstract class RequestAuthenticator {
|
public abstract class RequestAuthenticator {
|
||||||
protected Logger log = Logger.getLogger(RequestAuthenticator.class);
|
protected static Logger log = Logger.getLogger(RequestAuthenticator.class);
|
||||||
|
|
||||||
protected HttpFacade facade;
|
protected HttpFacade facade;
|
||||||
protected KeycloakDeployment deployment;
|
protected KeycloakDeployment deployment;
|
||||||
|
@ -33,19 +33,25 @@ public abstract class RequestAuthenticator {
|
||||||
public AuthOutcome authenticate() {
|
public AuthOutcome authenticate() {
|
||||||
log.info("--> authenticate()");
|
log.info("--> authenticate()");
|
||||||
BearerTokenRequestAuthenticator bearer = createBearerTokenAuthenticator();
|
BearerTokenRequestAuthenticator bearer = createBearerTokenAuthenticator();
|
||||||
|
log.info("try bearer");
|
||||||
AuthOutcome outcome = bearer.authenticate(facade);
|
AuthOutcome outcome = bearer.authenticate(facade);
|
||||||
if (outcome == AuthOutcome.FAILED) {
|
if (outcome == AuthOutcome.FAILED) {
|
||||||
challenge = bearer.getChallenge();
|
challenge = bearer.getChallenge();
|
||||||
|
log.info("Bearer FAILED");
|
||||||
return AuthOutcome.FAILED;
|
return AuthOutcome.FAILED;
|
||||||
} else if (outcome == AuthOutcome.AUTHENTICATED) {
|
} else if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||||
completeAuthentication(bearer);
|
completeAuthentication(bearer);
|
||||||
|
log.info("Bearer AUTHENTICATED");
|
||||||
return AuthOutcome.AUTHENTICATED;
|
return AuthOutcome.AUTHENTICATED;
|
||||||
} else if (deployment.isBearerOnly()) {
|
} else if (deployment.isBearerOnly()) {
|
||||||
challenge = bearer.getChallenge();
|
challenge = bearer.getChallenge();
|
||||||
|
log.info("NOT_ATTEMPTED: bearer only");
|
||||||
return AuthOutcome.NOT_ATTEMPTED;
|
return AuthOutcome.NOT_ATTEMPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
log.info("try oauth");
|
||||||
if (isCached()) {
|
if (isCached()) {
|
||||||
|
log.info("AUTHENTICATED: was cached");
|
||||||
return AuthOutcome.AUTHENTICATED;
|
return AuthOutcome.AUTHENTICATED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -36,6 +36,7 @@ public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
|
||||||
|
|
||||||
private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-as7-adapter");
|
private static final ModuleIdentifier KEYCLOAK_AS7_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-as7-adapter");
|
||||||
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
|
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
|
||||||
|
private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
|
||||||
private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
|
private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
|
||||||
//private static final ModuleIdentifier APACHE_HTTPCOMPONENTS = ModuleIdentifier.create("org.apache.httpcomponents");
|
//private static final ModuleIdentifier APACHE_HTTPCOMPONENTS = ModuleIdentifier.create("org.apache.httpcomponents");
|
||||||
|
|
||||||
|
@ -51,6 +52,7 @@ public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
|
||||||
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
||||||
|
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_AS7_ADAPTER, false, false, true, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_AS7_ADAPTER, false, false, true, false));
|
||||||
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
|
||||||
//moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE_HTTPCOMPONENTS, false, false, true, false));
|
//moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE_HTTPCOMPONENTS, false, false, true, false));
|
||||||
|
|
|
@ -23,6 +23,11 @@
|
||||||
<artifactId>keycloak-adapter-core</artifactId>
|
<artifactId>keycloak-adapter-core</artifactId>
|
||||||
<version>${project.version}</version>
|
<version>${project.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jboss-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.apache.httpcomponents</groupId>
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
<artifactId>httpclient</artifactId>
|
<artifactId>httpclient</artifactId>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import org.apache.catalina.Session;
|
||||||
import org.apache.catalina.authenticator.Constants;
|
import org.apache.catalina.authenticator.Constants;
|
||||||
import org.apache.catalina.connector.Request;
|
import org.apache.catalina.connector.Request;
|
||||||
import org.apache.catalina.realm.GenericPrincipal;
|
import org.apache.catalina.realm.GenericPrincipal;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
@ -22,6 +23,7 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
||||||
|
private static final Logger log = Logger.getLogger(CatalinaRequestAuthenticator.class);
|
||||||
protected KeycloakAuthenticatorValve valve;
|
protected KeycloakAuthenticatorValve valve;
|
||||||
protected CatalinaUserSessionManagement userSessionManagement;
|
protected CatalinaUserSessionManagement userSessionManagement;
|
||||||
protected Request request;
|
protected Request request;
|
||||||
|
@ -53,7 +55,7 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
||||||
@Override
|
@Override
|
||||||
protected void completeOAuthAuthentication(KeycloakPrincipal skp, RefreshableKeycloakSecurityContext securityContext) {
|
protected void completeOAuthAuthentication(KeycloakPrincipal skp, RefreshableKeycloakSecurityContext securityContext) {
|
||||||
Set<String> roles = getRolesFromToken(securityContext);
|
Set<String> roles = getRolesFromToken(securityContext);
|
||||||
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles);
|
GenericPrincipal principal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), skp, roles, securityContext);
|
||||||
Session session = request.getSessionInternal(true);
|
Session session = request.getSessionInternal(true);
|
||||||
session.setPrincipal(principal);
|
session.setPrincipal(principal);
|
||||||
session.setAuthType("OAUTH");
|
session.setAuthType("OAUTH");
|
||||||
|
@ -66,7 +68,10 @@ public class CatalinaRequestAuthenticator extends RequestAuthenticator {
|
||||||
@Override
|
@Override
|
||||||
protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext securityContext) {
|
protected void completeBearerAuthentication(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext securityContext) {
|
||||||
Set<String> roles = getRolesFromToken(securityContext);
|
Set<String> roles = getRolesFromToken(securityContext);
|
||||||
Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles);
|
for (String role : roles) {
|
||||||
|
log.info("Bearer role: " + role);
|
||||||
|
}
|
||||||
|
Principal generalPrincipal = new CatalinaSecurityContextHelper().createPrincipal(request.getContext().getRealm(), principal, roles, securityContext);
|
||||||
request.setUserPrincipal(generalPrincipal);
|
request.setUserPrincipal(generalPrincipal);
|
||||||
request.setAuthType("KEYCLOAK");
|
request.setAuthType("KEYCLOAK");
|
||||||
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
request.setAttribute(KeycloakSecurityContext.class.getName(), securityContext);
|
||||||
|
|
|
@ -9,6 +9,8 @@ import org.jboss.security.SecurityContext;
|
||||||
import org.jboss.security.SecurityContextAssociation;
|
import org.jboss.security.SecurityContextAssociation;
|
||||||
import org.jboss.security.SimpleGroup;
|
import org.jboss.security.SimpleGroup;
|
||||||
import org.jboss.security.SimplePrincipal;
|
import org.jboss.security.SimplePrincipal;
|
||||||
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
|
|
||||||
import javax.security.auth.Subject;
|
import javax.security.auth.Subject;
|
||||||
import java.security.Principal;
|
import java.security.Principal;
|
||||||
|
@ -25,9 +27,24 @@ import java.util.Set;
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class CatalinaSecurityContextHelper {
|
public class CatalinaSecurityContextHelper {
|
||||||
public GenericPrincipal createPrincipal(Realm realm, Principal identity, Collection<String> roleSet) {
|
public GenericPrincipal createPrincipal(Realm realm, final Principal identity, final Set<String> roleSet, final KeycloakSecurityContext securityContext) {
|
||||||
|
KeycloakAccount account = new KeycloakAccount() {
|
||||||
|
@Override
|
||||||
|
public Principal getPrincipal() {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<String> getRoles() {
|
||||||
|
return roleSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public KeycloakSecurityContext getKeycloakSecurityContext() {
|
||||||
|
return securityContext;
|
||||||
|
}
|
||||||
|
};
|
||||||
Subject subject = new Subject();
|
Subject subject = new Subject();
|
||||||
String credentials = "";
|
|
||||||
Set<Principal> principals = subject.getPrincipals();
|
Set<Principal> principals = subject.getPrincipals();
|
||||||
principals.add(identity);
|
principals.add(identity);
|
||||||
Group[] roleSets = getRoleSets(roleSet);
|
Group[] roleSets = getRoleSets(roleSet);
|
||||||
|
@ -56,11 +73,11 @@ public class CatalinaSecurityContextHelper {
|
||||||
principals.add(callerGroup);
|
principals.add(callerGroup);
|
||||||
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
|
SecurityContext sc = SecurityContextAssociation.getSecurityContext();
|
||||||
Principal userPrincipal = getPrincipal(subject);
|
Principal userPrincipal = getPrincipal(subject);
|
||||||
sc.getUtil().createSubjectInfo(userPrincipal, credentials, subject);
|
sc.getUtil().createSubjectInfo(userPrincipal, account, subject);
|
||||||
List<String> rolesAsStringList = new ArrayList<String>();
|
List<String> rolesAsStringList = new ArrayList<String>();
|
||||||
rolesAsStringList.addAll(roleSet);
|
rolesAsStringList.addAll(roleSet);
|
||||||
return new JBossGenericPrincipal(realm, userPrincipal.getName(), null, rolesAsStringList,
|
return new JBossGenericPrincipal(realm, userPrincipal.getName(), null, rolesAsStringList,
|
||||||
userPrincipal, null, credentials, null, subject);
|
userPrincipal, null, account, null, subject);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
83
integration/jboss-adapter-core/pom.xml
Executable file
83
integration/jboss-adapter-core/pom.xml
Executable file
|
@ -0,0 +1,83 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project>
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.0-alpha-3-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-jboss-adapter-core</artifactId>
|
||||||
|
<name>Common JBoss/Wildfly Core Classes</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<version>3.1.2.GA</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${keycloak.apache.httpcomponents.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.iharder</groupId>
|
||||||
|
<artifactId>base64</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-core-asl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-xc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.spec.javax.servlet</groupId>
|
||||||
|
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketbox</groupId>
|
||||||
|
<artifactId>picketbox</artifactId>
|
||||||
|
<version>4.0.20.Final</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
</dependencies>
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
|
<configuration>
|
||||||
|
<source>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,100 @@
|
||||||
|
package org.keycloak.adapters.jboss;
|
||||||
|
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.security.SimpleGroup;
|
||||||
|
import org.jboss.security.SimplePrincipal;
|
||||||
|
import org.jboss.security.auth.callback.ObjectCallback;
|
||||||
|
import org.jboss.security.auth.spi.AbstractServerLoginModule;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
|
|
||||||
|
import javax.security.auth.callback.Callback;
|
||||||
|
import javax.security.auth.callback.NameCallback;
|
||||||
|
import javax.security.auth.callback.UnsupportedCallbackException;
|
||||||
|
import javax.security.auth.login.LoginException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.acl.Group;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class KeycloakLoginModule extends AbstractServerLoginModule {
|
||||||
|
protected static Logger log = Logger.getLogger(KeycloakLoginModule.class);
|
||||||
|
protected Set<String> roleSet;
|
||||||
|
protected Principal identity;
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public boolean login() throws LoginException {
|
||||||
|
log.info("KeycloakLoginModule.login()");
|
||||||
|
if (super.login() == true) {
|
||||||
|
log.info("super.login()==true");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Object credential = getCredential();
|
||||||
|
if (credential != null && (credential instanceof KeycloakAccount)) {
|
||||||
|
log.info("Found Account");
|
||||||
|
KeycloakAccount account = (KeycloakAccount)credential;
|
||||||
|
roleSet = account.getRoles();
|
||||||
|
identity = account.getPrincipal();
|
||||||
|
sharedState.put("javax.security.auth.login.name", identity);
|
||||||
|
sharedState.put("javax.security.auth.login.password", credential);
|
||||||
|
loginOk = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// We return false to allow the next module to attempt authentication, maybe a
|
||||||
|
// username and password has been supplied to a web auth.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Principal getIdentity() {
|
||||||
|
return identity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
@Override
|
||||||
|
protected Group[] getRoleSets() throws LoginException {
|
||||||
|
return new Group[0];
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Group[] getRoleSets() throws LoginException {
|
||||||
|
//log.info("getRoleSets");
|
||||||
|
SimpleGroup roles = new SimpleGroup("Roles");
|
||||||
|
Group[] roleSets = {roles};
|
||||||
|
for (String role : roleSet) {
|
||||||
|
//log.info(" adding role: " + role);
|
||||||
|
roles.addMember(new SimplePrincipal(role));
|
||||||
|
}
|
||||||
|
return roleSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Object getCredential() throws LoginException {
|
||||||
|
NameCallback nc = new NameCallback("Alias: ");
|
||||||
|
ObjectCallback oc = new ObjectCallback("Credential: ");
|
||||||
|
Callback[] callbacks = { nc, oc };
|
||||||
|
|
||||||
|
try {
|
||||||
|
callbackHandler.handle(callbacks);
|
||||||
|
|
||||||
|
return oc.getCredential();
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
LoginException le = new LoginException();
|
||||||
|
le.initCause(ioe);
|
||||||
|
throw le;
|
||||||
|
} catch (UnsupportedCallbackException uce) {
|
||||||
|
LoginException le = new LoginException();
|
||||||
|
le.initCause(uce);
|
||||||
|
throw le;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -18,8 +18,10 @@
|
||||||
<module>adapter-core</module>
|
<module>adapter-core</module>
|
||||||
<module>jaxrs-oauth-client</module>
|
<module>jaxrs-oauth-client</module>
|
||||||
<module>servlet-oauth-client</module>
|
<module>servlet-oauth-client</module>
|
||||||
|
<module>jboss-adapter-core</module>
|
||||||
<module>as7-eap6/adapter</module>
|
<module>as7-eap6/adapter</module>
|
||||||
<module>undertow</module>
|
<module>undertow</module>
|
||||||
|
<module>wildfly-adapter</module>
|
||||||
<module>wildfly-subsystem</module>
|
<module>wildfly-subsystem</module>
|
||||||
<module>as7-eap-subsystem</module>
|
<module>as7-eap-subsystem</module>
|
||||||
<module>js</module>
|
<module>js</module>
|
||||||
|
|
|
@ -65,7 +65,7 @@ public class KeycloakServletExtension implements ServletExtension {
|
||||||
if (is == null) throw new RuntimeException("Unable to find realm config in /WEB-INF/keycloak.json or in keycloak subsystem.");
|
if (is == null) throw new RuntimeException("Unable to find realm config in /WEB-INF/keycloak.json or in keycloak subsystem.");
|
||||||
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
|
KeycloakDeployment deployment = KeycloakDeploymentBuilder.build(is);
|
||||||
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement(deployment);
|
UndertowUserSessionManagement userSessionManagement = new UndertowUserSessionManagement(deployment);
|
||||||
final ServletKeycloakAuthMech mech = new ServletKeycloakAuthMech(deployment, userSessionManagement, deploymentInfo.getConfidentialPortManager());
|
final ServletKeycloakAuthMech mech = createAuthenticationMechanism(deploymentInfo, deployment, userSessionManagement);
|
||||||
|
|
||||||
UndertowAuthenticatedActionsHandler.Wrapper actions = new UndertowAuthenticatedActionsHandler.Wrapper(deployment);
|
UndertowAuthenticatedActionsHandler.Wrapper actions = new UndertowAuthenticatedActionsHandler.Wrapper(deployment);
|
||||||
|
|
||||||
|
@ -102,4 +102,9 @@ public class KeycloakServletExtension implements ServletExtension {
|
||||||
cookieConfig.setPath(deploymentInfo.getContextPath());
|
cookieConfig.setPath(deploymentInfo.getContextPath());
|
||||||
deploymentInfo.setServletSessionConfig(cookieConfig);
|
deploymentInfo.setServletSessionConfig(cookieConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ServletKeycloakAuthMech createAuthenticationMechanism(DeploymentInfo deploymentInfo, KeycloakDeployment deployment, UndertowUserSessionManagement userSessionManagement) {
|
||||||
|
log.info("creating ServletKeycloakAuthMech");
|
||||||
|
return new ServletKeycloakAuthMech(deployment, userSessionManagement, deploymentInfo.getConfidentialPortManager());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.adapters.undertow;
|
||||||
import io.undertow.security.idm.Account;
|
import io.undertow.security.idm.Account;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
import org.keycloak.representations.AccessToken;
|
import org.keycloak.representations.AccessToken;
|
||||||
|
@ -16,7 +17,7 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class KeycloakUndertowAccount implements Account, Serializable {
|
public class KeycloakUndertowAccount implements Account, Serializable, KeycloakAccount {
|
||||||
protected static Logger log = Logger.getLogger(KeycloakUndertowAccount.class);
|
protected static Logger log = Logger.getLogger(KeycloakUndertowAccount.class);
|
||||||
protected RefreshableKeycloakSecurityContext session;
|
protected RefreshableKeycloakSecurityContext session;
|
||||||
protected KeycloakPrincipal principal;
|
protected KeycloakPrincipal principal;
|
||||||
|
@ -25,19 +26,27 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
||||||
public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session, KeycloakDeployment deployment) {
|
public KeycloakUndertowAccount(KeycloakPrincipal principal, RefreshableKeycloakSecurityContext session, KeycloakDeployment deployment) {
|
||||||
this.principal = principal;
|
this.principal = principal;
|
||||||
this.session = session;
|
this.session = session;
|
||||||
setRoles(session.getToken(), deployment);
|
setRoles(session.getToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setRoles(AccessToken accessToken, KeycloakDeployment deployment) {
|
protected void setRoles(AccessToken accessToken) {
|
||||||
Set<String> roles = null;
|
Set<String> roles = null;
|
||||||
if (deployment.isUseResourceRoleMappings()) {
|
if (session.getDeployment().isUseResourceRoleMappings()) {
|
||||||
AccessToken.Access access = accessToken.getResourceAccess(deployment.getResourceName());
|
log.info("useResourceRoleMappings");
|
||||||
|
AccessToken.Access access = accessToken.getResourceAccess(session.getDeployment().getResourceName());
|
||||||
if (access != null) roles = access.getRoles();
|
if (access != null) roles = access.getRoles();
|
||||||
} else {
|
} else {
|
||||||
|
log.info("use realm role mappings");
|
||||||
AccessToken.Access access = accessToken.getRealmAccess();
|
AccessToken.Access access = accessToken.getRealmAccess();
|
||||||
if (access != null) roles = access.getRoles();
|
if (access != null) roles = access.getRoles();
|
||||||
}
|
}
|
||||||
if (roles == null) roles = Collections.emptySet();
|
if (roles == null) roles = Collections.emptySet();
|
||||||
|
/*
|
||||||
|
log.info("Setting roles: ");
|
||||||
|
for (String role : roles) {
|
||||||
|
log.info(" role: " + role);
|
||||||
|
}
|
||||||
|
*/
|
||||||
this.accountRoles = roles;
|
this.accountRoles = roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,22 +60,17 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
||||||
return accountRoles;
|
return accountRoles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AccessToken getAccessToken() {
|
@Override
|
||||||
return session.getToken();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEncodedAccessToken() {
|
|
||||||
return session.getTokenString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
|
public RefreshableKeycloakSecurityContext getKeycloakSecurityContext() {
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isActive(KeycloakDeployment deployment) {
|
public void setDeployment(KeycloakDeployment deployment) {
|
||||||
// this object may have been serialized, so we need to reset realm config/metadata
|
|
||||||
session.setDeployment(deployment);
|
session.setDeployment(deployment);
|
||||||
log.info("realmConfig notBefore: " + deployment.getNotBefore());
|
}
|
||||||
|
|
||||||
|
public boolean isActive() {
|
||||||
|
// this object may have been serialized, so we need to reset realm config/metadata
|
||||||
if (session.isActive()) {
|
if (session.isActive()) {
|
||||||
log.info("session is active");
|
log.info("session is active");
|
||||||
return true;
|
return true;
|
||||||
|
@ -81,7 +85,7 @@ public class KeycloakUndertowAccount implements Account, Serializable {
|
||||||
}
|
}
|
||||||
log.info("refresh succeeded");
|
log.info("refresh succeeded");
|
||||||
|
|
||||||
setRoles(session.getToken(), deployment);
|
setRoles(session.getToken());
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,8 +29,7 @@ public class ServletKeycloakAuthMech implements AuthenticationMechanism {
|
||||||
@Override
|
@Override
|
||||||
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
public AuthenticationMechanismOutcome authenticate(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||||
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
UndertowHttpFacade facade = new UndertowHttpFacade(exchange);
|
||||||
ServletRequestAuthenticator authenticator = new ServletRequestAuthenticator(facade, deployment,
|
ServletRequestAuthenticator authenticator = createRequestAuthenticator(exchange, securityContext, facade);
|
||||||
portManager.getConfidentialPort(exchange), securityContext, exchange, userSessionManagement);
|
|
||||||
AuthOutcome outcome = authenticator.authenticate();
|
AuthOutcome outcome = authenticator.authenticate();
|
||||||
if (outcome == AuthOutcome.AUTHENTICATED) {
|
if (outcome == AuthOutcome.AUTHENTICATED) {
|
||||||
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
return AuthenticationMechanismOutcome.AUTHENTICATED;
|
||||||
|
@ -46,6 +45,11 @@ public class ServletKeycloakAuthMech implements AuthenticationMechanism {
|
||||||
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
return AuthenticationMechanismOutcome.NOT_ATTEMPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ServletRequestAuthenticator createRequestAuthenticator(HttpServerExchange exchange, SecurityContext securityContext, UndertowHttpFacade facade) {
|
||||||
|
return new ServletRequestAuthenticator(facade, deployment,
|
||||||
|
portManager.getConfidentialPort(exchange), securityContext, exchange, userSessionManagement);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
public ChallengeResult sendChallenge(HttpServerExchange exchange, SecurityContext securityContext) {
|
||||||
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
AuthChallenge challenge = exchange.getAttachment(KEYCLOAK_CHALLENGE_ATTACHMENT_KEY);
|
||||||
|
|
|
@ -5,6 +5,7 @@ import io.undertow.server.HttpServerExchange;
|
||||||
import io.undertow.servlet.handlers.ServletRequestContext;
|
import io.undertow.servlet.handlers.ServletRequestContext;
|
||||||
import org.keycloak.KeycloakSecurityContext;
|
import org.keycloak.KeycloakSecurityContext;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -39,7 +40,8 @@ public class ServletRequestAuthenticator extends UndertowRequestAuthenticator {
|
||||||
log.info("Account was not in session, returning null");
|
log.info("Account was not in session, returning null");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (account.isActive(deployment)) {
|
account.setDeployment(deployment);
|
||||||
|
if (account.isActive()) {
|
||||||
log.info("Cached account found");
|
log.info("Cached account found");
|
||||||
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
securityContext.authenticationComplete(account, "KEYCLOAK", false);
|
||||||
propagateKeycloakContext( account);
|
propagateKeycloakContext( account);
|
||||||
|
@ -59,7 +61,7 @@ public class ServletRequestAuthenticator extends UndertowRequestAuthenticator {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void login(KeycloakUndertowAccount account) {
|
protected void login(KeycloakAccount account) {
|
||||||
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
final ServletRequestContext servletRequestContext = exchange.getAttachment(ServletRequestContext.ATTACHMENT_KEY);
|
||||||
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
HttpServletRequest req = (HttpServletRequest) servletRequestContext.getServletRequest();
|
||||||
HttpSession session = req.getSession(true);
|
HttpSession session = req.getSession(true);
|
||||||
|
|
|
@ -4,6 +4,7 @@ import io.undertow.security.api.SecurityContext;
|
||||||
import io.undertow.server.HttpServerExchange;
|
import io.undertow.server.HttpServerExchange;
|
||||||
import org.keycloak.KeycloakPrincipal;
|
import org.keycloak.KeycloakPrincipal;
|
||||||
import org.keycloak.adapters.HttpFacade;
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
import org.keycloak.adapters.KeycloakDeployment;
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
import org.keycloak.adapters.OAuthRequestAuthenticator;
|
||||||
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
import org.keycloak.adapters.RefreshableKeycloakSecurityContext;
|
||||||
|
@ -46,7 +47,7 @@ public class UndertowRequestAuthenticator extends RequestAuthenticator {
|
||||||
login(account);
|
login(account);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void login(KeycloakUndertowAccount account) {
|
protected void login(KeycloakAccount account) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
108
integration/wildfly-adapter/pom.xml
Executable file
108
integration/wildfly-adapter/pom.xml
Executable file
|
@ -0,0 +1,108 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<project>
|
||||||
|
<parent>
|
||||||
|
<artifactId>keycloak-parent</artifactId>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<version>1.0-alpha-3-SNAPSHOT</version>
|
||||||
|
<relativePath>../../pom.xml</relativePath>
|
||||||
|
</parent>
|
||||||
|
<modelVersion>4.0.0</modelVersion>
|
||||||
|
|
||||||
|
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||||
|
<name>Keycloak Wildfly Integration</name>
|
||||||
|
<description/>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.jboss.logging</groupId>
|
||||||
|
<artifactId>jboss-logging</artifactId>
|
||||||
|
<version>3.1.2.GA</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-undertow-adapter</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.keycloak</groupId>
|
||||||
|
<artifactId>keycloak-jboss-adapter-core</artifactId>
|
||||||
|
<version>${project.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.apache.httpcomponents</groupId>
|
||||||
|
<artifactId>httpclient</artifactId>
|
||||||
|
<version>${keycloak.apache.httpcomponents.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.iharder</groupId>
|
||||||
|
<artifactId>base64</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.bouncycastle</groupId>
|
||||||
|
<artifactId>bcprov-jdk16</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-core-asl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-mapper-asl</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.codehaus.jackson</groupId>
|
||||||
|
<artifactId>jackson-xc</artifactId>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.picketbox</groupId>
|
||||||
|
<artifactId>picketbox</artifactId>
|
||||||
|
<version>4.0.20.Final</version>
|
||||||
|
<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>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-servlet</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.undertow</groupId>
|
||||||
|
<artifactId>undertow-core</artifactId>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</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>1.6</source>
|
||||||
|
<target>1.6</target>
|
||||||
|
</configuration>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
|
||||||
|
</project>
|
|
@ -0,0 +1,116 @@
|
||||||
|
package org.keycloak.adapters.wildfly;
|
||||||
|
|
||||||
|
import org.jboss.security.NestableGroup;
|
||||||
|
import org.jboss.security.SecurityConstants;
|
||||||
|
import org.jboss.security.SecurityContextAssociation;
|
||||||
|
import org.jboss.security.SimpleGroup;
|
||||||
|
import org.jboss.security.SimplePrincipal;
|
||||||
|
import org.keycloak.adapters.KeycloakAccount;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.acl.Group;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class SecurityInfoHelper {
|
||||||
|
public static void propagateSessionInfo(KeycloakAccount account) {
|
||||||
|
Subject subject = new Subject();
|
||||||
|
Set<Principal> principals = subject.getPrincipals();
|
||||||
|
principals.add(account.getPrincipal());
|
||||||
|
Group[] roleSets = getRoleSets(account.getRoles());
|
||||||
|
for (int g = 0; g < roleSets.length; g++) {
|
||||||
|
Group group = roleSets[g];
|
||||||
|
String name = group.getName();
|
||||||
|
Group subjectGroup = createGroup(name, principals);
|
||||||
|
if (subjectGroup instanceof NestableGroup) {
|
||||||
|
/* A NestableGroup only allows Groups to be added to it so we
|
||||||
|
need to add a SimpleGroup to subjectRoles to contain the roles
|
||||||
|
*/
|
||||||
|
SimpleGroup tmp = new SimpleGroup("Roles");
|
||||||
|
subjectGroup.addMember(tmp);
|
||||||
|
subjectGroup = tmp;
|
||||||
|
}
|
||||||
|
// Copy the group members to the Subject group
|
||||||
|
Enumeration<? extends Principal> members = group.members();
|
||||||
|
while (members.hasMoreElements()) {
|
||||||
|
Principal role = (Principal) members.nextElement();
|
||||||
|
subjectGroup.addMember(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the CallerPrincipal group if none has been added in getRoleSets
|
||||||
|
Group callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
|
||||||
|
callerGroup.addMember(account.getPrincipal());
|
||||||
|
principals.add(callerGroup);
|
||||||
|
org.jboss.security.SecurityContext sc = SecurityContextAssociation.getSecurityContext();
|
||||||
|
Principal userPrincipal = getPrincipal(subject);
|
||||||
|
sc.getUtil().createSubjectInfo(userPrincipal, account, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Principal given the authenticated Subject. Currently the first subject that is not of type {@code Group} is
|
||||||
|
* considered or the single subject inside the CallerPrincipal group.
|
||||||
|
*
|
||||||
|
* @param subject
|
||||||
|
* @return the authenticated subject
|
||||||
|
*/
|
||||||
|
protected static Principal getPrincipal(Subject subject) {
|
||||||
|
Principal principal = null;
|
||||||
|
Principal callerPrincipal = null;
|
||||||
|
if (subject != null) {
|
||||||
|
Set<Principal> principals = subject.getPrincipals();
|
||||||
|
if (principals != null && !principals.isEmpty()) {
|
||||||
|
for (Principal p : principals) {
|
||||||
|
if (!(p instanceof Group) && principal == null) {
|
||||||
|
principal = p;
|
||||||
|
}
|
||||||
|
if (p instanceof Group) {
|
||||||
|
Group g = Group.class.cast(p);
|
||||||
|
if (g.getName().equals(SecurityConstants.CALLER_PRINCIPAL_GROUP) && callerPrincipal == null) {
|
||||||
|
Enumeration<? extends Principal> e = g.members();
|
||||||
|
if (e.hasMoreElements())
|
||||||
|
callerPrincipal = e.nextElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callerPrincipal == null ? principal : callerPrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Group createGroup(String name, Set<Principal> principals) {
|
||||||
|
Group roles = null;
|
||||||
|
Iterator<Principal> iter = principals.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Object next = iter.next();
|
||||||
|
if ((next instanceof Group) == false)
|
||||||
|
continue;
|
||||||
|
Group grp = (Group) next;
|
||||||
|
if (grp.getName().equals(name)) {
|
||||||
|
roles = grp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we did not find a group create one
|
||||||
|
if (roles == null) {
|
||||||
|
roles = new SimpleGroup(name);
|
||||||
|
principals.add(roles);
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Group[] getRoleSets(Collection<String> roleSet) {
|
||||||
|
SimpleGroup roles = new SimpleGroup("Roles");
|
||||||
|
Group[] roleSets = {roles};
|
||||||
|
for (String role : roleSet) {
|
||||||
|
roles.addMember(new SimplePrincipal(role));
|
||||||
|
}
|
||||||
|
return roleSets;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
package org.keycloak.adapters.wildfly;
|
||||||
|
|
||||||
|
import io.undertow.security.api.SecurityContext;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import io.undertow.servlet.api.ConfidentialPortManager;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.undertow.ServletKeycloakAuthMech;
|
||||||
|
import org.keycloak.adapters.undertow.ServletRequestAuthenticator;
|
||||||
|
import org.keycloak.adapters.undertow.UndertowHttpFacade;
|
||||||
|
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class WildflyAuthenticationMechanism extends ServletKeycloakAuthMech {
|
||||||
|
|
||||||
|
public WildflyAuthenticationMechanism(KeycloakDeployment deployment,
|
||||||
|
UndertowUserSessionManagement userSessionManagement,
|
||||||
|
ConfidentialPortManager portManager) {
|
||||||
|
super(deployment, userSessionManagement, portManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServletRequestAuthenticator createRequestAuthenticator(HttpServerExchange exchange, SecurityContext securityContext, UndertowHttpFacade facade) {
|
||||||
|
return new WildflyRequestAuthenticator(facade, deployment,
|
||||||
|
portManager.getConfidentialPort(exchange), securityContext, exchange, userSessionManagement);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
package org.keycloak.adapters.wildfly;
|
||||||
|
|
||||||
|
import io.undertow.servlet.api.DeploymentInfo;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.undertow.KeycloakServletExtension;
|
||||||
|
import org.keycloak.adapters.undertow.ServletKeycloakAuthMech;
|
||||||
|
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class WildflyKeycloakServletExtension extends KeycloakServletExtension {
|
||||||
|
protected static Logger log = Logger.getLogger(WildflyKeycloakServletExtension.class);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ServletKeycloakAuthMech createAuthenticationMechanism(DeploymentInfo deploymentInfo, KeycloakDeployment deployment,
|
||||||
|
UndertowUserSessionManagement userSessionManagement) {
|
||||||
|
log.info("creating WildflyAuthenticationMechanism");
|
||||||
|
return new WildflyAuthenticationMechanism(deployment, userSessionManagement, deploymentInfo.getConfidentialPortManager());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,136 @@
|
||||||
|
package org.keycloak.adapters.wildfly;
|
||||||
|
|
||||||
|
import io.undertow.security.api.SecurityContext;
|
||||||
|
import io.undertow.server.HttpServerExchange;
|
||||||
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.security.NestableGroup;
|
||||||
|
import org.jboss.security.SecurityConstants;
|
||||||
|
import org.jboss.security.SecurityContextAssociation;
|
||||||
|
import org.jboss.security.SimpleGroup;
|
||||||
|
import org.jboss.security.SimplePrincipal;
|
||||||
|
import org.keycloak.adapters.HttpFacade;
|
||||||
|
import org.keycloak.adapters.KeycloakDeployment;
|
||||||
|
import org.keycloak.adapters.undertow.KeycloakUndertowAccount;
|
||||||
|
import org.keycloak.adapters.undertow.ServletRequestAuthenticator;
|
||||||
|
import org.keycloak.adapters.undertow.UndertowUserSessionManagement;
|
||||||
|
|
||||||
|
import javax.security.auth.Subject;
|
||||||
|
import java.security.Principal;
|
||||||
|
import java.security.acl.Group;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public class WildflyRequestAuthenticator extends ServletRequestAuthenticator {
|
||||||
|
protected static Logger log = Logger.getLogger(WildflyRequestAuthenticator.class);
|
||||||
|
|
||||||
|
public WildflyRequestAuthenticator(HttpFacade facade, KeycloakDeployment deployment, int sslRedirectPort,
|
||||||
|
SecurityContext securityContext, HttpServerExchange exchange,
|
||||||
|
UndertowUserSessionManagement userSessionManagement) {
|
||||||
|
super(facade, deployment, sslRedirectPort, securityContext, exchange, userSessionManagement);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void propagateKeycloakContext(KeycloakUndertowAccount account) {
|
||||||
|
super.propagateKeycloakContext(account);
|
||||||
|
SecurityInfoHelper.propagateSessionInfo(account);
|
||||||
|
log.info("propagate security context to wildfly");
|
||||||
|
Subject subject = new Subject();
|
||||||
|
Set<Principal> principals = subject.getPrincipals();
|
||||||
|
principals.add(account.getPrincipal());
|
||||||
|
Group[] roleSets = getRoleSets(account.getRoles());
|
||||||
|
for (int g = 0; g < roleSets.length; g++) {
|
||||||
|
Group group = roleSets[g];
|
||||||
|
String name = group.getName();
|
||||||
|
Group subjectGroup = createGroup(name, principals);
|
||||||
|
if (subjectGroup instanceof NestableGroup) {
|
||||||
|
/* A NestableGroup only allows Groups to be added to it so we
|
||||||
|
need to add a SimpleGroup to subjectRoles to contain the roles
|
||||||
|
*/
|
||||||
|
SimpleGroup tmp = new SimpleGroup("Roles");
|
||||||
|
subjectGroup.addMember(tmp);
|
||||||
|
subjectGroup = tmp;
|
||||||
|
}
|
||||||
|
// Copy the group members to the Subject group
|
||||||
|
Enumeration<? extends Principal> members = group.members();
|
||||||
|
while (members.hasMoreElements()) {
|
||||||
|
Principal role = (Principal) members.nextElement();
|
||||||
|
subjectGroup.addMember(role);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// add the CallerPrincipal group if none has been added in getRoleSets
|
||||||
|
Group callerGroup = new SimpleGroup(SecurityConstants.CALLER_PRINCIPAL_GROUP);
|
||||||
|
callerGroup.addMember(account.getPrincipal());
|
||||||
|
principals.add(callerGroup);
|
||||||
|
org.jboss.security.SecurityContext sc = SecurityContextAssociation.getSecurityContext();
|
||||||
|
Principal userPrincipal = getPrincipal(subject);
|
||||||
|
sc.getUtil().createSubjectInfo(userPrincipal, account, subject);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the Principal given the authenticated Subject. Currently the first subject that is not of type {@code Group} is
|
||||||
|
* considered or the single subject inside the CallerPrincipal group.
|
||||||
|
*
|
||||||
|
* @param subject
|
||||||
|
* @return the authenticated subject
|
||||||
|
*/
|
||||||
|
protected Principal getPrincipal(Subject subject) {
|
||||||
|
Principal principal = null;
|
||||||
|
Principal callerPrincipal = null;
|
||||||
|
if (subject != null) {
|
||||||
|
Set<Principal> principals = subject.getPrincipals();
|
||||||
|
if (principals != null && !principals.isEmpty()) {
|
||||||
|
for (Principal p : principals) {
|
||||||
|
if (!(p instanceof Group) && principal == null) {
|
||||||
|
principal = p;
|
||||||
|
}
|
||||||
|
if (p instanceof Group) {
|
||||||
|
Group g = Group.class.cast(p);
|
||||||
|
if (g.getName().equals(SecurityConstants.CALLER_PRINCIPAL_GROUP) && callerPrincipal == null) {
|
||||||
|
Enumeration<? extends Principal> e = g.members();
|
||||||
|
if (e.hasMoreElements())
|
||||||
|
callerPrincipal = e.nextElement();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return callerPrincipal == null ? principal : callerPrincipal;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Group createGroup(String name, Set<Principal> principals) {
|
||||||
|
Group roles = null;
|
||||||
|
Iterator<Principal> iter = principals.iterator();
|
||||||
|
while (iter.hasNext()) {
|
||||||
|
Object next = iter.next();
|
||||||
|
if ((next instanceof Group) == false)
|
||||||
|
continue;
|
||||||
|
Group grp = (Group) next;
|
||||||
|
if (grp.getName().equals(name)) {
|
||||||
|
roles = grp;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// If we did not find a group create one
|
||||||
|
if (roles == null) {
|
||||||
|
roles = new SimpleGroup(name);
|
||||||
|
principals.add(roles);
|
||||||
|
}
|
||||||
|
return roles;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Group[] getRoleSets(Collection<String> roleSet) {
|
||||||
|
SimpleGroup roles = new SimpleGroup("Roles");
|
||||||
|
Group[] roleSets = {roles};
|
||||||
|
for (String role : roleSet) {
|
||||||
|
roles.addMember(new SimplePrincipal(role));
|
||||||
|
}
|
||||||
|
return roleSets;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1 @@
|
||||||
|
org.keycloak.adapters.wildfly.WildflyKeycloakServletExtension
|
|
@ -48,11 +48,30 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
||||||
// Seems wise to have this run after INSTALL_WAR_DEPLOYMENT
|
// Seems wise to have this run after INSTALL_WAR_DEPLOYMENT
|
||||||
public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT - 1;
|
public static final int PRIORITY = Phase.INSTALL_WAR_DEPLOYMENT - 1;
|
||||||
|
|
||||||
|
// not sure if we need this yet, keeping here just in case
|
||||||
|
protected void addSecurityDomain(DeploymentUnit deploymentUnit, KeycloakAdapterConfigService service) {
|
||||||
|
String deploymentName = deploymentUnit.getName();
|
||||||
|
if (!service.isKeycloakDeployment(deploymentName)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
WarMetaData warMetaData = deploymentUnit.getAttachment(WarMetaData.ATTACHMENT_KEY);
|
||||||
|
if (warMetaData == null) return;
|
||||||
|
JBossWebMetaData webMetaData = warMetaData.getMergedJBossWebMetaData();
|
||||||
|
if (webMetaData == null) return;
|
||||||
|
|
||||||
|
LoginConfigMetaData loginConfig = webMetaData.getLoginConfig();
|
||||||
|
if (loginConfig == null || !loginConfig.getAuthMethod().equalsIgnoreCase("KEYCLOAK")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
webMetaData.setSecurityDomain("keycloak");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
|
public void deploy(DeploymentPhaseContext phaseContext) throws DeploymentUnitProcessingException {
|
||||||
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
|
DeploymentUnit deploymentUnit = phaseContext.getDeploymentUnit();
|
||||||
String deploymentName = deploymentUnit.getName();
|
|
||||||
|
|
||||||
|
String deploymentName = deploymentUnit.getName();
|
||||||
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
|
KeycloakAdapterConfigService service = KeycloakAdapterConfigService.find(phaseContext.getServiceRegistry());
|
||||||
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: " + deploymentName);
|
//log.info("********* CHECK KEYCLOAK DEPLOYMENT: " + deploymentName);
|
||||||
if (service.isKeycloakDeployment(deploymentName)) {
|
if (service.isKeycloakDeployment(deploymentName)) {
|
||||||
|
@ -61,6 +80,9 @@ public class KeycloakAdapterConfigDeploymentProcessor implements DeploymentUnitP
|
||||||
}
|
}
|
||||||
|
|
||||||
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
|
// FYI, Undertow Extension will find deployments that have auth-method set to KEYCLOAK
|
||||||
|
|
||||||
|
// todo notsure if we need this
|
||||||
|
// addSecurityDomain(deploymentUnit, service);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) {
|
private void addKeycloakAuthData(DeploymentPhaseContext phaseContext, String deploymentName, KeycloakAdapterConfigService service) {
|
||||||
|
|
|
@ -36,7 +36,9 @@ import org.keycloak.subsystem.logging.KeycloakLogger;
|
||||||
*/
|
*/
|
||||||
public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
|
public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
|
||||||
|
|
||||||
|
private static final ModuleIdentifier KEYCLOAK_WILDFLY_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-wildfly-adapter");
|
||||||
private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
|
private static final ModuleIdentifier KEYCLOAK_UNDERTOW_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-undertow-adapter");
|
||||||
|
private static final ModuleIdentifier KEYCLOAK_JBOSS_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-jboss-adapter-core");
|
||||||
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
|
private static final ModuleIdentifier KEYCLOAK_CORE_ADAPTER = ModuleIdentifier.create("org.keycloak.keycloak-adapter-core");
|
||||||
private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
|
private static final ModuleIdentifier KEYCLOAK_CORE = ModuleIdentifier.create("org.keycloak.keycloak-core");
|
||||||
//private static final ModuleIdentifier APACHE_HTTPCOMPONENTS = ModuleIdentifier.create("org.apache.httpcomponents");
|
//private static final ModuleIdentifier APACHE_HTTPCOMPONENTS = ModuleIdentifier.create("org.apache.httpcomponents");
|
||||||
|
@ -51,7 +53,9 @@ public class KeycloakDependencyProcessor implements DeploymentUnitProcessor {
|
||||||
final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
|
final ModuleSpecification moduleSpecification = deploymentUnit.getAttachment(Attachments.MODULE_SPECIFICATION);
|
||||||
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
final ModuleLoader moduleLoader = Module.getBootModuleLoader();
|
||||||
|
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, true, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_WILDFLY_ADAPTER, false, false, true, false));
|
||||||
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_UNDERTOW_ADAPTER, false, false, false, false));
|
||||||
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_JBOSS_CORE_ADAPTER, false, false, false, false));
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE_ADAPTER, false, false, false, false));
|
||||||
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
|
moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, KEYCLOAK_CORE, false, false, false, false));
|
||||||
//moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE_HTTPCOMPONENTS, false, false, true, false));
|
//moduleSpecification.addSystemDependency(new ModuleDependency(moduleLoader, APACHE_HTTPCOMPONENTS, false, false, true, false));
|
||||||
|
|
|
@ -218,7 +218,7 @@ public class AdminService {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Path("isLoggedIn.js")
|
@Path("isLoggedIn.js")
|
||||||
@GET
|
@GET
|
||||||
@Produces("application/javascript")
|
@Produces("application/javascript")
|
||||||
@NoCache
|
@NoCache
|
||||||
|
|
Loading…
Reference in a new issue