parent
27650ab816
commit
eed944292b
15 changed files with 310 additions and 36 deletions
|
@ -25,7 +25,7 @@ import org.hibernate.jpa.boot.internal.ParsedPersistenceXmlDescriptor;
|
||||||
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
|
import org.hibernate.jpa.boot.internal.PersistenceXmlParser;
|
||||||
import org.hibernate.jpa.boot.spi.Bootstrap;
|
import org.hibernate.jpa.boot.spi.Bootstrap;
|
||||||
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
import org.keycloak.connections.jpa.entityprovider.JpaEntityProvider;
|
||||||
import org.keycloak.connections.jpa.entityprovider.ProxyClassLoader;
|
import org.keycloak.utils.ProxyClassLoader;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -118,6 +118,7 @@
|
||||||
<xmlsec.version>2.2.3</xmlsec.version>
|
<xmlsec.version>2.2.3</xmlsec.version>
|
||||||
<glassfish.json.version>1.1.6</glassfish.json.version>
|
<glassfish.json.version>1.1.6</glassfish.json.version>
|
||||||
<wildfly.common.version>1.6.0.Final</wildfly.common.version>
|
<wildfly.common.version>1.6.0.Final</wildfly.common.version>
|
||||||
|
<nashorn.version>15.3</nashorn.version>
|
||||||
<ua-parser.version>1.5.2</ua-parser.version>
|
<ua-parser.version>1.5.2</ua-parser.version>
|
||||||
<picketbox.version>5.0.3.Final</picketbox.version>
|
<picketbox.version>5.0.3.Final</picketbox.version>
|
||||||
<google.guava.version>30.1-jre</google.guava.version>
|
<google.guava.version>30.1-jre</google.guava.version>
|
||||||
|
@ -592,6 +593,11 @@
|
||||||
<artifactId>jakarta.json</artifactId>
|
<artifactId>jakarta.json</artifactId>
|
||||||
<version>${glassfish.json.version}</version>
|
<version>${glassfish.json.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.nashorn</groupId>
|
||||||
|
<artifactId>nashorn-core</artifactId>
|
||||||
|
<version>${nashorn.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<!-- Twitter -->
|
<!-- Twitter -->
|
||||||
<dependency>
|
<dependency>
|
||||||
|
|
|
@ -19,13 +19,20 @@ package org.keycloak.quarkus.runtime.integration;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLClassLoader;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.Config;
|
||||||
import org.keycloak.platform.Platform;
|
import org.keycloak.platform.Platform;
|
||||||
import org.keycloak.platform.PlatformProvider;
|
import org.keycloak.platform.PlatformProvider;
|
||||||
import org.keycloak.quarkus.runtime.InitializationException;
|
import org.keycloak.quarkus.runtime.InitializationException;
|
||||||
|
@ -159,4 +166,10 @@ public class QuarkusPlatform implements PlatformProvider {
|
||||||
private void reset() {
|
private void reset() {
|
||||||
deferredExceptions.clear();
|
deferredExceptions.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig) {
|
||||||
|
// It is fine to return null assuming that nashorn and it's dependencies are included on the classpath (usually "providers" directory)
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* Copyright 2016 Red Hat, Inc. and/or its affiliates
|
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||||
* and other contributors as indicated by the @author tags.
|
* and other contributors as indicated by the @author tags.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
@ -13,13 +13,18 @@
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.keycloak.connections.jpa.entityprovider;
|
package org.keycloak.utils;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.Enumeration;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -29,7 +34,8 @@ import java.util.Set;
|
||||||
* Effectively it forms a proxy to one or more other classloaders.
|
* Effectively it forms a proxy to one or more other classloaders.
|
||||||
*
|
*
|
||||||
* The way it works:
|
* The way it works:
|
||||||
* - Get all (unique) classloaders from all provided classes
|
* - Get list of classloaders, which will be used as "delegates" when loaded classes or resources.
|
||||||
|
* - Can be retrived from provided classloaders or alternatively from the provided classes where the "delegate classloaders" will be determined from the classloaders of given classes
|
||||||
* - For each class or resource that is 'requested':
|
* - For each class or resource that is 'requested':
|
||||||
* - First try all provided classloaders and if we have a match, return that
|
* - First try all provided classloaders and if we have a match, return that
|
||||||
* - If no match was found: proceed with 'normal' classloading in 'current classpath' scope
|
* - If no match was found: proceed with 'normal' classloading in 'current classpath' scope
|
||||||
|
@ -41,17 +47,28 @@ public class ProxyClassLoader extends ClassLoader {
|
||||||
|
|
||||||
private Set<ClassLoader> classloaders;
|
private Set<ClassLoader> classloaders;
|
||||||
|
|
||||||
public ProxyClassLoader(Collection<Class<?>> classes, ClassLoader parentClassLoader) {
|
/**
|
||||||
super(parentClassLoader);
|
* Init classloader with the list of given delegates
|
||||||
init(classes);
|
* @param delegateClassLoaders
|
||||||
|
*/
|
||||||
|
public ProxyClassLoader(ClassLoader... delegateClassLoaders) {
|
||||||
|
if (delegateClassLoaders == null || delegateClassLoaders.length == 0) {
|
||||||
|
throw new IllegalStateException("At least one classloader to delegate must be provided");
|
||||||
|
}
|
||||||
|
classloaders = new LinkedHashSet<>();
|
||||||
|
classloaders.addAll(Arrays.asList(delegateClassLoaders));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all unique classloaders from the provided classes to be used as "Delegate classloaders"
|
||||||
|
* @param classes
|
||||||
|
*/
|
||||||
public ProxyClassLoader(Collection<Class<?>> classes) {
|
public ProxyClassLoader(Collection<Class<?>> classes) {
|
||||||
init(classes);
|
init(classes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void init(Collection<Class<?>> classes) {
|
private void init(Collection<Class<?>> classes) {
|
||||||
classloaders = new HashSet<>();
|
classloaders = new LinkedHashSet<>();
|
||||||
for (Class<?> clazz : classes) {
|
for (Class<?> clazz : classes) {
|
||||||
classloaders.add(clazz.getClassLoader());
|
classloaders.add(clazz.getClassLoader());
|
||||||
}
|
}
|
||||||
|
@ -84,4 +101,33 @@ public class ProxyClassLoader extends ClassLoader {
|
||||||
return super.getResource(name);
|
return super.getResource(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Enumeration<URL> getResources(String name) throws IOException {
|
||||||
|
final LinkedHashSet<URL> resourceUrls = new LinkedHashSet();
|
||||||
|
|
||||||
|
for (ClassLoader classloader : classloaders) {
|
||||||
|
Enumeration<URL> child = classloader.getResources(name);
|
||||||
|
|
||||||
|
while (child.hasMoreElements()) {
|
||||||
|
resourceUrls.add(child.nextElement());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Enumeration<URL>() {
|
||||||
|
final Iterator<URL> resourceUrlIterator = resourceUrls.iterator();
|
||||||
|
|
||||||
|
public boolean hasMoreElements() {
|
||||||
|
return this.resourceUrlIterator.hasNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL nextElement() {
|
||||||
|
return (URL)this.resourceUrlIterator.next();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "ProxyClassLoader: Delegates: " + classloaders;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -19,6 +19,8 @@ package org.keycloak.platform;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
import org.keycloak.Config;
|
||||||
|
|
||||||
public interface PlatformProvider {
|
public interface PlatformProvider {
|
||||||
|
|
||||||
void onStartup(Runnable runnable);
|
void onStartup(Runnable runnable);
|
||||||
|
@ -33,4 +35,17 @@ public interface PlatformProvider {
|
||||||
*/
|
*/
|
||||||
File getTmpDirectory();
|
File getTmpDirectory();
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns classloader to load script engine. Classloader should contain the implementation of {@link javax.script.ScriptEngineFactory}
|
||||||
|
* and it's definition inside META-INF/services of the jar file(s), which will be provided by this classloader.
|
||||||
|
*
|
||||||
|
* This method can return null and in that case, the default Keycloak services classloader will be used for load script engine. Note that java versions earlier than 15 always contain
|
||||||
|
* the "nashorn" script engine by default on the classpath (it is part of the Java platform itself) and hence for them it is always fine to return null (unless you want to override default engine)
|
||||||
|
*
|
||||||
|
* @param scriptProviderConfig Configuration scope of the "default" provider of "scripting" SPI. It can contain some config properties for the classloader (EG. file path)
|
||||||
|
* @return classloader or null
|
||||||
|
*/
|
||||||
|
ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,9 @@ import javax.script.ScriptEngineManager;
|
||||||
import javax.script.ScriptException;
|
import javax.script.ScriptException;
|
||||||
|
|
||||||
import org.keycloak.models.ScriptModel;
|
import org.keycloak.models.ScriptModel;
|
||||||
|
import org.keycloak.platform.Platform;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
import org.keycloak.utils.ProxyClassLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A {@link ScriptingProvider} that uses a {@link ScriptEngineManager} to evaluate scripts with a {@link ScriptEngine}.
|
* A {@link ScriptingProvider} that uses a {@link ScriptEngineManager} to evaluate scripts with a {@link ScriptEngine}.
|
||||||
|
@ -125,8 +127,17 @@ public class DefaultScriptingProvider implements ScriptingProvider {
|
||||||
private ScriptEngine lookupScriptEngineFor(ScriptModel script) {
|
private ScriptEngine lookupScriptEngineFor(ScriptModel script) {
|
||||||
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
ClassLoader cl = Thread.currentThread().getContextClassLoader();
|
||||||
try {
|
try {
|
||||||
Thread.currentThread().setContextClassLoader(DefaultScriptingProvider.class.getClassLoader());
|
ClassLoader scriptClassLoader = Platform.getPlatform().getScriptEngineClassLoader(factory.getConfig());
|
||||||
return factory.getScriptEngineManager().getEngineByMimeType(script.getMimeType());
|
|
||||||
|
// Also need to use classloader of keycloak services itself to be able to use keycloak classes in the scripts
|
||||||
|
if (scriptClassLoader != null) {
|
||||||
|
scriptClassLoader = new ProxyClassLoader(scriptClassLoader, DefaultScriptingProvider.class.getClassLoader());
|
||||||
|
} else {
|
||||||
|
scriptClassLoader = DefaultScriptingProvider.class.getClassLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread.currentThread().setContextClassLoader(scriptClassLoader);
|
||||||
|
return new ScriptEngineManager().getEngineByMimeType(script.getMimeType());
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
Thread.currentThread().setContextClassLoader(cl);
|
Thread.currentThread().setContextClassLoader(cl);
|
||||||
|
|
|
@ -23,10 +23,8 @@ import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.protocol.oidc.OIDCWellKnownProviderFactory;
|
|
||||||
|
|
||||||
import javax.script.ScriptEngine;
|
import javax.script.ScriptEngine;
|
||||||
import javax.script.ScriptEngineManager;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
* @author <a href="mailto:thomas.darimont@gmail.com">Thomas Darimont</a>
|
||||||
|
@ -35,30 +33,28 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
|
||||||
|
|
||||||
private static final Logger logger = Logger.getLogger(DefaultScriptingProviderFactory.class);
|
private static final Logger logger = Logger.getLogger(DefaultScriptingProviderFactory.class);
|
||||||
|
|
||||||
static final String ID = "script-based-auth";
|
static final String ID = "default";
|
||||||
|
|
||||||
private ScriptEngineManager scriptEngineManager;
|
|
||||||
|
|
||||||
private boolean enableScriptEngineCache;
|
private boolean enableScriptEngineCache;
|
||||||
|
|
||||||
// Key is mime-type. Value is engine for the particular mime-type. Cache can be used when the scriptEngine can be shared across multiple threads / requests (which is the case for nashorn)
|
// Key is mime-type. Value is engine for the particular mime-type. Cache can be used when the scriptEngine can be shared across multiple threads / requests (which is the case for nashorn)
|
||||||
private Map<String, ScriptEngine> scriptEngineCache;
|
private Map<String, ScriptEngine> scriptEngineCache;
|
||||||
|
|
||||||
|
private Config.Scope config;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ScriptingProvider create(KeycloakSession session) {
|
public ScriptingProvider create(KeycloakSession session) {
|
||||||
lazyInit();
|
|
||||||
|
|
||||||
return new DefaultScriptingProvider(this);
|
return new DefaultScriptingProvider(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(Config.Scope config) {
|
public void init(Config.Scope config) {
|
||||||
|
this.config = config;
|
||||||
this.enableScriptEngineCache = config.getBoolean("enable-script-engine-cache", true);
|
this.enableScriptEngineCache = config.getBoolean("enable-script-engine-cache", true);
|
||||||
logger.debugf("Enable script engine cache: %b", this.enableScriptEngineCache);
|
logger.debugf("Enable script engine cache: %b", this.enableScriptEngineCache);
|
||||||
|
if (enableScriptEngineCache) {
|
||||||
|
scriptEngineCache = new ConcurrentHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScriptEngineManager getScriptEngineManager() {
|
|
||||||
return scriptEngineManager;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isEnableScriptEngineCache() {
|
boolean isEnableScriptEngineCache() {
|
||||||
|
@ -69,6 +65,10 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
|
||||||
return scriptEngineCache;
|
return scriptEngineCache;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Config.Scope getConfig() {
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void postInit(KeycloakSessionFactory factory) {
|
public void postInit(KeycloakSessionFactory factory) {
|
||||||
//NOOP
|
//NOOP
|
||||||
|
@ -84,17 +84,4 @@ public class DefaultScriptingProviderFactory implements ScriptingProviderFactory
|
||||||
return ID;
|
return ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void lazyInit() {
|
|
||||||
if (scriptEngineManager == null) {
|
|
||||||
synchronized (this) {
|
|
||||||
if (scriptEngineManager == null) {
|
|
||||||
scriptEngineManager = new ScriptEngineManager();
|
|
||||||
if (enableScriptEngineCache) {
|
|
||||||
scriptEngineCache = new ConcurrentHashMap<>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -257,4 +257,18 @@
|
||||||
</filterset>
|
</filterset>
|
||||||
</copy>
|
</copy>
|
||||||
</target>
|
</target>
|
||||||
|
|
||||||
|
<!-- Needed on Java 15 and later -->
|
||||||
|
<target name="deploy-nashorn-module">
|
||||||
|
<copy todir="${cli.tmp.dir}">
|
||||||
|
<resources>
|
||||||
|
<file file="${common.resources}/jboss-cli/deploy-nashorn-module.cli"/>
|
||||||
|
</resources>
|
||||||
|
<filterset>
|
||||||
|
<filter token="NASHORN_JAR" value="${project.build.directory}/nashorn/nashorn-core-${nashorn.version}.jar"/>
|
||||||
|
</filterset>
|
||||||
|
</copy>
|
||||||
|
<echo>Nashorn module deployed</echo>
|
||||||
|
</target>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,5 @@
|
||||||
|
echo *** Installing nashorn-core module ***
|
||||||
|
module add --module-root-dir=../modules/system/layers/keycloak/ \
|
||||||
|
--name=org.openjdk.nashorn.nashorn-core \
|
||||||
|
--resources=@NASHORN_JAR@ \
|
||||||
|
--dependencies=asm.asm,jdk.dynalink
|
|
@ -732,5 +732,62 @@
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<!-- Nashorn script engine needs to be manually added for the new Java versions as it is not part of the JDK anymore -->
|
||||||
|
<profile>
|
||||||
|
<id>jdk15</id>
|
||||||
|
<activation>
|
||||||
|
<jdk>[15,)</jdk>
|
||||||
|
</activation>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<pluginManagement>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-nashorn-module</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<artifactItems>
|
||||||
|
<artifactItem>
|
||||||
|
<groupId>org.openjdk.nashorn</groupId>
|
||||||
|
<artifactId>nashorn-core</artifactId>
|
||||||
|
<version>${nashorn.version}</version>
|
||||||
|
<type>jar</type>
|
||||||
|
</artifactItem>
|
||||||
|
</artifactItems>
|
||||||
|
<outputDirectory>${project.build.directory}/nashorn</outputDirectory>
|
||||||
|
<overWriteIfNewer>true</overWriteIfNewer>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-antrun-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>deploy-nashorn-module</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>run</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<target>
|
||||||
|
<ant antfile="${common.resources}/ant/configure.xml" target="deploy-nashorn-module" />
|
||||||
|
</target>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</pluginManagement>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -262,5 +262,43 @@
|
||||||
<auth.server.quarkus.cluster.config>ha</auth.server.quarkus.cluster.config>
|
<auth.server.quarkus.cluster.config>ha</auth.server.quarkus.cluster.config>
|
||||||
</properties>
|
</properties>
|
||||||
</profile>
|
</profile>
|
||||||
|
|
||||||
|
<!-- Nashorn script engine needs to be manually added for the new Java versions as it is not part of the JDK anymore -->
|
||||||
|
<profile>
|
||||||
|
<id>jdk15</id>
|
||||||
|
<activation>
|
||||||
|
<jdk>[15,)</jdk>
|
||||||
|
</activation>
|
||||||
|
|
||||||
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.nashorn</groupId>
|
||||||
|
<artifactId>nashorn-core</artifactId>
|
||||||
|
</dependency>
|
||||||
|
</dependencies>
|
||||||
|
|
||||||
|
<build>
|
||||||
|
<plugins>
|
||||||
|
<plugin>
|
||||||
|
<groupId>org.apache.maven.plugins</groupId>
|
||||||
|
<artifactId>maven-dependency-plugin</artifactId>
|
||||||
|
<executions>
|
||||||
|
<execution>
|
||||||
|
<id>copy-dependencies-quarkus</id>
|
||||||
|
<phase>generate-resources</phase>
|
||||||
|
<goals>
|
||||||
|
<goal>copy-dependencies</goal>
|
||||||
|
</goals>
|
||||||
|
<configuration>
|
||||||
|
<outputDirectory>${auth.server.home}/providers</outputDirectory>
|
||||||
|
<includeArtifactIds>nashorn-core,asm,asm-util,asm-commons</includeArtifactIds>
|
||||||
|
</configuration>
|
||||||
|
</execution>
|
||||||
|
</executions>
|
||||||
|
</plugin>
|
||||||
|
</plugins>
|
||||||
|
</build>
|
||||||
|
</profile>
|
||||||
|
|
||||||
</profiles>
|
</profiles>
|
||||||
</project>
|
</project>
|
|
@ -254,6 +254,12 @@
|
||||||
<artifactId>mssql-jdbc</artifactId>
|
<artifactId>mssql-jdbc</artifactId>
|
||||||
<version>${mssql.driver.version}</version>
|
<version>${mssql.driver.version}</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Nashorn -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.openjdk.nashorn</groupId>
|
||||||
|
<artifactId>nashorn-core</artifactId>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.keycloak.Config;
|
||||||
import org.keycloak.platform.PlatformProvider;
|
import org.keycloak.platform.PlatformProvider;
|
||||||
|
|
||||||
public class TestPlatform implements PlatformProvider {
|
public class TestPlatform implements PlatformProvider {
|
||||||
|
@ -70,4 +71,10 @@ public class TestPlatform implements PlatformProvider {
|
||||||
}
|
}
|
||||||
return tmpDir;
|
return tmpDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig) {
|
||||||
|
// It is fine to return null as nashorn should be automatically included on the classpath of testsuite utils
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,11 @@ package org.keycloak.provider.wildfly;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
|
import org.jboss.modules.Module;
|
||||||
|
import org.jboss.modules.ModuleIdentifier;
|
||||||
|
import org.jboss.modules.ModuleLoadException;
|
||||||
|
import org.keycloak.Config;
|
||||||
|
import org.keycloak.common.util.Environment;
|
||||||
import org.keycloak.platform.PlatformProvider;
|
import org.keycloak.platform.PlatformProvider;
|
||||||
import org.keycloak.services.ServicesLogger;
|
import org.keycloak.services.ServicesLogger;
|
||||||
|
|
||||||
|
@ -27,6 +32,9 @@ public class WildflyPlatform implements PlatformProvider {
|
||||||
|
|
||||||
private static final Logger log = Logger.getLogger(WildflyPlatform.class);
|
private static final Logger log = Logger.getLogger(WildflyPlatform.class);
|
||||||
|
|
||||||
|
// In this module, the attempt to load script engine will be done by default
|
||||||
|
private static final String DEFAULT_SCRIPT_ENGINE_MODULE = "org.openjdk.nashorn.nashorn-core";
|
||||||
|
|
||||||
Runnable shutdownHook;
|
Runnable shutdownHook;
|
||||||
|
|
||||||
private File tmpDir;
|
private File tmpDir;
|
||||||
|
@ -74,4 +82,25 @@ public class WildflyPlatform implements PlatformProvider {
|
||||||
}
|
}
|
||||||
return tmpDir;
|
return tmpDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ClassLoader getScriptEngineClassLoader(Config.Scope scriptProviderConfig) {
|
||||||
|
String engineModule = scriptProviderConfig.get("script-engine-module");
|
||||||
|
if (engineModule == null) {
|
||||||
|
engineModule = DEFAULT_SCRIPT_ENGINE_MODULE;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Module module = Module.getContextModuleLoader().loadModule(ModuleIdentifier.fromString(engineModule));
|
||||||
|
log.infof("Found script engine module '%s'", engineModule);
|
||||||
|
return module.getClassLoader();
|
||||||
|
} catch (ModuleLoadException mle) {
|
||||||
|
if (WildflyUtil.getJavaVersion() >= 15) {
|
||||||
|
log.warnf("Cannot find script engine in the JBoss module '%s'. Please add JavaScript engine to the specified JBoss Module or make sure it is available on the classpath", engineModule);
|
||||||
|
} else {
|
||||||
|
log.debugf("Cannot find script engine in the JBoss module '%s'. Will fallback to the default script engine", engineModule);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2022 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.provider.wildfly;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
|
||||||
|
*/
|
||||||
|
public class WildflyUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return major version of java runtime like 8, 11 or 17
|
||||||
|
*/
|
||||||
|
public static int getJavaVersion() {
|
||||||
|
String version = System.getProperty("java.version");
|
||||||
|
if (version.startsWith("1.")) {
|
||||||
|
version = version.substring(2, 3);
|
||||||
|
} else {
|
||||||
|
int dot = version.indexOf(".");
|
||||||
|
if (dot != -1) version = version.substring(0, dot);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Integer.parseInt(version);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue