Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
201d2c6aac
369 changed files with 66132 additions and 853 deletions
|
@ -88,7 +88,6 @@
|
|||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>3.1.0.GA</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
@ -104,7 +103,6 @@ projects that depend on this project.-->
|
|||
<dependency>
|
||||
<groupId>org.jboss.msc</groupId>
|
||||
<artifactId>jboss-msc</artifactId>
|
||||
<version>1.0.2.GA</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -30,6 +30,18 @@
|
|||
<artifactId>keycloak-as7-integration-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.as</groupId>
|
||||
<artifactId>jboss-as-parent</artifactId>
|
||||
<version>${jboss.as.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
<module>as7-adapter-spi</module>
|
||||
<module>as7-adapter</module>
|
||||
|
|
|
@ -33,9 +33,22 @@
|
|||
<modules>
|
||||
<module>jetty-core</module>
|
||||
<module>jetty8.1</module>
|
||||
<module>jetty9.1</module>
|
||||
<module>jetty9.2</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>jetty9.1</module>
|
||||
<module>jetty9.3</module>
|
||||
<module>jetty9.4</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -832,11 +832,16 @@
|
|||
document.body.appendChild(iframe);
|
||||
|
||||
var messageCallback = function(event) {
|
||||
if (event.origin !== loginIframe.iframeOrigin) {
|
||||
if ((event.origin !== loginIframe.iframeOrigin) || (loginIframe.iframe.contentWindow !== event.source)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data != "unchanged") {
|
||||
if (!(event.data == 'unchanged' || event.data == 'changed' || event.data == 'error')) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (event.data != 'unchanged') {
|
||||
kc.clearToken();
|
||||
}
|
||||
|
||||
|
@ -844,7 +849,7 @@
|
|||
|
||||
for (var i = callbacks.length - 1; i >= 0; --i) {
|
||||
var promise = callbacks[i];
|
||||
if (event.data == "unchanged") {
|
||||
if (event.data == 'unchanged') {
|
||||
promise.setSuccess();
|
||||
} else {
|
||||
promise.setError();
|
||||
|
|
|
@ -45,5 +45,6 @@
|
|||
<module>tomcat</module>
|
||||
<module>undertow</module>
|
||||
<module>wildfly</module>
|
||||
<module>wildfly-elytron</module>
|
||||
</modules>
|
||||
</project>
|
||||
|
|
|
@ -32,8 +32,21 @@
|
|||
|
||||
<modules>
|
||||
<module>tomcat-core</module>
|
||||
<module>tomcat6</module>
|
||||
<module>tomcat7</module>
|
||||
<module>tomcat8</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>tomcat6</module>
|
||||
<module>tomcat7</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -29,8 +29,6 @@
|
|||
<artifactId>keycloak-tomcat-core-adapter</artifactId>
|
||||
<name>Keycloak Tomcat Core Integration</name>
|
||||
<properties>
|
||||
<!-- <tomcat.version>8.0.14</tomcat.version> -->
|
||||
<!-- <tomcat.version>7.0.52</tomcat.version> -->
|
||||
<tomcat.version>6.0.41</tomcat.version>
|
||||
</properties>
|
||||
<description />
|
||||
|
|
99
adapters/oidc/wildfly-elytron/pom.xml
Executable file
99
adapters/oidc/wildfly-elytron/pom.xml
Executable file
|
@ -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>
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -32,7 +32,20 @@
|
|||
|
||||
<modules>
|
||||
<module>wildfly-adapter</module>
|
||||
<module>wf8-subsystem</module>
|
||||
<module>wildfly-subsystem</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>wf8-subsystem</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
0
adapters/oidc/wildfly/wildfly-adapter/pom.xml
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/pom.xml
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/SecurityInfoHelper.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/SecurityInfoHelper.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyAuthenticationMechanism.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyAuthenticationMechanism.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyKeycloakServletExtension.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyKeycloakServletExtension.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyRequestAuthenticator.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/java/org/keycloak/adapters/wildfly/WildflyRequestAuthenticator.java
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
Executable file → Normal file
0
adapters/oidc/wildfly/wildfly-adapter/src/main/resources/META-INF/services/io.undertow.servlet.ServletExtension
Executable file → Normal file
|
@ -30,6 +30,18 @@
|
|||
<artifactId>keycloak-saml-eap-integration-pom</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<dependencyManagement>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.as</groupId>
|
||||
<artifactId>jboss-as-parent</artifactId>
|
||||
<version>${jboss.as.version}</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
<modules>
|
||||
<module>adapter</module>
|
||||
<module>subsystem</module>
|
||||
|
|
|
@ -88,7 +88,6 @@
|
|||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<version>3.1.0.GA</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
|
|
|
@ -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;
|
||||
|
@ -98,6 +102,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
|
||||
*
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -35,9 +35,23 @@
|
|||
<module>core</module>
|
||||
<module>undertow</module>
|
||||
<module>tomcat</module>
|
||||
<module>jetty</module>
|
||||
<module>wildfly</module>
|
||||
<module>as7-eap6</module>
|
||||
<module>servlet-filter</module>
|
||||
<module>wildfly-elytron</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>jetty</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -32,8 +32,21 @@
|
|||
|
||||
<modules>
|
||||
<module>tomcat-core</module>
|
||||
<module>tomcat6</module>
|
||||
<module>tomcat7</module>
|
||||
<module>tomcat8</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>tomcat6</module>
|
||||
<module>tomcat7</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
102
adapters/saml/wildfly-elytron/pom.xml
Executable file
102
adapters/saml/wildfly-elytron/pom.xml
Executable file
|
@ -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>
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -93,4 +93,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}-eap6-adapter</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -32,7 +32,20 @@
|
|||
|
||||
<modules>
|
||||
<module>as7-modules</module>
|
||||
<module>as7-adapter-zip</module>
|
||||
<module>eap6-adapter-zip</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>as7-adapter-zip</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -85,4 +85,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}-js-adapter</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -33,17 +33,31 @@
|
|||
<modules>
|
||||
<module>as7-eap6-adapter</module>
|
||||
<module>fuse-adapter-zip</module>
|
||||
<module>js-adapter-zip</module>
|
||||
<module>osgi</module>
|
||||
|
||||
<module>wildfly-adapter</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>tomcat6-adapter-zip</module>
|
||||
<module>tomcat7-adapter-zip</module>
|
||||
<module>tomcat8-adapter-zip</module>
|
||||
<module>jetty81-adapter-zip</module>
|
||||
<module>jetty91-adapter-zip</module>
|
||||
<module>jetty92-adapter-zip</module>
|
||||
<module>jetty93-adapter-zip</module>
|
||||
<module>jetty94-adapter-zip</module>
|
||||
<module>js-adapter-zip</module>
|
||||
<module>osgi</module>
|
||||
<module>tomcat6-adapter-zip</module>
|
||||
<module>tomcat7-adapter-zip</module>
|
||||
<module>tomcat8-adapter-zip</module>
|
||||
<module>wf8-adapter</module>
|
||||
<module>wildfly-adapter</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
57
distribution/adapters/shared-cli/adapter-elytron-install.cli
Normal file
57
distribution/adapters/shared-cli/adapter-elytron-install.cli
Normal file
|
@ -0,0 +1,57 @@
|
|||
if (outcome != success) of /extension=org.keycloak.keycloak-adapter-subsystem:read-resource
|
||||
/extension=org.keycloak.keycloak-adapter-subsystem/:add(module=org.keycloak.keycloak-adapter-subsystem)
|
||||
else
|
||||
echo Keycloak OpenID Connect Extension already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=keycloak:read-resource
|
||||
/subsystem=keycloak:add
|
||||
else
|
||||
echo Keycloak OpenID Connect Subsystem already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/custom-realm=KeycloakOIDCRealm:read-resource
|
||||
/subsystem=elytron/custom-realm=KeycloakOIDCRealm:add(class-name=org.keycloak.adapters.elytron.KeycloakSecurityRealm, module=org.keycloak.keycloak-wildfly-elytron-oidc-adapter)
|
||||
else
|
||||
echo Keycloak OpenID Connect Realm already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/security-domain=KeycloakDomain:read-resource
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:add(default-realm=KeycloakOIDCRealm,permission-mapper=default-permission-mapper,security-event-listener=local-audit,realms=[{realm=KeycloakOIDCRealm}])
|
||||
else
|
||||
echo Keycloak Security Domain already installed. Trying to install Keycloak OpenID Connect Realm.
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:list-add(name=realms, value={realm=KeycloakOIDCRealm})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/constant-realm-mapper=keycloak-oidc-realm-mapper:read-resource
|
||||
/subsystem=elytron/constant-realm-mapper=keycloak-oidc-realm-mapper:add(realm-name=KeycloakOIDCRealm)
|
||||
else
|
||||
echo Keycloak OpenID Connect Realm Mapper already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-oidc-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-oidc-http-server-mechanism-factory:add(module=org.keycloak.keycloak-wildfly-elytron-oidc-adapter)
|
||||
else
|
||||
echo Keycloak OpenID Connect HTTP Mechanism already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-oidc-http-server-mechanism-factory, global])
|
||||
else
|
||||
echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak OpenID Connect HTTP Mechanism Factory.
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-oidc-http-server-mechanism-factory)
|
||||
end-if
|
||||
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/http-authentication-factory=keycloak-http-authentication:read-resource
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:add(security-domain=KeycloakDomain,http-server-mechanism-factory=keycloak-http-server-mechanism-factory,mechanism-configurations=[{mechanism-name=KEYCLOAK,mechanism-realm-configurations=[{realm-name=KeycloakOIDCRealm,realm-mapper=keycloak-oidc-realm-mapper}]}])
|
||||
else
|
||||
echo Keycloak HTTP Authentication Factory already installed. Trying to install Keycloak OpenID Connect Mechanism Configuration
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:list-add(name=mechanism-configurations, value={mechanism-name=KEYCLOAK,mechanism-realm-configurations=[{realm-name=KeycloakOIDCRealm,realm-mapper=keycloak-oidc-realm-mapper}]})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=undertow/application-security-domain=other:read-resource
|
||||
/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=keycloak-http-authentication)
|
||||
else
|
||||
echo Undertow already configured with Keycloak
|
||||
end-if
|
|
@ -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>
|
||||
|
@ -57,5 +58,13 @@
|
|||
<source>cli/adapter-install-offline.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>../../shared-cli/adapter-elytron-install.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>cli/adapter-elytron-install-offline.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
|
@ -0,0 +1,59 @@
|
|||
embed-server --server-config=standalone.xml
|
||||
|
||||
if (outcome != success) of /extension=org.keycloak.keycloak-adapter-subsystem:read-resource
|
||||
/extension=org.keycloak.keycloak-adapter-subsystem/:add(module=org.keycloak.keycloak-adapter-subsystem)
|
||||
else
|
||||
echo Keycloak OpenID Connect Extension already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=keycloak:read-resource
|
||||
/subsystem=keycloak:add
|
||||
else
|
||||
echo Keycloak OpenID Connect Subsystem already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/custom-realm=KeycloakOIDCRealm:read-resource
|
||||
/subsystem=elytron/custom-realm=KeycloakOIDCRealm:add(class-name=org.keycloak.adapters.elytron.KeycloakSecurityRealm, module=org.keycloak.keycloak-wildfly-elytron-oidc-adapter)
|
||||
else
|
||||
echo Keycloak OpenID Connect Realm already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/security-domain=KeycloakDomain:read-resource
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:add(default-realm=KeycloakOIDCRealm,permission-mapper=default-permission-mapper,security-event-listener=local-audit,realms=[{realm=KeycloakOIDCRealm}])
|
||||
else
|
||||
echo Keycloak Security Domain already installed. Trying to install Keycloak OpenID Connect Realm.
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:list-add(name=realms, value={realm=KeycloakOIDCRealm})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/constant-realm-mapper=keycloak-oidc-realm-mapper:read-resource
|
||||
/subsystem=elytron/constant-realm-mapper=keycloak-oidc-realm-mapper:add(realm-name=KeycloakOIDCRealm)
|
||||
else
|
||||
echo Keycloak OpenID Connect Realm Mapper already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-oidc-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-oidc-http-server-mechanism-factory:add(module=org.keycloak.keycloak-wildfly-elytron-oidc-adapter)
|
||||
else
|
||||
echo Keycloak OpenID Connect HTTP Mechanism already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-oidc-http-server-mechanism-factory, global])
|
||||
else
|
||||
echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak OpenID Connect HTTP Mechanism Factory.
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-oidc-http-server-mechanism-factory)
|
||||
end-if
|
||||
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/http-authentication-factory=keycloak-http-authentication:read-resource
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:add(security-domain=KeycloakDomain,http-server-mechanism-factory=keycloak-http-server-mechanism-factory,mechanism-configurations=[{mechanism-name=KEYCLOAK,mechanism-realm-configurations=[{realm-name=KeycloakOIDCRealm,realm-mapper=keycloak-oidc-realm-mapper}]}])
|
||||
else
|
||||
echo Keycloak HTTP Authentication Factory already installed. Trying to install Keycloak OpenID Connect Mechanism Configuration
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:list-add(name=mechanism-configurations, value={mechanism-name=KEYCLOAK,mechanism-realm-configurations=[{realm-name=KeycloakOIDCRealm,realm-mapper=keycloak-oidc-realm-mapper}]})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=undertow/application-security-domain=other:read-resource
|
||||
/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=keycloak-http-authentication)
|
||||
else
|
||||
echo Undertow already configured with Keycloak
|
||||
end-if
|
|
@ -90,4 +90,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}-eap7-adapter</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -66,6 +66,10 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-elytron-oidc-adapter</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-subsystem</artifactId>
|
||||
|
|
|
@ -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>
|
|
@ -29,6 +29,14 @@
|
|||
<name>Keycloak Demo Distribution</name>
|
||||
<description/>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
@ -223,5 +231,7 @@
|
|||
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -34,6 +34,14 @@
|
|||
<maven.compiler.source>1.8</maven.compiler.source>
|
||||
</properties>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
@ -74,4 +82,6 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -29,8 +29,14 @@
|
|||
<name>Keycloak Examples Distribution</name>
|
||||
<description/>
|
||||
|
||||
<dependencies>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>keycloak-examples-${project.version}</finalName>
|
||||
<plugins>
|
||||
|
@ -101,5 +107,7 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -24,39 +24,10 @@
|
|||
</formats>
|
||||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<!-- If war is filtered it will get corrupted. Only need to filter module.xml -->
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>target/${project.build.finalName}</directory>
|
||||
<outputDirectory/>
|
||||
<filtered>true</filtered>
|
||||
<includes>
|
||||
<include>**/module.xml</include>
|
||||
<include>**/MANIFEST.MF</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target/${project.build.finalName}</directory>
|
||||
<outputDirectory/>
|
||||
<filtered>false</filtered>
|
||||
<excludes>
|
||||
<exclude>**/module.xml</exclude>
|
||||
<exclude>**/MANIFEST.MF</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>../../../</directory>
|
||||
<includes>
|
||||
<include>License.html</include>
|
||||
</includes>
|
||||
<outputDirectory>content</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>../../../themes/src/main/resources/theme</directory>
|
||||
<outputDirectory>content/themes</outputDirectory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
|
||||
<build xmlns="urn:wildfly:feature-pack-build:1.1">
|
||||
<dependencies>
|
||||
<artifact name="org.wildfly:wildfly-feature-pack" />
|
||||
<artifact name="${feature.parent}" />
|
||||
</dependencies>
|
||||
<config>
|
||||
<standalone template="configuration/standalone/template.xml" subsystems="configuration/standalone/subsystems.xml" output-file="standalone/configuration/standalone.xml" />
|
||||
|
|
|
@ -47,27 +47,153 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-wildfly-server-subsystem</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-feature-pack</artifactId>
|
||||
<type>zip</type>
|
||||
<!-- Need to exlcude that in order to use the right guava version for drools -->
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions></executions>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>unpack-theme</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-themes</artifactId>
|
||||
<outputDirectory>target/unpacked-themes</outputDirectory>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-configuration</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/configuration</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-modules</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/modules</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/modules</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-content</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/content</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/content</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-identity</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/content/bin</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/identity</directory>
|
||||
<includes>
|
||||
<include>**/product.conf</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-identity-module</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/modules/system/layers/keycloak/org/jboss/as/product/${product.slot}</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources/identity/module</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-themes</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/content/themes</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>target/unpacked-themes/theme</directory>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>copy-license</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/content</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>../../../</directory>
|
||||
<includes>
|
||||
<include>License.html</include>
|
||||
</includes>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
|
||||
<plugin>
|
||||
<groupId>org.wildfly.build</groupId>
|
||||
<artifactId>wildfly-feature-pack-build-maven-plugin</artifactId>
|
||||
|
@ -80,6 +206,7 @@
|
|||
<phase>compile</phase>
|
||||
<configuration>
|
||||
<config-file>feature-pack-build.xml</config-file>
|
||||
<resources-dir>target/resources</resources-dir>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -111,4 +238,121 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<properties>
|
||||
<feature.parent>org.wildfly:wildfly-feature-pack</feature.parent>
|
||||
<xmlns.domain>urn:jboss:domain:4.0</xmlns.domain>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-feature-pack</artifactId>
|
||||
<type>zip</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</profile>
|
||||
|
||||
<!-- Temporary profile to test with WildFly 11 -->
|
||||
<profile>
|
||||
<id>wf11</id>
|
||||
|
||||
<properties>
|
||||
<xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-feature-pack</artifactId>
|
||||
<version>${wildfly11.version}</version>
|
||||
<type>zip</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-configuration-wf11</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources-wf11/configuration</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
|
||||
<properties>
|
||||
<feature.parent>org.jboss.eap:wildfly-feature-pack</feature.parent>
|
||||
<xmlns.domain>urn:jboss:domain:5.0</xmlns.domain>
|
||||
</properties>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.jboss.eap</groupId>
|
||||
<artifactId>wildfly-feature-pack</artifactId>
|
||||
<version>${eap.version}</version>
|
||||
<type>zip</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-configuration-wf11</id>
|
||||
<phase>validate</phase>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<outputDirectory>target/resources/configuration</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>src/main/resources-wf11/configuration</directory>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
<?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.
|
||||
-->
|
||||
|
||||
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
|
||||
<config>
|
||||
<subsystems>
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem supplement="default">keycloak-datasources.xml</subsystem>
|
||||
<subsystem>deployment-scanner.xml</subsystem>
|
||||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem supplement="ha">ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem supplement="ha">keycloak-infinispan.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem supplement="default">jgroups.xml</subsystem>
|
||||
<subsystem>jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem supplement="default">mod_cluster.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem supplement="standalone-wildfly">elytron.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem supplement="ha">undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
</config>
|
|
@ -0,0 +1,47 @@
|
|||
<?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.
|
||||
-->
|
||||
|
||||
<!-- See src/resources/configuration/ReadMe.txt for how the configuration assembly works -->
|
||||
<config>
|
||||
<subsystems>
|
||||
<subsystem>logging.xml</subsystem>
|
||||
<subsystem>bean-validation.xml</subsystem>
|
||||
<subsystem supplement="default">keycloak-datasources2.xml</subsystem>
|
||||
<subsystem>deployment-scanner.xml</subsystem>
|
||||
<subsystem>ee.xml</subsystem>
|
||||
<subsystem>ejb3.xml</subsystem>
|
||||
<subsystem>io.xml</subsystem>
|
||||
<subsystem>keycloak-infinispan2.xml</subsystem>
|
||||
<subsystem>jaxrs.xml</subsystem>
|
||||
<subsystem>jca.xml</subsystem>
|
||||
<subsystem>jdr.xml</subsystem>
|
||||
<subsystem>jmx.xml</subsystem>
|
||||
<subsystem>jpa.xml</subsystem>
|
||||
<subsystem>jsf.xml</subsystem>
|
||||
<subsystem>mail.xml</subsystem>
|
||||
<subsystem>naming.xml</subsystem>
|
||||
<subsystem>remoting.xml</subsystem>
|
||||
<subsystem>request-controller.xml</subsystem>
|
||||
<subsystem>security-manager.xml</subsystem>
|
||||
<subsystem supplement="standalone-wildfly">elytron.xml</subsystem>
|
||||
<subsystem>security.xml</subsystem>
|
||||
<subsystem>transactions.xml</subsystem>
|
||||
<subsystem>undertow.xml</subsystem>
|
||||
<subsystem>keycloak-server.xml</subsystem>
|
||||
</subsystems>
|
||||
</config>
|
|
@ -0,0 +1,90 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
|
||||
<server xmlns="${xmlns.domain}">
|
||||
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
</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" />
|
||||
<socket-binding http="management-http"/>
|
||||
</http-interface>
|
||||
</management-interfaces>
|
||||
<access-control provider="simple">
|
||||
<role-mapping>
|
||||
<role name="SuperUser">
|
||||
<include>
|
||||
<user name="$local"/>
|
||||
</include>
|
||||
</role>
|
||||
</role-mapping>
|
||||
</access-control>
|
||||
</management>
|
||||
|
||||
<profile>
|
||||
|
||||
<?SUBSYSTEMS socket-binding-group="standard-sockets"?>
|
||||
|
||||
</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?>
|
||||
|
||||
</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-BINDINGS?>
|
||||
|
||||
</socket-binding-group>
|
||||
</server>
|
|
@ -17,7 +17,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<domain xmlns="urn:jboss:domain:4.0">
|
||||
<domain xmlns="${xmlns.domain}">
|
||||
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
is also started by this host controller file. The other instance must be started
|
||||
via host-slave.xml
|
||||
-->
|
||||
<host name="master" xmlns="urn:jboss:domain:4.0">
|
||||
<host name="master" xmlns="${xmlns.domain}">
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
</extensions>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<host xmlns="urn:jboss:domain:4.0">
|
||||
<host xmlns="${xmlns.domain}">
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
</extensions>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
via host-slave.xml
|
||||
-->
|
||||
|
||||
<host name="master" xmlns="urn:jboss:domain:4.0">
|
||||
<host name="master" xmlns="${xmlns.domain}">
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
</extensions>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<server xmlns="urn:jboss:domain:4.0">
|
||||
<server xmlns="${xmlns.domain}">
|
||||
|
||||
<extensions>
|
||||
<?EXTENSIONS?>
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
slot=keycloak
|
|
@ -1,2 +1,2 @@
|
|||
Any provider implementation jars and libraries in this folder will be loaded by Keycloak. See the providers
|
||||
section in the documentation for more details.
|
||||
Any provider implementation jars and libraries in this folder will be loaded. See the providers section in the
|
||||
documentation for more details.
|
|
@ -0,0 +1,3 @@
|
|||
JBoss-Product-Release-Name: ${product.name.full}
|
||||
JBoss-Product-Release-Version: ${product.version}
|
||||
JBoss-Product-Console-Slot: ${product.wildfly.console.slot}
|
|
@ -15,7 +15,7 @@
|
|||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.jboss.as.product" slot="keycloak">
|
||||
<module xmlns="urn:jboss:module:1.3" name="org.jboss.as.product" slot="${product.slot}">
|
||||
<properties>
|
||||
<property name="jboss.api" value="private"/>
|
||||
</properties>
|
|
@ -0,0 +1 @@
|
|||
slot=${product.slot}
|
|
@ -1,3 +0,0 @@
|
|||
JBoss-Product-Release-Name: ${product.name}
|
||||
JBoss-Product-Release-Version: ${product.version}
|
||||
JBoss-Product-Console-Slot: main
|
|
@ -34,10 +34,6 @@
|
|||
<modules>
|
||||
<module>adapters</module>
|
||||
<module>saml-adapters</module>
|
||||
<module>demo-dist</module>
|
||||
<module>api-docs-dist</module>
|
||||
<module>examples-dist</module>
|
||||
<module>proxy-dist</module>
|
||||
<module>server-dist</module>
|
||||
<module>server-overlay</module>
|
||||
<module>feature-packs</module>
|
||||
|
@ -47,7 +43,11 @@
|
|||
<profile>
|
||||
<id>jboss-release</id>
|
||||
<modules>
|
||||
<module>api-docs-dist</module>
|
||||
<module>downloads</module>
|
||||
<module>demo-dist</module>
|
||||
<module>examples-dist</module>
|
||||
<module>proxy-dist</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
|
|
@ -29,6 +29,14 @@
|
|||
<name>Proxy Distro</name>
|
||||
<description/>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
|
@ -69,5 +77,7 @@
|
|||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -93,4 +93,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}-saml-eap6-adapter</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -32,7 +32,20 @@
|
|||
|
||||
<modules>
|
||||
<module>as7-modules</module>
|
||||
<module>as7-adapter-zip</module>
|
||||
<module>eap6-adapter-zip</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>as7-adapter-zip</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
|
|
|
@ -32,15 +32,29 @@
|
|||
|
||||
<modules>
|
||||
<module>wildfly-adapter</module>
|
||||
<module>tomcat6-adapter-zip</module>
|
||||
<module>tomcat7-adapter-zip</module>
|
||||
<module>tomcat8-adapter-zip</module>
|
||||
<module>as7-eap6-adapter</module>
|
||||
</modules>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>jetty81-adapter-zip</module>
|
||||
<!-- jetty 9.1 doesn't work right now
|
||||
<module>jetty91-adapter-zip</module> -->
|
||||
<module>jetty92-adapter-zip</module>
|
||||
<module>jetty93-adapter-zip</module>
|
||||
<module>jetty94-adapter-zip</module>
|
||||
<module>as7-eap6-adapter</module>
|
||||
<module>tomcat6-adapter-zip</module>
|
||||
<module>tomcat7-adapter-zip</module>
|
||||
<module>tomcat8-adapter-zip</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
56
distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli
Executable file
56
distribution/saml-adapters/shared-cli/adapter-elytron-install-saml.cli
Executable file
|
@ -0,0 +1,56 @@
|
|||
if (outcome != success) of /extension=org.keycloak.keycloak-saml-adapter-subsystem:read-resource
|
||||
/extension=org.keycloak.keycloak-saml-adapter-subsystem/:add(module=org.keycloak.keycloak-saml-adapter-subsystem)
|
||||
else
|
||||
echo Keycloak SAML Extension already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=keycloak-saml:read-resource
|
||||
/subsystem=keycloak-saml:add
|
||||
else
|
||||
echo Keycloak SAML Subsystem already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/custom-realm=KeycloakSAMLRealm:read-resource
|
||||
/subsystem=elytron/custom-realm=KeycloakSAMLRealm:add(class-name=org.keycloak.adapters.saml.elytron.KeycloakSecurityRealm, module=org.keycloak.keycloak-saml-wildfly-elytron-adapter)
|
||||
else
|
||||
echo Keycloak SAML Realm already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/security-domain=KeycloakDomain:read-resource
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:add(default-realm=KeycloakSAMLRealm,permission-mapper=default-permission-mapper,security-event-listener=local-audit,realms=[{realm=KeycloakSAMLRealm}])
|
||||
else
|
||||
echo Keycloak Security Domain already installed. Trying to install Keycloak SAML Realm.
|
||||
/subsystem=elytron/security-domain=KeycloakDomain:list-add(name=realms, value={realm=KeycloakSAMLRealm})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/constant-realm-mapper=keycloak-saml-realm-mapper:read-resource
|
||||
/subsystem=elytron/constant-realm-mapper=keycloak-saml-realm-mapper:add(realm-name=KeycloakSAMLRealm)
|
||||
else
|
||||
echo Keycloak SAML Realm Mapper already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-saml-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/service-loader-http-server-mechanism-factory=keycloak-saml-http-server-mechanism-factory:add(module=org.keycloak.keycloak-saml-wildfly-elytron-adapter)
|
||||
else
|
||||
echo Keycloak SAML HTTP Mechanism Factory already installed
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:read-resource
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:add(http-server-factories=[keycloak-saml-http-server-mechanism-factory, global])
|
||||
else
|
||||
echo Keycloak HTTP Mechanism Factory already installed. Trying to install Keycloak SAML HTTP Mechanism Factory.
|
||||
/subsystem=elytron/aggregate-http-server-mechanism-factory=keycloak-http-server-mechanism-factory:list-add(name=http-server-factories, value=keycloak-saml-http-server-mechanism-factory)
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=elytron/http-authentication-factory=keycloak-http-authentication:read-resource
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:add(security-domain=KeycloakDomain,http-server-mechanism-factory=keycloak-http-server-mechanism-factory,mechanism-configurations=[{mechanism-name=KEYCLOAK-SAML,mechanism-realm-configurations=[{realm-name=KeycloakSAMLCRealm,realm-mapper=keycloak-saml-realm-mapper}]}])
|
||||
else
|
||||
echo Keycloak HTTP Authentication Factory already installed. Trying to install Keycloak SAML Mechanism Configuration
|
||||
/subsystem=elytron/http-authentication-factory=keycloak-http-authentication:list-add(name=mechanism-configurations, value={mechanism-name=KEYCLOAK-SAML,mechanism-realm-configurations=[{realm-name=KeycloakSAMLRealm,realm-mapper=keycloak-saml-realm-mapper}]})
|
||||
end-if
|
||||
|
||||
if (outcome != success) of /subsystem=undertow/application-security-domain=other:read-resource
|
||||
/subsystem=undertow/application-security-domain=other:add(http-authentication-factory=keycloak-http-authentication)
|
||||
else
|
||||
echo Undertow already configured with Keycloak
|
||||
end-if
|
|
@ -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>
|
||||
|
@ -51,5 +52,9 @@
|
|||
<source>../../shared-cli/adapter-install-saml.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
<file>
|
||||
<source>../../shared-cli/adapter-elytron-install-saml.cli</source>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
</file>
|
||||
</files>
|
||||
</assembly>
|
||||
|
|
|
@ -90,4 +90,18 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}-saml-eap7-adapter</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -70,6 +70,10 @@
|
|||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-saml-wildfly-adapter</artifactId>
|
||||
</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>
|
||||
|
|
|
@ -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>
|
|
@ -39,21 +39,23 @@
|
|||
<outputDirectory/>
|
||||
<filtered>false</filtered>
|
||||
<excludes>
|
||||
<exclude>**/*.sh</exclude>
|
||||
<exclude>**/module.xml</exclude>
|
||||
<exclude>bin/*.sh</exclude>
|
||||
<exclude>module.xml</exclude>
|
||||
<exclude>welcome-content/**</exclude>
|
||||
<exclude>appclient</exclude>
|
||||
<exclude>appclient/**</exclude>
|
||||
<exclude>bin/appclient.*</exclude>
|
||||
<exclude>copyright.txt</exclude>
|
||||
<exclude>README.txt</exclude>
|
||||
<exclude>themes/**</exclude>
|
||||
<exclude>version.txt</exclude>
|
||||
<exclude>${profileExcludes}</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>target/${project.build.finalName}</directory>
|
||||
<outputDirectory/>
|
||||
<includes>
|
||||
<include>**/*.sh</include>
|
||||
<include>bin/*.sh</include>
|
||||
</includes>
|
||||
<fileMode>0755</fileMode>
|
||||
</fileSet>
|
||||
|
@ -88,4 +90,13 @@
|
|||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
<files>
|
||||
<file>
|
||||
<source>src/main/version.txt</source>
|
||||
<outputDirectory/>
|
||||
<filtered>true</filtered>
|
||||
</file>
|
||||
</files>
|
||||
|
||||
</assembly>
|
||||
|
|
|
@ -38,8 +38,6 @@
|
|||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>keycloak-${project.version}</finalName>
|
||||
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.wildfly.build</groupId>
|
||||
|
@ -69,7 +67,7 @@
|
|||
</goals>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>assembly.xml</descriptor>
|
||||
<descriptor>${assemblyFile}</descriptor>
|
||||
</descriptors>
|
||||
<recompressZippedFiles>true</recompressZippedFiles>
|
||||
<finalName>${project.build.finalName}</finalName>
|
||||
|
@ -107,4 +105,44 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<assemblyFile>assembly.xml</assemblyFile>
|
||||
</properties>
|
||||
<build>
|
||||
<finalName>keycloak-${project.version}</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<assemblyFile>assembly.xml</assemblyFile>
|
||||
<profileExcludes>%regex[(providers.*)|(docs/contrib.*)|(docs/examples.*)|(docs/schema.*)]</profileExcludes>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.wildfly</groupId>
|
||||
<artifactId>wildfly-dist</artifactId>
|
||||
<type>zip</type>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
<finalName>${product.name}-${product.filename.version}</finalName>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
1
distribution/server-dist/src/main/version.txt
Normal file
1
distribution/server-dist/src/main/version.txt
Normal file
|
@ -0,0 +1 @@
|
|||
${product.name.full} - Version ${product.version}
|
|
@ -18,11 +18,13 @@
|
|||
|
||||
<html>
|
||||
<head>
|
||||
<title>Welcome to Keycloak</title>
|
||||
<meta http-equiv="refresh" content="0; url=/auth/" />
|
||||
<meta name="robots" content="noindex, nofollow">
|
||||
<script type="text/javascript">
|
||||
window.location.href = "/auth/"
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
If you are not redirected automatically, follow this <a href='/auth'>link</a>.
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -26,34 +26,6 @@
|
|||
<includeBaseDirectory>false</includeBaseDirectory>
|
||||
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/modules/system/layers/keycloak</directory>
|
||||
<outputDirectory>modules/system/add-ons/keycloak</outputDirectory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/themes</directory>
|
||||
<outputDirectory>themes</outputDirectory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/providers</directory>
|
||||
<outputDirectory>providers</outputDirectory>
|
||||
<includes>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>../../</directory>
|
||||
<includes>
|
||||
<include>License.html</include>
|
||||
</includes>
|
||||
<outputDirectory></outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/cli</directory>
|
||||
<includes>
|
||||
|
@ -62,21 +34,21 @@
|
|||
<outputDirectory>bin</outputDirectory>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/bin</directory>
|
||||
<directory>${project.build.directory}/unpacked/${serverDistDir}</directory>
|
||||
<outputDirectory/>
|
||||
<includes>
|
||||
<include>add-user-keycloak.*</include>
|
||||
<include>federation-sssd-setup.sh</include>
|
||||
<include>kcadm.*</include>
|
||||
<include>kcreg.*</include>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<excludes>
|
||||
<exclude>modules/**</exclude>
|
||||
</excludes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/unpacked/keycloak-${project.version}/bin/client</directory>
|
||||
<directory>${project.build.directory}/unpacked/${serverDistDir}/modules/system/layers/keycloak</directory>
|
||||
<outputDirectory>modules/system/${identityType}/keycloak</outputDirectory>
|
||||
<includes>
|
||||
<include>keycloak*</include>
|
||||
<include>**/**</include>
|
||||
</includes>
|
||||
<outputDirectory>bin/client</outputDirectory>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
|
||||
|
|
|
@ -37,9 +37,22 @@
|
|||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<properties>
|
||||
<commonFilesToInclude>
|
||||
${serverDistDir}/modules/system/layers/keycloak/**,
|
||||
${serverDistDir}/themes/**,
|
||||
${serverDistDir}/providers/**,
|
||||
${serverDistDir}/License.html,
|
||||
${serverDistDir}/bin/client/keycloak*,
|
||||
${serverDistDir}/bin/*keycloak*,
|
||||
${serverDistDir}/bin/kc*,
|
||||
${serverDistDir}/bin/federation-sssd-setup.sh,
|
||||
${serverDistDir}/bin/migrate*
|
||||
</commonFilesToInclude>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<finalName>keycloak-overlay-${project.version}</finalName>
|
||||
<resources></resources>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
|
@ -77,6 +90,7 @@
|
|||
<artifactId>keycloak-server-dist</artifactId>
|
||||
<type>zip</type>
|
||||
<outputDirectory>${project.build.directory}/unpacked</outputDirectory>
|
||||
<includes>${filesToInclude}</includes>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
|
@ -169,4 +183,38 @@
|
|||
</plugins>
|
||||
</build>
|
||||
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<identityType>add-ons</identityType>
|
||||
<serverDistDir>keycloak-${project.version}</serverDistDir>
|
||||
<filesToInclude>${commonFilesToInclude}</filesToInclude>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<identityType>layers</identityType>
|
||||
<serverDistDir>${product.name}-${product.filename.version}</serverDistDir>
|
||||
<filesToInclude>
|
||||
${commonFilesToInclude},
|
||||
${serverDistDir}/bin/product.conf,
|
||||
${serverDistDir}/modules/layers.conf</filesToInclude>
|
||||
</properties>
|
||||
</profile>
|
||||
</profiles>
|
||||
|
||||
</project>
|
||||
|
|
|
@ -50,6 +50,13 @@
|
|||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.keycloak.helper;
|
||||
package org.keycloak.test;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
|
@ -27,20 +27,21 @@ import org.keycloak.admin.client.Keycloak;
|
|||
import org.keycloak.client.registration.Auth;
|
||||
import org.keycloak.client.registration.ClientRegistration;
|
||||
import org.keycloak.client.registration.ClientRegistrationException;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import org.keycloak.representations.idm.ClientInitialAccessCreatePresentation;
|
||||
import org.keycloak.representations.idm.ClientInitialAccessPresentation;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.RoleRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.test.builders.ClientBuilder;
|
||||
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class TestsHelper {
|
||||
|
||||
|
@ -54,6 +55,8 @@ public class TestsHelper {
|
|||
|
||||
public static String appName;
|
||||
|
||||
public static int initialAccessTokenCount = 2;
|
||||
|
||||
protected static String clientConfiguration;
|
||||
|
||||
protected static String registrationAccessCode;
|
||||
|
@ -80,30 +83,7 @@ public class TestsHelper {
|
|||
}
|
||||
|
||||
public static String createDirectGrantClient() {
|
||||
ClientRepresentation clientRepresentation = new ClientRepresentation();
|
||||
clientRepresentation.setClientId("test-dga");
|
||||
clientRepresentation.setFullScopeAllowed(true);
|
||||
clientRepresentation.setPublicClient(Boolean.TRUE);
|
||||
clientRepresentation.setDirectAccessGrantsEnabled(true);
|
||||
|
||||
ClientRegistration reg = ClientRegistration.create()
|
||||
.url(keycloakBaseUrl, testRealm)
|
||||
.build();
|
||||
|
||||
reg.auth(Auth.token(initialAccessCode));
|
||||
try {
|
||||
clientRepresentation = reg.create(clientRepresentation);
|
||||
registrationAccessCode = clientRepresentation.getRegistrationAccessToken();
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
reg.auth(Auth.token(registrationAccessCode));
|
||||
clientConfiguration = mapper.writeValueAsString(reg.getAdapterConfig(clientRepresentation.getClientId()));
|
||||
} catch (ClientRegistrationException e) {
|
||||
e.printStackTrace();
|
||||
} catch (JsonProcessingException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return clientConfiguration;
|
||||
return createClient(ClientBuilder.create("test-dga").publicClient(true));
|
||||
}
|
||||
|
||||
public static void deleteClient(String clientId) {
|
||||
|
@ -169,7 +149,7 @@ public class TestsHelper {
|
|||
|
||||
}
|
||||
|
||||
public static boolean ImportTestRealm(String username, String password, String realmJsonPath) throws IOException {
|
||||
public static boolean importTestRealm(String username, String password, String realmJsonPath) throws IOException {
|
||||
|
||||
ObjectMapper mapper = new ObjectMapper();
|
||||
ClassLoader classLoader = TestsHelper.class.getClassLoader();
|
||||
|
@ -184,16 +164,12 @@ public class TestsHelper {
|
|||
"admin-cli");
|
||||
keycloak.realms().create(realmRepresentation);
|
||||
testRealm = realmRepresentation.getRealm();
|
||||
ClientInitialAccessCreatePresentation rep = new ClientInitialAccessCreatePresentation();
|
||||
rep.setCount(2);
|
||||
rep.setExpiration(100);
|
||||
ClientInitialAccessPresentation initialAccess = keycloak.realms().realm(testRealm).clientInitialAccess().create(rep);
|
||||
initialAccessCode = initialAccess.getToken();
|
||||
generateInitialAccessToken(keycloak);
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
public static boolean ImportTestRealm(String username, String password) throws IOException {
|
||||
public static boolean importTestRealm(String username, String password) throws IOException {
|
||||
testRealm = appName + "-realm";
|
||||
RealmRepresentation realmRepresentation = new RealmRepresentation();
|
||||
realmRepresentation.setRealm(testRealm);
|
||||
|
@ -205,12 +181,16 @@ public class TestsHelper {
|
|||
password,
|
||||
"admin-cli");
|
||||
keycloak.realms().create(realmRepresentation);
|
||||
generateInitialAccessToken(keycloak);
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void generateInitialAccessToken(Keycloak keycloak) {
|
||||
ClientInitialAccessCreatePresentation rep = new ClientInitialAccessCreatePresentation();
|
||||
rep.setCount(2);
|
||||
rep.setCount(initialAccessTokenCount);
|
||||
rep.setExpiration(100);
|
||||
ClientInitialAccessPresentation initialAccess = keycloak.realms().realm(testRealm).clientInitialAccess().create(rep);
|
||||
initialAccessCode = initialAccess.getToken();
|
||||
return true;
|
||||
}
|
||||
|
||||
public static boolean deleteRealm(String username, String password, String realmName) throws IOException {
|
||||
|
@ -235,19 +215,20 @@ public class TestsHelper {
|
|||
password,
|
||||
"admin-cli");
|
||||
UserRepresentation userRepresentation = new UserRepresentation();
|
||||
userRepresentation.setUsername("testuser");
|
||||
userRepresentation.setUsername(username);
|
||||
userRepresentation.setEnabled(Boolean.TRUE);
|
||||
Response response = keycloak.realms().realm(realmName).users().create(userRepresentation);
|
||||
String userId = getCreatedId(response);
|
||||
response.close();
|
||||
CredentialRepresentation rep = new CredentialRepresentation();
|
||||
rep.setType(CredentialRepresentation.PASSWORD);
|
||||
rep.setValue("password");
|
||||
rep.setValue(password);
|
||||
rep.setTemporary(false);
|
||||
keycloak.realms().realm(realmName).users().get(userId).resetPassword(rep);
|
||||
//add roles
|
||||
RoleRepresentation representation = new RoleRepresentation();
|
||||
representation.setName("user");
|
||||
|
||||
keycloak.realms().realm(realmName).roles().create(representation);
|
||||
RoleRepresentation realmRole = keycloak.realms().realm(realmName).roles().get("user").toRepresentation();
|
||||
keycloak.realms().realm(realmName).users().get(userId).roles().realmLevel().add(Arrays.asList(realmRole));
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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.test.builders;
|
||||
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bruno@abstractj.org">Bruno Oliveira</a>
|
||||
*/
|
||||
public class ClientBuilder {
|
||||
|
||||
private ClientRepresentation rep;
|
||||
|
||||
public static ClientBuilder create(String clientId) {
|
||||
ClientRepresentation rep = new ClientRepresentation();
|
||||
rep.setEnabled(Boolean.TRUE);
|
||||
rep.setClientId(clientId);
|
||||
return new ClientBuilder(rep);
|
||||
}
|
||||
|
||||
private ClientBuilder(ClientRepresentation rep) {
|
||||
this.rep = rep;
|
||||
}
|
||||
|
||||
public ClientRepresentation bearerOnly(boolean bearerOnly) {
|
||||
rep.setBearerOnly(bearerOnly);
|
||||
return rep;
|
||||
}
|
||||
|
||||
public ClientBuilder rootUrl(String rootUrl) {
|
||||
rep.setRootUrl(rootUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientBuilder redirectUri(String redirectUri) {
|
||||
rep.setRedirectUris(Collections.singletonList(redirectUri));
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientBuilder baseUrl(String baseUrl) {
|
||||
rep.setBaseUrl(baseUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientBuilder adminUrl(String adminUrl) {
|
||||
rep.setAdminUrl(adminUrl);
|
||||
return this;
|
||||
}
|
||||
|
||||
public ClientRepresentation publicClient(boolean publicClient) {
|
||||
rep.setFullScopeAllowed(true);
|
||||
rep.setPublicClient(publicClient);
|
||||
rep.setDirectAccessGrantsEnabled(true);
|
||||
rep.setAdminUrl(rep.getRootUrl());
|
||||
|
||||
if (rep.getRedirectUris() == null && rep.getRootUrl() != null)
|
||||
rep.setRedirectUris(Collections.singletonList(rep.getRootUrl().concat("/*")));
|
||||
return rep;
|
||||
}
|
||||
|
||||
}
|
85
pom.xml
85
pom.xml
|
@ -1,3 +1,4 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 Red Hat, Inc. and/or its affiliates
|
||||
~ and other contributors as indicated by the @author tags.
|
||||
|
@ -36,18 +37,20 @@
|
|||
<packaging>pom</packaging>
|
||||
|
||||
<properties>
|
||||
<product.name>Keycloak</product.name>
|
||||
<product.name-html>\u003Cdiv class="kc-logo-text"\u003E\u003Cspan\u003EKeycloak\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E</product.name-html>
|
||||
<product.version>${project.version}</product.version>
|
||||
<product.build-time>${timestamp}</product.build-time>
|
||||
<product.default-profile>community</product.default-profile>
|
||||
|
||||
<!-- WildFly -->
|
||||
<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>
|
||||
<wildfly11.version>11.0.0.Alpha1</wildfly11.version> <!-- for testing with wf11 pre-releases -->
|
||||
<eap.version>7.1.0.Alpha1-redhat-16</eap.version>
|
||||
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
|
||||
<wildfly.build-tools.version>1.1.8.Final</wildfly.build-tools.version>
|
||||
|
||||
<aesh.version>0.66.12</aesh.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.15</aesh.version>
|
||||
<apache.httpcomponents.version>4.5</apache.httpcomponents.version>
|
||||
<apache.httpcomponents.httpcore.version>4.4.1</apache.httpcomponents.httpcore.version>
|
||||
<apache.mime4j.version>0.6</apache.mime4j.version>
|
||||
|
@ -75,8 +78,6 @@
|
|||
<sun.jaxb.version>2.2.11</sun.jaxb.version>
|
||||
<sun.xsom.version>20140925</sun.xsom.version>
|
||||
<undertow.version>1.3.15.Final</undertow.version>
|
||||
<wildfly.core.version>2.0.10.Final</wildfly.core.version>
|
||||
<wildfly.build-tools.version>1.1.0.Final</wildfly.build-tools.version>
|
||||
<xmlsec.version>2.0.5</xmlsec.version>
|
||||
|
||||
<!-- Authorization Drools Policy Provider -->
|
||||
|
@ -112,6 +113,7 @@
|
|||
<!-- Maven Plugins -->
|
||||
<enforcer.plugin.version>1.4</enforcer.plugin.version>
|
||||
<replacer.plugin.version>1.3.5</replacer.plugin.version>
|
||||
<jar.plugin.version>3.0.2</jar.plugin.version>
|
||||
<jboss.as.plugin.version>7.5.Final</jboss.as.plugin.version>
|
||||
<jmeter.plugin.version>1.9.0</jmeter.plugin.version>
|
||||
<jmeter.analysis.plugin.version>1.0.4</jmeter.analysis.plugin.version>
|
||||
|
@ -178,7 +180,6 @@
|
|||
<module>server-spi-private</module>
|
||||
<module>saml-core-api</module>
|
||||
<module>saml-core</module>
|
||||
<module>proxy</module>
|
||||
<module>federation</module>
|
||||
<module>services</module>
|
||||
<module>themes</module>
|
||||
|
@ -360,6 +361,7 @@
|
|||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<version>${log4j.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
|
@ -621,6 +623,16 @@
|
|||
<artifactId>wildfly-web-common</artifactId>
|
||||
<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>
|
||||
|
@ -921,6 +933,16 @@
|
|||
<artifactId>keycloak-wildfly-adapter</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</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>
|
||||
|
@ -1386,6 +1408,11 @@
|
|||
<argLine>${surefire.memory.settings}</argLine>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>${jar.plugin.version}</version>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.google.code.maven-replacer-plugin</groupId>
|
||||
<artifactId>maven-replacer-plugin</artifactId>
|
||||
|
@ -1474,6 +1501,46 @@
|
|||
</build>
|
||||
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>community</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>!product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<product.name>Keycloak</product.name>
|
||||
<product.name.full>Keycloak</product.name.full>
|
||||
<product.slot>keycloak</product.slot>
|
||||
<product.wildfly.console.slot>main</product.wildfly.console.slot>
|
||||
<product.name-html>\u003Cdiv class="kc-logo-text"\u003E\u003Cspan\u003EKeycloak\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E</product.name-html>
|
||||
<product.version>${project.version}</product.version>
|
||||
<product.default-profile>community</product.default-profile>
|
||||
</properties>
|
||||
<modules>
|
||||
<module>proxy</module>
|
||||
</modules>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>product</id>
|
||||
<activation>
|
||||
<property>
|
||||
<name>product</name>
|
||||
</property>
|
||||
</activation>
|
||||
<properties>
|
||||
<product.name.full>Red Hat Single Sign-On</product.name.full>
|
||||
<product.name>rh-sso</product.name>
|
||||
<product.slot>rh-sso</product.slot>
|
||||
<product.wildfly.console.slot>eap</product.wildfly.console.slot>
|
||||
<product.name-html>\u003Cstrong\u003ERed Hat\u003C\u002Fstrong\u003E\u003Csup\u003E\u00AE\u003C\u002Fsup\u003E Single Sign On</product.name-html>
|
||||
<product.version>${project.version}</product.version>
|
||||
<product.default-profile>product</product.default-profile>
|
||||
<product.filename.version>7.1</product.filename.version>
|
||||
</properties>
|
||||
</profile>
|
||||
|
||||
<profile>
|
||||
<id>distribution</id>
|
||||
<modules>
|
||||
|
|
|
@ -33,6 +33,7 @@ import org.keycloak.migration.migrators.MigrateTo2_2_0;
|
|||
import org.keycloak.migration.migrators.MigrateTo2_3_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo2_5_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo3_0_0;
|
||||
import org.keycloak.migration.migrators.MigrateTo3_1_0;
|
||||
import org.keycloak.migration.migrators.Migration;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
|
@ -58,7 +59,8 @@ public class MigrationModelManager {
|
|||
new MigrateTo2_2_0(),
|
||||
new MigrateTo2_3_0(),
|
||||
new MigrateTo2_5_0(),
|
||||
new MigrateTo3_0_0()
|
||||
new MigrateTo3_0_0(),
|
||||
new MigrateTo3_1_0()
|
||||
};
|
||||
|
||||
public static void migrate(KeycloakSession session) {
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.migration.migrators;
|
||||
|
||||
|
||||
import org.keycloak.migration.ModelVersion;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:bburke@redhat.com">Bill Burke</a>
|
||||
*/
|
||||
public class MigrateTo3_1_0 implements Migration {
|
||||
|
||||
public static final ModelVersion VERSION = new ModelVersion("3.1.0");
|
||||
|
||||
@Override
|
||||
public void migrate(KeycloakSession session) {
|
||||
for (RealmModel realm : session.realms().getRealms()) {
|
||||
if (realm.getBrowserSecurityHeaders() != null) {
|
||||
|
||||
Map<String, String> browserSecurityHeaders = new HashMap<>(realm.getBrowserSecurityHeaders());
|
||||
browserSecurityHeaders.put("xRobotsTag", "none");
|
||||
|
||||
realm.setBrowserSecurityHeaders(Collections.unmodifiableMap(browserSecurityHeaders));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ModelVersion getVersion() {
|
||||
return VERSION;
|
||||
}
|
||||
|
||||
}
|
|
@ -34,11 +34,13 @@ public class BrowserSecurityHeaders {
|
|||
headerMap.put("xFrameOptions", "X-Frame-Options");
|
||||
headerMap.put("contentSecurityPolicy", "Content-Security-Policy");
|
||||
headerMap.put("xContentTypeOptions", "X-Content-Type-Options");
|
||||
headerMap.put("xRobotsTag", "X-Robots-Tag");
|
||||
|
||||
Map<String, String> dh = new HashMap<>();
|
||||
dh.put("xFrameOptions", "SAMEORIGIN");
|
||||
dh.put("contentSecurityPolicy", "frame-src 'self'");
|
||||
dh.put("xContentTypeOptions", "nosniff");
|
||||
dh.put("xRobotsTag", "none");
|
||||
|
||||
defaultHeaders = Collections.unmodifiableMap(dh);
|
||||
headerAttributeMap = Collections.unmodifiableMap(headerMap);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue