Merge pull request #392 from stianst/master

Server config
This commit is contained in:
Stian Thorgersen 2014-05-16 17:34:13 +01:00
commit 6714e2b87f
97 changed files with 1354 additions and 1355 deletions

View file

@ -5,10 +5,8 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderFactoryLoader;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
/**

View file

@ -0,0 +1,27 @@
package org.keycloak.audit;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuditListenerSpi implements Spi {
@Override
public String getName() {
return "audit-listener";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuditListener.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuditListenerFactory.class;
}
}

View file

@ -0,0 +1,27 @@
package org.keycloak.audit;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuditProviderSpi implements Spi {
@Override
public String getName() {
return "audit";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuditProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuditProviderFactory.class;
}
}

View file

@ -0,0 +1,2 @@
org.keycloak.audit.AuditProviderSpi
org.keycloak.audit.AuditListenerSpi

View file

@ -1,10 +1,10 @@
package org.keycloak.audit.log;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -21,7 +21,7 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
}
@Override
public void init() {
public void init(Config.Scope config) {
}
@Override
@ -33,9 +33,4 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
return ID;
}
@Override
public boolean lazyLoad() {
return false;
}
}

View file

@ -1,9 +1,9 @@
package org.keycloak.audit.jpa;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
@ -22,7 +22,7 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
}
@Override
public void init() {
public void init(Config.Scope config) {
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store");
}
@ -36,9 +36,4 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
return ID;
}
@Override
public boolean lazyLoad() {
return true;
}
}

View file

@ -2,13 +2,16 @@ package org.keycloak.audit.mongo;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import java.net.UnknownHostException;
import java.util.Collections;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -29,11 +32,28 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
}
@Override
public void init() {
public void init(Config.Scope config) {
try {
client = new MongoClient(System.getProperty(MONGO_HOST, "localhost"), Integer.parseInt(System.getProperty(MONGO_PORT, "27017")));
String host = config.get("host", ServerAddress.defaultHost());
int port = config.getInt("port", ServerAddress.defaultPort());
String dbName = config.get("db", "keycloak-audit");
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
String user = config.get("user");
String password = config.get("password");
if (user != null && password != null) {
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
} else {
client = new MongoClient(host, port);
}
client.setWriteConcern(WriteConcern.UNACKNOWLEDGED);
db = client.getDB(System.getProperty(MONGO_DB_NAME, "keycloak-audit"));
db = client.getDB(dbName);
if (clearOnStartup) {
db.getCollection("audit").drop();
}
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
@ -49,9 +69,4 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
return ID;
}
@Override
public boolean lazyLoad() {
return true;
}
}

View file

@ -4,14 +4,15 @@ import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.Event;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import java.util.HashMap;
import java.util.Map;
import java.util.ServiceLoader;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -23,9 +24,14 @@ public abstract class AbstractAuditProviderTest {
@Before
public void before() {
ProviderFactoryLoader<AuditProvider> loader = ProviderFactoryLoader.create(AuditProviderFactory.class);
factory = loader.find(getProviderId());
factory.init();
String providerId = getProviderId();
ServiceLoader<AuditProviderFactory> factories = ServiceLoader.load(AuditProviderFactory.class);
for (AuditProviderFactory f : factories) {
if (f.getId().equals(providerId)) {
factory = f;
factory.init(Config.scope("audit", providerId));
}
}
provider = factory.create(null);
}

View file

@ -0,0 +1,27 @@
package org.keycloak.authentication;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class AuthenticationSpi implements Spi {
@Override
public String getName() {
return "authentication";
}
@Override
public Class<? extends Provider> getProviderClass() {
return AuthenticationProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return AuthenticationProviderFactory.class;
}
}

View file

@ -0,0 +1 @@
org.keycloak.authentication.AuthenticationSpi

View file

@ -1,10 +1,10 @@
package org.keycloak.authentication.model;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -17,7 +17,7 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio
}
@Override
public void init() {
public void init(Config.Scope config) {
}
@Override
@ -29,8 +29,4 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio
return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL;
}
@Override
public boolean lazyLoad() {
return false;
}
}

View file

@ -1,5 +1,6 @@
package org.keycloak.authentication.model;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
@ -16,7 +17,7 @@ public class ModelAuthenticationProviderFactory implements AuthenticationProvide
}
@Override
public void init() {
public void init(Config.Scope config) {
}
@Override
@ -28,8 +29,4 @@ public class ModelAuthenticationProviderFactory implements AuthenticationProvide
return AuthProviderConstants.PROVIDER_NAME_MODEL;
}
@Override
public boolean lazyLoad() {
return false;
}
}

View file

@ -1,28 +1,25 @@
package org.keycloak.authentication.picketlink;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.jboss.logging.Logger;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.authentication.AuthProviderStatus;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthProviderStatus;
import org.keycloak.authentication.AuthUser;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderException;
import org.keycloak.models.RealmModel;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.keycloak.util.ProviderLoader;
import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* AuthenticationProvider, which delegates authentication to picketlink
*

View file

@ -1,5 +1,6 @@
package org.keycloak.authentication.picketlink;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
@ -17,7 +18,7 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr
}
@Override
public void init() {
public void init(Config.Scope config) {
}
@Override
@ -29,8 +30,4 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr
return AuthProviderConstants.PROVIDER_NAME_PICKETLINK;
}
@Override
public boolean lazyLoad() {
return false;
}
}

View file

@ -0,0 +1,129 @@
package org.keycloak;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Config {
private static ConfigProvider configProvider = new SystemPropertiesConfigProvider();
public static void init(ConfigProvider configProvider) {
Config.configProvider = configProvider;
}
public static String getAdminRealm() {
return configProvider.scope("admin").get("realm", "keycloak-admin");
}
public static String getProvider(String spi) {
return configProvider.getProvider(spi);
}
public static Scope scope(String... scope) {
return configProvider.scope(scope);
}
public static interface ConfigProvider {
String getProvider(String spi);
Scope scope(String... scope);
}
public static class SystemPropertiesConfigProvider implements ConfigProvider {
@Override
public String getProvider(String spi) {
return System.getProperties().getProperty("keycloak." + spi + ".provider");
}
@Override
public Scope scope(String... scope) {
StringBuilder sb = new StringBuilder();
sb.append("keycloak.");
for (String s : scope) {
sb.append(s);
sb.append(".");
}
return new SystemPropertiesScope(sb.toString());
}
}
public static class SystemPropertiesScope implements Scope {
private String prefix;
public SystemPropertiesScope(String prefix) {
this.prefix = prefix;
}
@Override
public String get(String key) {
return get(key, null);
}
@Override
public String get(String key, String defaultValue) {
return System.getProperty(prefix + key, defaultValue);
}
@Override
public Integer getInt(String key) {
return getInt(key, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
String v = get(key, null);
return v != null ? Integer.parseInt(v) : defaultValue;
}
@Override
public Long getLong(String key) {
return getLong(key, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
String v = get(key, null);
return v != null ? Long.parseLong(v) : defaultValue;
}
@Override
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
String v = get(key, null);
return v != null ? Boolean.parseBoolean(v) : defaultValue;
}
}
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public static interface Scope {
String get(String key);
String get(String key, String defaultValue);
Integer getInt(String key);
Integer getInt(String key, Integer defaultValue);
Long getLong(String key);
Long getLong(String key, Long defaultValue);
Boolean getBoolean(String key);
Boolean getBoolean(String key, Boolean defaultValue);
}
}

View file

@ -1,5 +1,7 @@
package org.keycloak.provider;
import org.keycloak.Config;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@ -7,12 +9,10 @@ public interface ProviderFactory<T extends Provider> {
public T create(ProviderSession providerSession);
public void init();
public void init(Config.Scope config);
public void close();
public String getId();
public boolean lazyLoad();
}

View file

@ -1,102 +0,0 @@
package org.keycloak.provider;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ProviderFactoryLoader<T extends Provider> implements Iterable<ProviderFactory<T>> {
private final Map<String, ProviderFactory<T>> factories = new HashMap<String, ProviderFactory<T>>();
private ProviderFactoryLoader(ServiceLoader<? extends ProviderFactory> serviceLoader) {
for (ProviderFactory p : serviceLoader) {
if (!System.getProperties().containsKey(p.getClass().getName() + ".disabled")) {
if (p.lazyLoad()) {
p = new LazyProviderFactory(p);
}
factories.put(p.getId(), p);
}
}
}
public static ProviderFactoryLoader create(Class<? extends ProviderFactory> service) {
return new ProviderFactoryLoader(ServiceLoader.load(service));
}
public static ProviderFactoryLoader create(Class<? extends ProviderFactory> service, ClassLoader loader) {
return new ProviderFactoryLoader(ServiceLoader.load(service, loader));
}
public ProviderFactory find(String id) {
return factories.get(id);
}
@Override
public Iterator<ProviderFactory<T>> iterator() {
return factories.values().iterator();
}
public Set<String> providerIds() {
return factories.keySet();
}
public void init() {
for (ProviderFactory p : factories.values()) {
p.init();
}
}
public void close() {
for (ProviderFactory p : factories.values()) {
p.close();
}
}
private class LazyProviderFactory<T extends Provider> implements ProviderFactory<T> {
private final ProviderFactory<T> factory;
private volatile boolean initialized = false;
private LazyProviderFactory(ProviderFactory<T> factory) {
this.factory = factory;
}
@Override
public synchronized T create(ProviderSession providerSession) {
if (!initialized) {
factory.init();
initialized = true;
}
return factory.create(providerSession);
}
@Override
public void init() {
// do nothing
}
@Override
public synchronized void close() {
if (initialized) {
factory.close();
}
}
@Override
public String getId() {
return factory.getId();
}
@Override
public boolean lazyLoad() {
return false;
}
}
}

View file

@ -1,7 +1,5 @@
package org.keycloak.provider;
import java.util.Set;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
@ -11,12 +9,6 @@ public interface ProviderSessionFactory {
void close();
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz);
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id);
Set<String> providerIds(Class<? extends Provider> clazz);
void init();
}

View file

@ -0,0 +1,12 @@
package org.keycloak.provider;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public interface Spi {
public String getName();
public Class<? extends Provider> getProviderClass();
public Class<? extends ProviderFactory> getProviderFactoryClass();
}

View file

@ -0,0 +1,263 @@
/*
* JBoss, Home of Professional Open Source
* Copyright 2005, JBoss Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.keycloak.util;
import java.util.Properties;
import java.io.File;
/**
* A utility class for replacing properties in strings.
*
* @author <a href="mailto:jason@planet57.com">Jason Dillon</a>
* @author <a href="Scott.Stark@jboss.org">Scott Stark</a>
* @author <a href="claudio.vesco@previnet.it">Claudio Vesco</a>
* @author <a href="mailto:adrian@jboss.com">Adrian Brock</a>
* @author <a href="mailto:dimitris@jboss.org">Dimitris Andreadis</a>
* @version <tt>$Revision: 2898 $</tt>
*/
public final class StringPropertyReplacer
{
/** New line string constant */
public static final String NEWLINE = System.getProperty("line.separator", "\n");
/** File separator value */
private static final String FILE_SEPARATOR = File.separator;
/** Path separator value */
private static final String PATH_SEPARATOR = File.pathSeparator;
/** File separator alias */
private static final String FILE_SEPARATOR_ALIAS = "/";
/** Path separator alias */
private static final String PATH_SEPARATOR_ALIAS = ":";
// States used in property parsing
private static final int NORMAL = 0;
private static final int SEEN_DOLLAR = 1;
private static final int IN_BRACKET = 2;
/**
* Go through the input string and replace any occurance of ${p} with
* the System.getProperty(p) value. If there is no such property p defined,
* then the ${p} reference will remain unchanged.
*
* If the property reference is of the form ${p:v} and there is no such property p,
* then the default value v will be returned.
*
* If the property reference is of the form ${p1,p2} or ${p1,p2:v} then
* the primary and the secondary properties will be tried in turn, before
* returning either the unchanged input, or the default value.
*
* The property ${/} is replaced with System.getProperty("file.separator")
* value and the property ${:} is replaced with System.getProperty("path.separator").
*
* @param string - the string with possible ${} references
* @return the input string with all property references replaced if any.
* If there are no valid references the input string will be returned.
*/
public static String replaceProperties(final String string)
{
return replaceProperties(string, null);
}
/**
* Go through the input string and replace any occurance of ${p} with
* the props.getProperty(p) value. If there is no such property p defined,
* then the ${p} reference will remain unchanged.
*
* If the property reference is of the form ${p:v} and there is no such property p,
* then the default value v will be returned.
*
* If the property reference is of the form ${p1,p2} or ${p1,p2:v} then
* the primary and the secondary properties will be tried in turn, before
* returning either the unchanged input, or the default value.
*
* The property ${/} is replaced with System.getProperty("file.separator")
* value and the property ${:} is replaced with System.getProperty("path.separator").
*
* @param string - the string with possible ${} references
* @param props - the source for ${x} property ref values, null means use System.getProperty()
* @return the input string with all property references replaced if any.
* If there are no valid references the input string will be returned.
*/
public static String replaceProperties(final String string, final Properties props)
{
final char[] chars = string.toCharArray();
StringBuffer buffer = new StringBuffer();
boolean properties = false;
int state = NORMAL;
int start = 0;
for (int i = 0; i < chars.length; ++i)
{
char c = chars[i];
// Dollar sign outside brackets
if (c == '$' && state != IN_BRACKET)
state = SEEN_DOLLAR;
// Open bracket immediatley after dollar
else if (c == '{' && state == SEEN_DOLLAR)
{
buffer.append(string.substring(start, i - 1));
state = IN_BRACKET;
start = i - 1;
}
// No open bracket after dollar
else if (state == SEEN_DOLLAR)
state = NORMAL;
// Closed bracket after open bracket
else if (c == '}' && state == IN_BRACKET)
{
// No content
if (start + 2 == i)
{
buffer.append("${}"); // REVIEW: Correct?
}
else // Collect the system property
{
String value = null;
String key = string.substring(start + 2, i);
// check for alias
if (FILE_SEPARATOR_ALIAS.equals(key))
{
value = FILE_SEPARATOR;
}
else if (PATH_SEPARATOR_ALIAS.equals(key))
{
value = PATH_SEPARATOR;
}
else
{
// check from the properties
if (props != null)
value = props.getProperty(key);
else
value = System.getProperty(key);
if (value == null)
{
// Check for a default value ${key:default}
int colon = key.indexOf(':');
if (colon > 0)
{
String realKey = key.substring(0, colon);
if (props != null)
value = props.getProperty(realKey);
else
value = System.getProperty(realKey);
if (value == null)
{
// Check for a composite key, "key1,key2"
value = resolveCompositeKey(realKey, props);
// Not a composite key either, use the specified default
if (value == null)
value = key.substring(colon+1);
}
}
else
{
// No default, check for a composite key, "key1,key2"
value = resolveCompositeKey(key, props);
}
}
}
if (value != null)
{
properties = true;
buffer.append(value);
}
else
{
buffer.append("${");
buffer.append(key);
buffer.append('}');
}
}
start = i + 1;
state = NORMAL;
}
}
// No properties
if (properties == false)
return string;
// Collect the trailing characters
if (start != chars.length)
buffer.append(string.substring(start, chars.length));
// Done
return buffer.toString();
}
/**
* Try to resolve a "key" from the provided properties by
* checking if it is actually a "key1,key2", in which case
* try first "key1", then "key2". If all fails, return null.
*
* It also accepts "key1," and ",key2".
*
* @param key the key to resolve
* @param props the properties to use
* @return the resolved key or null
*/
private static String resolveCompositeKey(String key, Properties props)
{
String value = null;
// Look for the comma
int comma = key.indexOf(',');
if (comma > -1)
{
// If we have a first part, try resolve it
if (comma > 0)
{
// Check the first part
String key1 = key.substring(0, comma);
if (props != null)
value = props.getProperty(key1);
else
value = System.getProperty(key1);
}
// Check the second part, if there is one and first lookup failed
if (value == null && comma < key.length() - 1)
{
String key2 = key.substring(comma + 1);
if (props != null)
value = props.getProperty(key2);
else
value = System.getProperty(key2);
}
}
// Return whatever we've found or null
return value;
}
}

View file

@ -252,12 +252,12 @@ keycloak-war-dist-all-1.0-beta-1-SNAPSHOT/
First you need to specify that you want to use <literal>mongo</literal> instead of default <literal>jpa</literal> model, and you may also specify
host, port and name of mongo database. So you can start keycloak with the command like this:
<programlisting><![CDATA[
./standalone.sh -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost
-Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak
./standalone.sh -Dkeycloak.model=mongo -Dkeycloak.model.mongo.host=localhost
-Dkeycloak.model.mongoport=27017 -Dkeycloak.model.mongo.db=keycloak
]]></programlisting>
Note that when you install MongoDB on your laptop, it's usually on localhost/270717 by default. That's why properties
<literal>keycloak.mongo.host</literal> and <literal>keycloak.mongo.port</literal> are not mandatory, but they already have
default values <literal>localhost</literal> and <literal>27017</literal> . Similarly property <literal>keycloak.mongo.db</literal>
<literal>keycloak.model.mongo.host</literal> and <literal>keycloak.model.mongo.port</literal> are not mandatory, but they already have
default values <literal>localhost</literal> and <literal>27017</literal> . Similarly property <literal>keycloak.model.mongo.db</literal>
has default value <literal>keycloak</literal> for name of underlying database. So the example above could be simplified like:
<programlisting><![CDATA[
./standalone.sh -Dkeycloak.model=mongo

View file

@ -14,6 +14,12 @@
<description/>
<dependencies>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-core</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>

View file

@ -1,11 +1,12 @@
package org.keycloak.exportimport;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderSessionFactory;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface ExportImportProvider {
void checkExportImport(KeycloakSessionFactory identitySessionFactory);
void checkExportImport(ProviderSessionFactory identitySessionFactory);
}

View file

@ -32,13 +32,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId>
@ -121,6 +114,12 @@
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId>
@ -149,10 +148,10 @@
</dependencies>
<properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
</properties>
<build>
@ -179,10 +178,10 @@
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
</systemPropertyVariables>
</configuration>
</execution>
@ -207,7 +206,7 @@
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.mongo.port}</port>
<port>${keycloak.model.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
</configuration>

View file

@ -0,0 +1,54 @@
package org.keycloak.exportimport;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ExportImportConfig {
public static final String ACTION = "keycloak.migration.action";
public static final String PROVIDER = "keycloak.migration.provider";
public static final String PROVIDER_DEFAULT = "zip";
// used for "directory" provider
public static final String DIR = "keycloak.migration.dir";
// used for "zip" provider
public static final String FILE = "keycloak.migration.zipFile";
public static final String PASSWORD = "keycloak.migration.zipPassword";
public static String getAction() {
return System.getProperty(ACTION);
}
public static void setAction(String exportImportAction) {
System.setProperty(ACTION, exportImportAction);
}
public static String getProvider() {
return System.getProperty(PROVIDER, PROVIDER_DEFAULT);
}
public static void setProvider(String exportImportProvider) {
System.setProperty(PROVIDER, exportImportProvider);
}
public static String getDir() {
return System.getProperty(DIR);
}
public static String getZipFile() {
return System.getProperty(FILE);
}
public static void setZipFile(String exportImportZipFile) {
System.setProperty(FILE, exportImportZipFile);
}
public static String getZipPassword() {
return System.getProperty(PASSWORD);
}
public static void setZipPassword(String exportImportZipPassword) {
System.setProperty(PASSWORD, exportImportZipPassword);
}
}

View file

@ -4,10 +4,10 @@ import org.jboss.logging.Logger;
import org.keycloak.exportimport.io.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.util.ProviderLoader;
/**
@ -21,8 +21,8 @@ public class ExportImportProviderImpl implements ExportImportProvider {
public static final String ACTION_IMPORT = "import";
@Override
public void checkExportImport(KeycloakSessionFactory identitySessionFactory) {
String exportImportAction = Config.getExportImportAction();
public void checkExportImport(ProviderSessionFactory providerSessionFactory) {
String exportImportAction = ExportImportConfig.getAction();
boolean export = false;
boolean importt = false;
@ -35,7 +35,8 @@ public class ExportImportProviderImpl implements ExportImportProvider {
}
if (export || importt) {
KeycloakSession session = identitySessionFactory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = session.getTransaction();
try {
transaction.begin();
@ -63,13 +64,13 @@ public class ExportImportProviderImpl implements ExportImportProvider {
}
throw new RuntimeException(e);
} finally {
session.close();
providerSession.close();
}
}
}
private ExportImportIOProvider getProvider() {
String providerId = Config.getExportImportProvider();
String providerId = ExportImportConfig.getProvider();
logger.infof("Requested migration provider: " + providerId);
Iterable<ExportImportIOProvider> providers = ProviderLoader.load(ExportImportIOProvider.class);

View file

@ -13,7 +13,7 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy;

View file

@ -1,11 +1,11 @@
package org.keycloak.exportimport.io.directory;
import java.io.File;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.io.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader;
import org.keycloak.models.Config;
import java.io.File;
/**
* Export/import into JSON files inside "tmp" directory. This implementation is used mainly for testing
@ -19,13 +19,13 @@ public class TmpDirExportImportIOProvider implements ExportImportIOProvider {
@Override
public ExportWriter getExportWriter() {
String dir = Config.getExportImportDir();
String dir = ExportImportConfig.getDir();
return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter();
}
@Override
public ImportReader getImportReader() {
String dir = Config.getExportImportDir();
String dir = ExportImportConfig.getDir();
return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader();
}

View file

@ -1,10 +1,10 @@
package org.keycloak.exportimport.io.zip;
import org.jboss.logging.Logger;
import org.keycloak.exportimport.ExportImportConfig;
import org.keycloak.exportimport.io.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader;
import org.keycloak.models.Config;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -22,8 +22,8 @@ public class EncryptedZIPIOProvider implements ExportImportIOProvider {
@Override
public ExportWriter getExportWriter() {
String zipFile = Config.getExportImportZipFile();
String zipPassword = Config.getExportImportZipPassword();
String zipFile = ExportImportConfig.getZipFile();
String zipPassword = ExportImportConfig.getZipPassword();
logger.infof("Using zip for export: " + zipFile);
if (zipFile==null || zipPassword==null) {
@ -35,8 +35,8 @@ public class EncryptedZIPIOProvider implements ExportImportIOProvider {
@Override
public ImportReader getImportReader() {
String zipFile = Config.getExportImportZipFile();
String zipPassword = Config.getExportImportZipPassword();
String zipFile = ExportImportConfig.getZipFile();
String zipPassword = ExportImportConfig.getZipPassword();
logger.infof("Using zip for import: " + zipFile);
if (zipFile==null || zipPassword==null) {

View file

@ -1,36 +1,43 @@
package org.keycloak.exportimport;
import java.util.Iterator;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.keycloak.model.test.AbstractModelTest;
import org.keycloak.model.test.ImportTest;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.util.ProviderLoader;
import java.util.Iterator;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public abstract class ExportImportTestBase {
protected KeycloakSessionFactory factory;
protected ProviderSessionFactory factory;
protected ProviderSession providerSession;
protected KeycloakSession identitySession;
protected RealmManager realmManager;
@After
public void after() {
System.getProperties().remove("keycloak.model.provider");
}
@Test
public void testExportImport() throws Exception {
// Init JPA model
Config.setModelProvider(getExportModelProvider());
factory = KeycloakApplication.createSessionFactory();
System.setProperty("keycloak.model.provider", getExportModelProvider());
factory = KeycloakApplication.createProviderSessionFactory();
// Bootstrap admin realm
beginTransaction();
@ -59,8 +66,8 @@ public abstract class ExportImportTestBase {
factory.close();
// Bootstrap mongo session and factory
Config.setModelProvider(getImportModelProvider());
factory = KeycloakApplication.createSessionFactory();
System.setProperty("keycloak.model.provider", getImportModelProvider());
factory = KeycloakApplication.createProviderSessionFactory();
// Full import of previous export into mongo
importModel(factory);
@ -83,19 +90,20 @@ public abstract class ExportImportTestBase {
protected abstract String getImportModelProvider();
protected abstract void exportModel(KeycloakSessionFactory factory);
protected abstract void exportModel(ProviderSessionFactory factory);
protected abstract void importModel(KeycloakSessionFactory factory);
protected abstract void importModel(ProviderSessionFactory factory);
protected void beginTransaction() {
identitySession = factory.createSession();
providerSession = factory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
}
protected void commitTransaction() {
identitySession.getTransaction().commit();
identitySession.close();
providerSession.close();
}
protected ExportImportProvider getExportImportProvider() {

View file

@ -1,8 +1,7 @@
package org.keycloak.exportimport;
import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderSessionFactory;
/**
* Test for full export of data from JPA and import them to Mongo. Using "directory" provider
@ -22,16 +21,16 @@ public class JPAToMongoExportImportTest extends ExportImportTestBase {
}
@Override
protected void exportModel(KeycloakSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT);
Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
protected void exportModel(ProviderSessionFactory factory) {
ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT);
ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
getExportImportProvider().checkExportImport(factory);
}
@Override
protected void importModel(KeycloakSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT);
Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
protected void importModel(ProviderSessionFactory factory) {
ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT);
ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID);
getExportImportProvider().checkExportImport(factory);
}
}

View file

@ -1,11 +1,10 @@
package org.keycloak.exportimport;
import java.io.File;
import org.junit.Assert;
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderSessionFactory;
import java.io.File;
/**
* Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it
@ -27,12 +26,12 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase {
}
@Override
protected void exportModel(KeycloakSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT);
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID);
protected void exportModel(ProviderSessionFactory factory) {
ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT);
ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID);
File zipFile = getZipFile();
Config.setExportImportZipFile(zipFile.getAbsolutePath());
Config.setExportImportZipPassword("password123");
ExportImportConfig.setZipFile(zipFile.getAbsolutePath());
ExportImportConfig.setZipPassword("password123");
if (zipFile.exists()) {
zipFile.delete();
@ -42,12 +41,12 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase {
}
@Override
protected void importModel(KeycloakSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT);
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID);
protected void importModel(ProviderSessionFactory factory) {
ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT);
ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID);
File zipFile = getZipFile();
Config.setExportImportZipFile(zipFile.getAbsolutePath());
Config.setExportImportZipPassword("password-invalid");
ExportImportConfig.setZipFile(zipFile.getAbsolutePath());
ExportImportConfig.setZipPassword("password-invalid");
// Try invalid password
try {
@ -55,7 +54,7 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase {
Assert.fail("Not expected to be here. Exception should be thrown");
} catch (Exception e) {};
Config.setExportImportZipPassword("password123");
ExportImportConfig.setZipPassword("password123");
new ExportImportProviderImpl().checkExportImport(factory);
if (zipFile.exists()) {

View file

@ -1,6 +1,6 @@
package org.keycloak.freemarker;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.util.ProviderLoader;
import java.io.IOException;
@ -20,7 +20,7 @@ public class ThemeLoader {
public static Theme createTheme(String name, Theme.Type type) throws FreeMarkerException {
if (name == null) {
name = Config.getThemeDefault();
name = Config.scope("theme").get("default");
}
List<ThemeProvider> providers = new LinkedList();

View file

@ -1,15 +1,11 @@
package org.keycloak.theme;
import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeLoader;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.Config;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
/**

View file

@ -2,7 +2,7 @@ package org.keycloak.theme;
import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.Config;
import org.keycloak.Config;
import java.io.File;
import java.io.FileFilter;
@ -19,7 +19,7 @@ public class FolderThemeProvider implements ThemeProvider {
private File rootDir;
public FolderThemeProvider() {
String d = Config.getThemeDir();
String d = Config.scope("theme").get("dir");
if (d != null) {
rootDir = new File(d);
}

View file

@ -1,183 +0,0 @@
package org.keycloak.models;
import java.io.File;
import java.util.concurrent.TimeUnit;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class Config {
public static final String ADMIN_REALM_KEY = "keycloak.admin.realm";
public static final String ADMIN_REALM_DEFAULT = "keycloak-admin";
public static final String MODEL_PROVIDER_KEY = "keycloak.model";
public static final String USER_EXPIRATION_SCHEDULE_KEY = "keycloak.scheduled.clearExpiredUserSessions";
public static final String USER_EXPIRATION_SCHEDULE_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(15));
public static final String AUDIT_PROVIDER_KEY = "keycloak.audit";
public static final String AUDIT_PROVIDER_DEFAULT = "jpa";
public static final String AUDIT_EXPIRATION_SCHEDULE_KEY = "keycloak.scheduled.clearExpiredAuditEvents";
public static final String AUDIT_EXPIRATION_SCHEDULE_DEFAULT = String.valueOf(TimeUnit.MINUTES.toMillis(15));
public static final String PICKETLINK_PROVIDER_KEY = "keycloak.picketlink";
public static final String THEME_BASE_KEY = "keycloak.theme.base";
public static final String THEME_BASE_DEFAULT = "base";
public static final String THEME_DEFAULT_KEY = "keycloak.theme.default";
public static final String THEME_DEFAULT_DEFAULT = "keycloak";
public static final String THEME_DIR_KEY = "keycloak.theme.dir";
public static final String THEME_ADMIN_KEY = "keycloak.theme.admin";
public static final String THEME_ADMIN_DEFAULT = "keycloak";
public static final String JBOSS_SERVER_CONFIG_DIR_KEY = "jboss.server.config.dir";
public static final String TIMER_PROVIDER_KEY = "keycloak.timer";
public static final String TIMER_PROVIDER_DEFAULT = "basic";
public static final String EXPORT_IMPORT_ACTION = "keycloak.migration.action";
public static final String EXPORT_IMPORT_PROVIDER = "keycloak.migration.provider";
public static final String EXPORT_IMPORT_PROVIDER_DEFAULT = "zip";
// used for "directory" provider
public static final String EXPORT_IMPORT_DIR = "keycloak.migration.dir";
// used for "zip" provider
public static final String EXPORT_IMPORT_ZIP_FILE = "keycloak.migration.zipFile";
public static final String EXPORT_IMPORT_ZIP_PASSWORD = "keycloak.migration.zipPassword";
public static String getAdminRealm() {
return System.getProperty(ADMIN_REALM_KEY, ADMIN_REALM_DEFAULT);
}
public static void setAdminRealm(String realm) {
System.setProperty(ADMIN_REALM_KEY, realm);
}
public static String getAuditProvider() {
return System.getProperty(AUDIT_PROVIDER_KEY, AUDIT_PROVIDER_DEFAULT);
}
public static void setAuditProvider(String provider) {
System.setProperty(AUDIT_PROVIDER_KEY, provider);
}
public static String getAuditExpirationSchedule() {
return System.getProperty(AUDIT_EXPIRATION_SCHEDULE_KEY, AUDIT_EXPIRATION_SCHEDULE_DEFAULT);
}
public static void setAuditExpirationSchedule(String schedule) {
System.setProperty(AUDIT_EXPIRATION_SCHEDULE_KEY, schedule);
}
public static String getUserExpirationSchedule() {
return System.getProperty(USER_EXPIRATION_SCHEDULE_KEY, USER_EXPIRATION_SCHEDULE_DEFAULT);
}
public static void setUserExpirationSchedule(String schedule) {
System.setProperty(USER_EXPIRATION_SCHEDULE_KEY, schedule);
}
public static String getModelProvider() {
return System.getProperty(MODEL_PROVIDER_KEY);
}
public static void setModelProvider(String provider) {
System.setProperty(MODEL_PROVIDER_KEY, provider);
}
public static String getTimerProvider() {
return System.getProperty(TIMER_PROVIDER_KEY, TIMER_PROVIDER_DEFAULT);
}
public static void setTimerProvider(String provider) {
System.setProperty(TIMER_PROVIDER_KEY, provider);
}
public static String getIdentityManagerProvider() {
return System.getProperty(PICKETLINK_PROVIDER_KEY, "realm");
}
public static void setIdentityManagerProvider(String provider) {
System.setProperty(PICKETLINK_PROVIDER_KEY, provider);
}
public static String getThemeDir() {
String themeDir = System.getProperty(THEME_DIR_KEY);
if (themeDir == null && System.getProperties().containsKey(JBOSS_SERVER_CONFIG_DIR_KEY)) {
themeDir = System.getProperty(JBOSS_SERVER_CONFIG_DIR_KEY) + File.separator + "themes";
}
return themeDir;
}
public static void setThemeDir(String dir) {
System.setProperty(THEME_DIR_KEY, dir);
}
public static String getThemeBase() {
return System.getProperty(THEME_BASE_KEY, THEME_BASE_DEFAULT);
}
public static void setThemeBase(String baseTheme) {
System.setProperty(THEME_BASE_KEY, baseTheme);
}
public static String getThemeDefault() {
return System.getProperty(THEME_DEFAULT_KEY, THEME_DEFAULT_DEFAULT);
}
public static void setThemeDefault(String defaultTheme) {
System.setProperty(THEME_DEFAULT_KEY, defaultTheme);
}
public static String getThemeAdmin() {
return System.getProperty(THEME_ADMIN_KEY, THEME_ADMIN_DEFAULT);
}
public static void setThemeAdmin(String adminTheme) {
System.setProperty(THEME_ADMIN_KEY, adminTheme);
}
// EXPORT + IMPORT
public static String getExportImportAction() {
return System.getProperty(EXPORT_IMPORT_ACTION);
}
public static void setExportImportAction(String exportImportAction) {
System.setProperty(EXPORT_IMPORT_ACTION, exportImportAction);
}
public static String getExportImportProvider() {
return System.getProperty(EXPORT_IMPORT_PROVIDER, EXPORT_IMPORT_PROVIDER_DEFAULT);
}
public static void setExportImportProvider(String exportImportProvider) {
System.setProperty(EXPORT_IMPORT_PROVIDER, exportImportProvider);
}
public static String getExportImportDir() {
return System.getProperty(EXPORT_IMPORT_DIR);
}
public static void setExportImportDir(String exportImportDir) {
System.setProperty(EXPORT_IMPORT_DIR, exportImportDir);
}
public static String getExportImportZipFile() {
return System.getProperty(EXPORT_IMPORT_ZIP_FILE);
}
public static void setExportImportZipFile(String exportImportZipFile) {
System.setProperty(EXPORT_IMPORT_ZIP_FILE, exportImportZipFile);
}
public static String getExportImportZipPassword() {
return System.getProperty(EXPORT_IMPORT_ZIP_PASSWORD);
}
public static void setExportImportZipPassword(String exportImportZipPassword) {
System.setProperty(EXPORT_IMPORT_ZIP_PASSWORD, exportImportZipPassword);
}
}

View file

@ -1,12 +1,14 @@
package org.keycloak.models;
import org.keycloak.provider.Provider;
import java.util.List;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface KeycloakSession {
public interface KeycloakSession extends Provider {
KeycloakTransaction getTransaction();
RealmModel createRealm(String name);

View file

@ -1,10 +1,13 @@
package org.keycloak.models;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderSession;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public interface KeycloakSessionFactory {
KeycloakSession createSession();
public interface KeycloakSessionFactory extends ProviderFactory<KeycloakSession> {
KeycloakSession create(ProviderSession providerSession);
void close();
}

View file

@ -0,0 +1,27 @@
package org.keycloak.models;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class ModelSpi implements Spi {
@Override
public String getName() {
return "model";
}
@Override
public Class<? extends Provider> getProviderClass() {
return KeycloakSession.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return KeycloakSessionFactory.class;
}
}

View file

@ -1,49 +0,0 @@
package org.keycloak.models.utils;
import java.util.ServiceLoader;
import org.keycloak.models.Config;
import org.keycloak.models.ModelProvider;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class ModelProviderUtils {
public static final String DEFAULT_MODEL_PROVIDER = "jpa";
public static Iterable<ModelProvider> getRegisteredProviders() {
return ServiceLoader.load(ModelProvider.class);
}
public static ModelProvider getConfiguredModelProvider(Iterable<ModelProvider> providers) {
String configuredProvider = Config.getModelProvider();
ModelProvider provider = null;
if (configuredProvider != null) {
for (ModelProvider p : providers) {
if (p.getId().equals(configuredProvider)) {
provider = p;
}
}
} else {
for (ModelProvider p : providers) {
if (provider == null) {
provider = p;
}
if (p.getId().equals(DEFAULT_MODEL_PROVIDER)) {
provider = p;
break;
}
}
}
return provider;
}
public static ModelProvider getConfiguredModelProvider() {
return getConfiguredModelProvider(getRegisteredProviders());
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.ModelSpi

View file

@ -30,24 +30,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-jpa</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-jboss-logging</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId>

View file

@ -1,28 +1,53 @@
package org.keycloak.models.jpa;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderSession;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.Properties;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaKeycloakSessionFactory implements KeycloakSessionFactory {
protected EntityManagerFactory factory;
public JpaKeycloakSessionFactory(EntityManagerFactory factory) {
this.factory = factory;
protected EntityManagerFactory emf;
@Override
public void init(Config.Scope config) {
emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", getHibernateProperties());
}
@Override
public KeycloakSession createSession() {
return new JpaKeycloakSession(factory.createEntityManager());
public String getId() {
return "jpa";
}
@Override
public KeycloakSession create(ProviderSession providerSession) {
return new JpaKeycloakSession(emf.createEntityManager());
}
@Override
public void close() {
factory.close();
emf.close();
}
// Allows to override some properties in persistence.xml by system properties
protected Properties getHibernateProperties() {
Properties result = new Properties();
for (Object property : System.getProperties().keySet()) {
if (property.toString().startsWith("hibernate.")) {
String propValue = System.getProperty(property.toString());
result.put(property, propValue);
}
}
return result;
}
}

View file

@ -1,41 +0,0 @@
package org.keycloak.models.jpa;
import java.util.Properties;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class JpaModelProvider implements ModelProvider {
@Override
public String getId() {
return "jpa";
}
@Override
public KeycloakSessionFactory createFactory() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", getHibernateProperties());
return new JpaKeycloakSessionFactory(emf);
}
// Allows to override some properties in persistence.xml by system properties
protected Properties getHibernateProperties() {
Properties result = new Properties();
for (Object property : System.getProperties().keySet()) {
if (property.toString().startsWith("hibernate.")) {
String propValue = System.getProperty(property.toString());
result.put(property, propValue);
}
}
return result;
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.jpa.JpaKeycloakSessionFactory

View file

@ -1 +0,0 @@
org.keycloak.models.jpa.JpaModelProvider

View file

@ -44,18 +44,6 @@
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-jboss-logging</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
@ -103,10 +91,10 @@
</dependencies>
<properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
</properties>
<build>
@ -133,10 +121,10 @@
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
</systemPropertyVariables>
<dependenciesToScan>
<dependency>org.keycloak:keycloak-model-tests</dependency>
@ -164,7 +152,7 @@
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.mongo.port}</port>
<port>${keycloak.model.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
</configuration>

View file

@ -1,27 +0,0 @@
package org.keycloak.models.mongo.keycloak;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
import org.keycloak.models.mongo.keycloak.adapters.MongoKeycloakSessionFactory;
import org.keycloak.models.mongo.keycloak.config.MongoClientProvider;
import org.keycloak.models.mongo.keycloak.config.MongoClientProviderHolder;
import java.lang.Override;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $
*/
public class MongoModelProvider implements ModelProvider {
@Override
public String getId() {
return "mongo";
}
@Override
public KeycloakSessionFactory createFactory() {
MongoClientProvider mongoClientProvider = MongoClientProviderHolder.getInstance();
return new MongoKeycloakSessionFactory(mongoClientProvider);
}
}

View file

@ -1,6 +1,11 @@
package org.keycloak.models.mongo.keycloak.adapters;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.entities.AuthenticationLinkEntity;
@ -10,7 +15,6 @@ import org.keycloak.models.entities.RequiredCredentialEntity;
import org.keycloak.models.entities.SocialLinkEntity;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.keycloak.config.MongoClientProvider;
import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity;
@ -18,6 +22,10 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity;
import org.keycloak.provider.ProviderSession;
import java.net.UnknownHostException;
import java.util.Collections;
/**
* KeycloakSessionFactory implementation based on MongoDB
@ -27,7 +35,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEnti
public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class);
private static final Class<?>[] MANAGED_ENTITY_TYPES = (Class<?>[])new Class<?>[] {
private static final Class<?>[] MANAGED_ENTITY_TYPES = (Class<?>[]) new Class<?>[]{
MongoRealmEntity.class,
MongoUserEntity.class,
MongoRoleEntity.class,
@ -42,21 +50,50 @@ public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
MongoUserSessionEntity.class
};
private final MongoClientProvider mongoClientProvider;
private final MongoStore mongoStore;
private MongoClient client;
public MongoKeycloakSessionFactory(MongoClientProvider provider) {
this.mongoClientProvider = provider;
this.mongoStore = new MongoStoreImpl(provider.getDB(), provider.clearCollectionsOnStartup(), MANAGED_ENTITY_TYPES);
private MongoStore mongoStore;
@Override
public String getId() {
return "mongo";
}
@Override
public KeycloakSession createSession() {
public void init(Config.Scope config) {
try {
String host = config.get("host", ServerAddress.defaultHost());
int port = config.getInt("port", ServerAddress.defaultPort());
String dbName = config.get("db", "keycloak-audit");
boolean clearOnStartup = config.getBoolean("clearOnStartup", false);
String user = config.get("user");
String password = config.get("password");
if (user != null && password != null) {
MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray());
client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential));
} else {
client = new MongoClient(host, port);
}
DB db = client.getDB(dbName);
this.mongoStore = new MongoStoreImpl(db, clearOnStartup, MANAGED_ENTITY_TYPES);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
@Override
public KeycloakSession create(ProviderSession providerSession) {
return new MongoKeycloakSession(mongoStore);
}
@Override
public void close() {
this.mongoClientProvider.close();
this.client.close();
}
}

View file

@ -1,21 +0,0 @@
package org.keycloak.models.mongo.keycloak.config;
import com.mongodb.DB;
import com.mongodb.MongoClient;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public interface MongoClientProvider {
MongoClient getMongoClient();
DB getDB();
/**
* @return true if collections should be cleared on startup
*/
boolean clearCollectionsOnStartup();
void close();
}

View file

@ -1,20 +0,0 @@
package org.keycloak.models.mongo.keycloak.config;
/**
* Provides {@link MongoClientProvider} instance
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoClientProviderHolder {
// Just use static object for now. Default idm is SystemPropsMongoClientProvider
private static MongoClientProvider instance = new SystemPropertiesMongoClientProvider();
public static MongoClientProvider getInstance() {
return instance;
}
public static void setInstance(MongoClientProvider instance) {
MongoClientProviderHolder.instance = instance;
}
}

View file

@ -1,104 +0,0 @@
package org.keycloak.models.mongo.keycloak.config;
import java.net.UnknownHostException;
import com.mongodb.DB;
import com.mongodb.MongoClient;
import org.jboss.logging.Logger;
/**
* Instance of {@link MongoClientProvider} which reads configuration of MongoDB from system properties
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class SystemPropertiesMongoClientProvider implements MongoClientProvider {
protected static final Logger logger = Logger.getLogger(SystemPropertiesMongoClientProvider.class);
public static final String MONGO_HOST = "keycloak.mongo.host";
public static final String MONGO_PORT = "keycloak.mongo.port";
public static final String MONGO_DB_NAME = "keycloak.mongo.db";
public static final String MONGO_CLEAR_ON_STARTUP = "keycloak.mongo.clearOnStartup";
// Property names from Liveoak . Those are used as fallback
private static final String MONGO_HOST_2 = "mongo.host";
private static final String MONGO_PORT_2 = "mongo.port";
private static final String MONGO_DB_NAME_2 = "mongo.db";
private static final String MONGO_CLEAR_ON_STARTUP_2 = "mongo.clearOnStartup";
// Port where MongoDB instance is normally started on linux. This port should be used if we're not starting embedded instance
private static final String MONGO_DEFAULT_PORT = "27017";
private MongoClient mongoClient;
private DB db;
@Override
public synchronized MongoClient getMongoClient() {
if (this.mongoClient == null) {
init();
}
return this.mongoClient;
}
@Override
public synchronized DB getDB() {
if (mongoClient == null) {
init();
}
return this.db;
}
@Override
public boolean clearCollectionsOnStartup() {
return isClearCollectionsOnStartup();
}
@Override
public synchronized void close() {
// Assume that client is dedicated just for Keycloak, so close it
logger.info("Closing MongoDB client");
mongoClient.close();
mongoClient = null;
db = null;
}
protected void init() {
try {
String host = getMongoHost();
int port = getMongoPort();
String dbName = getMongoDbName();
boolean clearOnStartup = isClearCollectionsOnStartup();
logger.info(String.format("Configuring MongoStore with: host=%s, port=%d, dbName=%s, clearOnStartup=%b", host, port, dbName, clearOnStartup));
this.mongoClient = new MongoClient(host, port);
this.db = mongoClient.getDB(dbName);
} catch (UnknownHostException e) {
throw new RuntimeException(e);
}
}
public static String getMongoHost() {
return getSystemPropertyWithFallback(MONGO_HOST, MONGO_HOST_2, "localhost");
}
public static int getMongoPort() {
String portProp = getSystemPropertyWithFallback(MONGO_PORT, MONGO_PORT_2, MONGO_DEFAULT_PORT);
return Integer.parseInt(portProp);
}
public static String getMongoDbName() {
return getSystemPropertyWithFallback(MONGO_DB_NAME, MONGO_DB_NAME_2, "keycloak");
}
public static boolean isClearCollectionsOnStartup() {
String property = getSystemPropertyWithFallback(MONGO_CLEAR_ON_STARTUP, MONGO_CLEAR_ON_STARTUP_2, "false");
return "true".equalsIgnoreCase(property);
}
// Check if property propName1 (like "keycloak.mongo.host" is available and if not, then fallback to property "mongo.host" )
private static String getSystemPropertyWithFallback(String propName1, String propName2, String defaultValue) {
String propValue1 = System.getProperty(propName1);
return propValue1!=null ? propValue1 : System.getProperty(propName2, defaultValue);
}
}

View file

@ -0,0 +1 @@
org.keycloak.models.mongo.keycloak.adapters.MongoKeycloakSessionFactory

View file

@ -1 +0,0 @@
org.keycloak.models.mongo.keycloak.MongoModelProvider

View file

@ -1,31 +0,0 @@
package org.keycloak.models.mongo.test;
import org.keycloak.models.mongo.api.MongoEntity;
import org.keycloak.models.mongo.api.MongoField;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class Address {
private String street;
private int number;
public String getStreet() {
return street;
}
public void setStreet(String street) {
this.street = street;
}
public int getNumber() {
return number;
}
public void setNumber(int number) {
this.number = number;
}
}

View file

@ -1,23 +0,0 @@
package org.keycloak.models.mongo.test;
import java.util.List;
import org.keycloak.models.mongo.api.MongoField;
/**
* Just to test inheritance
*
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class AddressWithFlats extends Address {
private List<String> flatNumbers;
public List<String> getFlatNumbers() {
return flatNumbers;
}
public void setFlatNumbers(List<String> flatNumbers) {
this.flatNumbers = flatNumbers;
}
}

View file

@ -1,140 +0,0 @@
package org.keycloak.models.mongo.test;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import org.keycloak.models.mongo.impl.MongoStoreImpl;
import org.keycloak.models.mongo.impl.context.TransactionMongoStoreInvocationContext;
import org.keycloak.models.mongo.keycloak.config.MongoClientProvider;
import org.keycloak.models.mongo.keycloak.config.MongoClientProviderHolder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
public class MongoStoreTest {
private static final Class<?>[] MANAGED_DATA_TYPES = (Class<?>[])new Class<?>[] {
Person.class,
Address.class,
AddressWithFlats.class
};
private MongoClientProvider mongoClientProvider;
private MongoStore mongoStore;
@Before
public void before() throws Exception {
mongoClientProvider = MongoClientProviderHolder.getInstance();
mongoStore = new MongoStoreImpl(mongoClientProvider.getDB(), true, MANAGED_DATA_TYPES);
}
@After
public void after() throws Exception {
mongoClientProvider.close();
}
@Test
public void mongoModelTest() throws Exception {
MongoStoreInvocationContext context = new TransactionMongoStoreInvocationContext(mongoStore);
// Add some user
Person john = new Person();
john.setFirstName("john");
john.setAge(25);
john.setGender(Person.Gender.MALE);
mongoStore.insertEntity(john, context);
// Add another user
Person mary = new Person();
mary.setFirstName("mary");
mary.setKids(asList("Peter", "Paul", "Wendy"));
AddressWithFlats addr1 = new AddressWithFlats();
addr1.setStreet("Elm");
addr1.setNumber(5);
addr1.setFlatNumbers(asList("flat1", "flat2"));
AddressWithFlats addr2 = new AddressWithFlats();
List<AddressWithFlats> addresses = new ArrayList<AddressWithFlats>();
addresses.add(addr1);
addresses.add(addr2);
mary.setAddresses(addresses);
mary.setMainAddress(addr1);
mary.setGender(Person.Gender.FEMALE);
mary.setGenders(asList(Person.Gender.FEMALE));
mongoStore.insertEntity(mary, context);
Assert.assertEquals(2, mongoStore.loadEntities(Person.class, new QueryBuilder().get(), context).size());
// Commit this context
context.commit();
Assert.assertEquals(2, mongoStore.loadEntities(Person.class, new QueryBuilder().get(), context).size());
DBObject query = new QueryBuilder().and("addresses.flatNumbers").is("flat1").get();
List<Person> persons = mongoStore.loadEntities(Person.class, query, context);
Assert.assertEquals(1, persons.size());
mary = persons.get(0);
Assert.assertEquals(mary.getFirstName(), "mary");
Assert.assertTrue(mary.getKids().contains("Paul"));
Assert.assertEquals(2, mary.getAddresses().size());
Assert.assertEquals(AddressWithFlats.class, mary.getAddresses().get(0).getClass());
// Test push/pull
mongoStore.pushItemToList(mary, "kids", "Pauline", true, context);
mongoStore.pullItemFromList(mary, "kids", "Paul", context);
Address addr3 = new Address();
addr3.setNumber(6);
addr3.setStreet("Broadway");
mongoStore.pushItemToList(mary, "addresses", addr3, true, context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(3, mary.getKids().size());
Assert.assertTrue(mary.getKids().contains("Pauline"));
Assert.assertFalse(mary.getKids().contains("Paul"));
Assert.assertEquals(3, mary.getAddresses().size());
Address mainAddress = mary.getMainAddress();
Assert.assertEquals("Elm", mainAddress.getStreet());
Assert.assertEquals(5, mainAddress.getNumber());
Assert.assertEquals(Person.Gender.FEMALE, mary.getGender());
Assert.assertTrue(mary.getGenders().contains(Person.Gender.FEMALE));
// Some test of Map (attributes)
mary.addAttribute("attr1", "value1");
mary.addAttribute("attr2", "value2");
mary.addAttribute("attr.some3", "value3");
mongoStore.updateEntity(mary, context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(3, mary.getAttributes().size());
mary.removeAttribute("attr2");
mary.removeAttribute("nonExisting");
mongoStore.updateEntity(mary, context);
mary = mongoStore.loadEntity(Person.class, mary.getId(), context);
Assert.assertEquals(2, mary.getAttributes().size());
Assert.assertEquals("value1", mary.getAttributes().get("attr1"));
Assert.assertEquals("value3", mary.getAttributes().get("attr.some3"));
context.commit();
}
private <T> List<T> asList(T... objects) {
List<T> list = new ArrayList<T>();
list.addAll(Arrays.asList(objects));
return list;
}
}

View file

@ -1,114 +0,0 @@
package org.keycloak.models.mongo.test;
import org.keycloak.models.mongo.api.MongoCollection;
import org.keycloak.models.mongo.api.MongoIdentifiableEntity;
import org.keycloak.models.mongo.api.context.MongoStoreInvocationContext;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
*/
@MongoCollection(collectionName = "persons")
public class Person implements MongoIdentifiableEntity {
private String id;
private String firstName;
private int age;
private List<String> kids;
private List<AddressWithFlats> addresses;
private Address mainAddress;
private Gender gender;
private List<Gender> genders;
private Map<String, String> attributes = new HashMap<String, String>();
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Gender getGender() {
return gender;
}
public void setGender(Gender gender) {
this.gender = gender;
}
public List<Gender> getGenders() {
return genders;
}
public void setGenders(List<Gender> genders) {
this.genders = genders;
}
public List<String> getKids() {
return kids;
}
public void setKids(List<String> kids) {
this.kids = kids;
}
public List<AddressWithFlats> getAddresses() {
return addresses;
}
public void setAddresses(List<AddressWithFlats> addresses) {
this.addresses = addresses;
}
public Address getMainAddress() {
return mainAddress;
}
public void setMainAddress(Address mainAddress) {
this.mainAddress = mainAddress;
}
public Map<String, String> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, String> attributes) {
this.attributes = attributes;
}
public void addAttribute(String key, String value) {
attributes.put(key, value);
}
public void removeAttribute(String key) {
attributes.remove(key);
}
@Override
public void afterRemove(MongoStoreInvocationContext invocationContext) {
}
public static enum Gender {
MALE, FEMALE
}
}

View file

@ -11,10 +11,9 @@ import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
@ -28,7 +27,6 @@ import org.keycloak.util.JsonSerialization;
*/
public class AbstractModelTest {
protected static KeycloakSessionFactory factory;
protected static ProviderSessionFactory providerSessionFactory;
protected KeycloakSession identitySession;
@ -37,41 +35,40 @@ public class AbstractModelTest {
@BeforeClass
public static void beforeClass() {
factory = KeycloakApplication.createSessionFactory();
providerSessionFactory = KeycloakApplication.createProviderSessionFactory();
KeycloakSession identitySession = factory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
try {
identitySession.getTransaction().begin();
new ApplianceBootstrap().bootstrap(identitySession, "/auth");
identitySession.getTransaction().commit();
} finally {
identitySession.close();
providerSession.close();
}
}
@AfterClass
public static void afterClass() {
providerSessionFactory.close();
factory.close();
}
@Before
public void before() throws Exception {
identitySession = factory.createSession();
providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
providerSession = providerSessionFactory.createSession();
}
@After
public void after() throws Exception {
identitySession.getTransaction().commit();
providerSession.close();
identitySession.close();
identitySession = factory.createSession();
providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
try {
identitySession.getTransaction().begin();
@ -84,7 +81,7 @@ public class AbstractModelTest {
identitySession.getTransaction().commit();
} finally {
identitySession.close();
providerSession.close();
}
}
@ -103,8 +100,10 @@ public class AbstractModelTest {
}
protected void resetSession() {
identitySession.close();
identitySession = factory.createSession();
providerSession.close();
providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession);
}

View file

@ -164,7 +164,7 @@ public class AuthenticationManagerTest extends AbstractModelTest {
realm.setAccessTokenLifespan(1000);
realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER));
protector = new BruteForceProtector(factory);
protector = new BruteForceProtector(providerSessionFactory);
protector.start();
am = new AuthenticationManager(providerSession, protector);

View file

@ -0,0 +1,25 @@
package org.keycloak.picketlink;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class IdentityManagerSpi implements Spi {
@Override
public String getName() {
return "picketlink-identity-manager";
}
@Override
public Class<? extends Provider> getProviderClass() {
return IdentityManagerProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return IdentityManagerProviderFactory.class;
}
}

View file

@ -0,0 +1 @@
org.keycloak.picketlink.IdentityManagerSpi

View file

@ -1,5 +1,6 @@
package org.keycloak.picketlink.realm;
import org.keycloak.Config;
import org.keycloak.picketlink.IdentityManagerProvider;
import org.keycloak.picketlink.IdentityManagerProviderFactory;
import org.keycloak.provider.ProviderSession;
@ -20,7 +21,7 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi
}
@Override
public void init() {
public void init(Config.Scope config) {
partitionManagerRegistry = new PartitionManagerRegistry();
}
@ -33,8 +34,4 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi
return "realm";
}
@Override
public boolean lazyLoad() {
return false;
}
}

View file

@ -4,6 +4,7 @@ import org.jboss.resteasy.core.Dispatcher;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
@ -22,7 +23,8 @@ public class UpsSecurityApplication extends KeycloakApplication {
@Override
protected void setupDefaultRealm(String contextPath) {
super.setupDefaultRealm(contextPath);
KeycloakSession session = factory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
// disable master realm by deleting the admin user.
@ -33,7 +35,7 @@ public class UpsSecurityApplication extends KeycloakApplication {
if (admin != null) master.removeUser(admin.getLoginName());
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}

View file

@ -225,6 +225,39 @@
<scope>provided</scope>
</dependency>
<!-- Mongo dependencies -->
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-mongo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-mongo</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.mongodb</groupId>
<artifactId>mongo-java-driver</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-common</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-api</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-impl</artifactId>
</dependency>
<dependency>
<groupId>org.picketlink</groupId>
<artifactId>picketlink-idm-simple-schema</artifactId>
</dependency>
<!-- export/import -->
<dependency>
<groupId>org.keycloak</groupId>

View file

@ -0,0 +1,26 @@
{
"admin": {
"realm": "keycloak-admin"
},
"audit": {
"provider": "jpa"
},
"model": {
"provider": "jpa"
},
"timer": {
"provider": "basic"
},
"theme": {
"default": "keycloak",
"dir": "${jboss.server.config.dir}/themes"
},
"scheduled": {
"interval": 900
}
}

View file

@ -24,6 +24,7 @@
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="jboss.as.jpa.managed" value="false" />
</properties>
</persistence-unit>
@ -35,6 +36,7 @@
<properties>
<property name="hibernate.hbm2ddl.auto" value="update" />
<property name="jboss.as.jpa.managed" value="false" />
</properties>
</persistence-unit>

View file

@ -102,6 +102,7 @@
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>

View file

@ -6,6 +6,8 @@ import org.keycloak.provider.ProviderSession;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
@ -13,6 +15,7 @@ import java.util.Set;
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class DefaultProviderSession implements ProviderSession {
private DefaultProviderSessionFactory factory;
private Map<Integer, Provider> providers = new HashMap<Integer, Provider>();
@ -21,8 +24,16 @@ public class DefaultProviderSession implements ProviderSession {
}
public <T extends Provider> T getProvider(Class<T> clazz) {
String id = factory.getDefaultProvider(clazz);
return id != null ? getProvider(clazz, id) : null;
Integer hash = clazz.hashCode();
T provider = (T) providers.get(hash);
if (provider == null) {
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz);
if (providerFactory != null) {
provider = providerFactory.create(this);
providers.put(hash, provider);
}
}
return provider;
}
public <T extends Provider> T getProvider(Class<T> clazz, String id) {
@ -39,15 +50,14 @@ public class DefaultProviderSession implements ProviderSession {
}
public <T extends Provider> Set<String> listProviderIds(Class<T> clazz) {
return factory.providerIds(clazz);
return factory.getAllProviderIds(clazz);
}
@Override
public <T extends Provider> Set<T> getAllProviders(Class<T> clazz) {
Set<String> providerIds = listProviderIds(clazz);
Set<T> providers = new HashSet<T>();
for (String providerId : providerIds) {
providers.add(getProvider(clazz, providerId));
for (String id : listProviderIds(clazz)) {
providers.add(getProvider(clazz, id));
}
return providers;
}

View file

@ -1,71 +1,99 @@
package org.keycloak.services;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.provider.Spi;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set;
public class DefaultProviderSessionFactory implements ProviderSessionFactory {
private Map<Class<? extends Provider>, ProviderFactoryLoader> loaders = new HashMap<Class<? extends Provider>, ProviderFactoryLoader>();
private Map<Class<? extends Provider>, String> defaultFactories = new HashMap<Class<? extends Provider>, String>();
private static final Logger log = Logger.getLogger(DefaultProviderSessionFactory.class);
private Map<Class<? extends Provider>, String> provider = new HashMap<Class<? extends Provider>, String>();
private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
public void init() {
for (Spi spi : ServiceLoader.load(Spi.class)) {
Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
factoriesMap.put(spi.getProviderClass(), factories);
String provider = Config.getProvider(spi.getName());
if (provider != null) {
this.provider.put(spi.getProviderClass(), provider);
ProviderFactory factory = loadProviderFactory(spi, provider);
Config.Scope scope = Config.scope(spi.getName(), provider);
factory.init(scope);
log.debug("Initialized " + factory.getClass().getName() + " (config = " + scope + ")");
factories.put(factory.getId(), factory);
log.info("Loaded SPI " + spi.getName() + " (provider = " + provider + ")");
} else {
for (ProviderFactory factory : ServiceLoader.load(spi.getProviderFactoryClass())) {
Config.Scope scope = Config.scope(spi.getName(), factory.getId());
factory.init(scope);
log.debug("Initialized " + factory.getClass().getName() + " (config = " + scope + ")");
factories.put(factory.getId(), factory);
}
if (factories.size() == 1) {
provider = factories.values().iterator().next().getId();
this.provider.put(spi.getProviderClass(), provider);
log.info("Loaded SPI " + spi.getName() + " (provider = " + provider + ")");
} else {
log.info("Loaded SPI " + spi.getName() + " (providers = " + factories.keySet() + ")");
}
}
}
}
private ProviderFactory loadProviderFactory(Spi spi, String id) {
for (ProviderFactory factory : ServiceLoader.load(spi.getProviderFactoryClass())) {
if (factory.getId().equals(id)){
return factory;
}
}
throw new RuntimeException("Failed to find provider " + id + " for " + spi.getName());
}
public ProviderSession createSession() {
return new DefaultProviderSession(this);
}
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
return getProviderFactory(clazz, provider.get(clazz));
}
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
return factoriesMap.get(clazz).get(id);
}
<T extends Provider> Set<String> getAllProviderIds(Class<T> clazz) {
Set<String> ids = new HashSet<String>();
for (ProviderFactory f : factoriesMap.get(clazz).values()) {
ids.add(f.getId());
}
return ids;
}
public void close() {
for (ProviderFactoryLoader loader : loaders.values()) {
loader.close();
for (Map<String, ProviderFactory> factories : factoriesMap.values()) {
for (ProviderFactory factory : factories.values()) {
factory.close();
}
}
}
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
String id = defaultFactories.get(clazz);
if (id == null) {
return null;
}
return getProviderFactory(clazz, id);
}
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
ProviderFactoryLoader loader = getLoader(clazz);
return loader != null ? loader.find(id) : null;
}
public Set<String> providerIds(Class<? extends Provider> clazz) {
ProviderFactoryLoader loader = getLoader(clazz);
return loader != null ? loader.providerIds() : null;
}
public String getDefaultProvider(Class<? extends Provider> clazz) {
return defaultFactories.get(clazz);
}
public void registerLoader(Class<? extends Provider> clazz, ProviderFactoryLoader loader) {
loaders.put(clazz, loader);
}
public void registerLoader(Class<? extends Provider> clazz, ProviderFactoryLoader loader, String defaultProvider) {
loaders.put(clazz, loader);
defaultFactories.put(clazz, defaultProvider);
}
public void init() {
for (ProviderFactoryLoader l : loaders.values()) {
l.init();
}
}
private <T extends Provider> ProviderFactoryLoader getLoader(Class<T> clazz) {
return loaders.get(clazz);
}
}

View file

@ -32,9 +32,7 @@ public class KeycloakSessionServletFilter implements Filter {
ResteasyProviderFactory.pushContext(ProviderSession.class, providerSession);
KeycloakSessionFactory factory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName());
if (factory == null) throw new ServletException("Factory was null");
KeycloakSession session = factory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
ResteasyProviderFactory.pushContext(KeycloakSession.class, session);
KeycloakTransaction tx = session.getTransaction();
ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx);
@ -56,7 +54,6 @@ public class KeycloakSessionServletFilter implements Filter {
if (tx.isActive()) tx.rollback();
throw ex;
} finally {
session.close();
providerSession.close();
ResteasyProviderFactory.clearContextData();
}

View file

@ -6,14 +6,15 @@ import org.jboss.logging.Logger;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.CredentialRepresentation;
import java.util.Collections;
@ -26,26 +27,25 @@ public class ApplianceBootstrap {
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
public void bootstrap(KeycloakSessionFactory factory, String contextPath) {
KeycloakSession session = factory.createSession();
public void bootstrap(ProviderSessionFactory factory, String contextPath) {
ProviderSession providerSession = factory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
bootstrap(session, contextPath);
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}
public void bootstrap(KeycloakSession session, String contextPath) {
if (session.getRealm(Config.getAdminRealm()) != null) {
String adminRealmName = Config.getAdminRealm();
if (session.getRealm(adminRealmName) != null) {
return;
}
String adminRealmName = Config.getAdminRealm();
logger.info("Initializing " + adminRealmName + " realm");
RealmManager manager = new RealmManager(session);

View file

@ -6,6 +6,8 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.ClientConnection;
import java.util.ArrayList;
@ -25,7 +27,7 @@ public class BruteForceProtector implements Runnable {
protected volatile boolean run = true;
protected int maxDeltaTimeSeconds = 60 * 60 * 12; // 12 hours
protected KeycloakSessionFactory factory;
protected ProviderSessionFactory factory;
protected CountDownLatch shutdownLatch = new CountDownLatch(1);
protected volatile long failures;
@ -73,7 +75,7 @@ public class BruteForceProtector implements Runnable {
}
}
public BruteForceProtector(KeycloakSessionFactory factory) {
public BruteForceProtector(ProviderSessionFactory factory) {
this.factory = factory;
}
@ -160,7 +162,8 @@ public class BruteForceProtector implements Runnable {
events.add(take);
queue.drainTo(events, TRANSACTION_SIZE);
Collections.sort(events); // we sort to avoid deadlock due to ordered updates. Maybe I'm overthinking this.
KeycloakSession session = factory.createSession();
ProviderSession providerSession = factory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
for (LoginEvent event : events) {
@ -179,7 +182,7 @@ public class BruteForceProtector implements Runnable {
}
}
events.clear();
session.close();
providerSession.close();
}
} catch (Exception e) {
logger.error("Failed processing event", e);

View file

@ -7,7 +7,7 @@ import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel;

View file

@ -1,39 +1,30 @@
package org.keycloak.services.resources;
import org.jboss.resteasy.core.Dispatcher;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.jboss.logging.Logger;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.SkeletonKeyContextResolver;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.exportimport.ExportImportProvider;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.DefaultProviderSessionFactory;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.models.utils.ModelProviderUtils;
import org.keycloak.services.scheduled.ClearExpiredAuditEvents;
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
import org.keycloak.services.scheduled.ScheduledTaskRunner;
import org.keycloak.services.util.JsonConfigProvider;
import org.keycloak.timer.TimerProvider;
import org.keycloak.timer.TimerProviderFactory;
import org.keycloak.util.JsonSerialization;
import org.keycloak.util.ProviderLoader;
@ -41,12 +32,13 @@ import javax.servlet.ServletContext;
import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.util.Date;
import java.net.URL;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
@ -63,22 +55,21 @@ public class KeycloakApplication extends Application {
protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>();
protected KeycloakSessionFactory factory;
protected ProviderSessionFactory providerSessionFactory;
protected String contextPath;
public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
loadConfig();
this.providerSessionFactory = createProviderSessionFactory();
dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
this.contextPath = context.getContextPath();
this.factory = createSessionFactory();
BruteForceProtector protector = new BruteForceProtector(factory);
BruteForceProtector protector = new BruteForceProtector(providerSessionFactory);
dispatcher.getDefaultContextObjects().put(BruteForceProtector.class, protector);
ResteasyProviderFactory.pushContext(BruteForceProtector.class, protector); // for injection
protector.start();
context.setAttribute(BruteForceProtector.class.getName(), protector);
this.providerSessionFactory = createProviderSessionFactory();
context.setAttribute(KeycloakSessionFactory.class.getName(), factory);
context.setAttribute(ProviderSessionFactory.class.getName(), this.providerSessionFactory);
TokenManager tokenManager = new TokenManager();
@ -95,7 +86,7 @@ public class KeycloakApplication extends Application {
setupDefaultRealm(context.getContextPath());
setupScheduledTasks(providerSessionFactory, factory);
setupScheduledTasks(providerSessionFactory);
importRealms(context);
checkExportImportProvider();
@ -115,55 +106,38 @@ public class KeycloakApplication extends Application {
return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
}
protected void setupDefaultRealm(String contextPath) {
new ApplianceBootstrap().bootstrap(factory, contextPath);
protected void loadConfig() {
try {
URL config = Thread.currentThread().getContextClassLoader().getResource("META-INF/keycloak-server.json");
if (config != null) {
JsonNode node = new ObjectMapper().readTree(config);
Config.init(new JsonConfigProvider(node));
log.info("Loaded config from " + config);
return;
}
} catch (IOException e) {
throw new RuntimeException("Failed to load config", e);
}
}
public static KeycloakSessionFactory createSessionFactory() {
ModelProvider provider = ModelProviderUtils.getConfiguredModelProvider();
if (provider != null) {
log.debug("Model provider: " + provider.getId());
return provider.createFactory();
}
throw new RuntimeException("Model provider not found");
protected void setupDefaultRealm(String contextPath) {
new ApplianceBootstrap().bootstrap(providerSessionFactory, contextPath);
}
public static DefaultProviderSessionFactory createProviderSessionFactory() {
DefaultProviderSessionFactory factory = new DefaultProviderSessionFactory();
factory.registerLoader(AuditProvider.class, ProviderFactoryLoader.create(AuditProviderFactory.class), Config.getAuditProvider());
factory.registerLoader(AuditListener.class, ProviderFactoryLoader.create(AuditListenerFactory.class));
factory.registerLoader(TimerProvider.class, ProviderFactoryLoader.create(TimerProviderFactory.class), Config.getTimerProvider());
try {
Class identityManagerProvider = Class.forName("org.keycloak.picketlink.IdentityManagerProvider");
Class identityManagerProviderFactory = Class.forName("org.keycloak.picketlink.IdentityManagerProviderFactory");
factory.registerLoader(identityManagerProvider, ProviderFactoryLoader.create(identityManagerProviderFactory), Config.getIdentityManagerProvider());
} catch (ClassNotFoundException e) {
log.warn("Picketlink libraries not installed for IdentityManagerProviderFactory");
}
factory.registerLoader(AuthenticationProvider.class, ProviderFactoryLoader.create(AuthenticationProviderFactory.class));
factory.init();
return factory;
}
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory, final KeycloakSessionFactory keycloakSessionFactory) {
ProviderFactory<TimerProvider> timerFactory = providerSessionFactory.getProviderFactory(TimerProvider.class);
if (timerFactory == null) {
log.error("Can't setup schedule tasks, no timer provider found");
return;
}
TimerProvider timer = timerFactory.create(null);
timer.schedule(new ScheduledTaskRunner(keycloakSessionFactory, providerSessionFactory, new ClearExpiredAuditEvents()), Config.getAuditExpirationSchedule());
timer.schedule(new ScheduledTaskRunner(keycloakSessionFactory, providerSessionFactory, new ClearExpiredUserSessions()), Config.getUserExpirationSchedule());
}
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory) {
long interval = Config.scope("scheduled").getLong("interval") * 1000;
public KeycloakSessionFactory getFactory() {
return factory;
TimerProvider timer = providerSessionFactory.createSession().getProvider(TimerProvider.class);
timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredAuditEvents()), interval);
timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredUserSessions()), interval);
}
public ProviderSessionFactory getProviderSessionFactory() {
@ -215,7 +189,8 @@ public class KeycloakApplication extends Application {
}
public void importRealm(RealmRepresentation rep, String from) {
KeycloakSession session = factory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try {
session.getTransaction().begin();
RealmManager manager = new RealmManager(session);
@ -238,7 +213,7 @@ public class KeycloakApplication extends Application {
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}
@ -255,7 +230,7 @@ public class KeycloakApplication extends Application {
if (providers.hasNext()) {
ExportImportProvider exportImport = providers.next();
exportImport.checkExportImport(factory);
exportImport.checkExportImport(providerSessionFactory);
} else {
log.warn("No ExportImportProvider found!");
}

View file

@ -10,14 +10,12 @@ import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeLoader;
import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Config;
import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.ApplicationManager;
import org.keycloak.services.managers.RealmManager;
@ -280,12 +278,7 @@ public class AdminConsole {
try {
//logger.info("getting resource: " + path + " uri: " + uriInfo.getRequestUri().toString());
String themeName = realm.getAdminTheme();
if (themeName == null || themeName.trim().equals("")) {
themeName = Config.getThemeAdmin();
}
Theme theme = ThemeLoader.createTheme(themeName, Theme.Type.ADMIN);
Theme theme = ThemeLoader.createTheme(realm.getAdminTheme(), Theme.Type.ADMIN);
InputStream resource = theme.getResourceAsStream(path);
if (resource != null) {
String contentType = mimeTypes.getContentType(path);

View file

@ -13,20 +13,18 @@ public class ScheduledTaskRunner implements Runnable {
private static final Logger logger = Logger.getLogger(ScheduledTaskRunner.class);
private final KeycloakSessionFactory keycloakSessionFactory;
private final ProviderSessionFactory providerSessionFactory;
private final ScheduledTask task;
public ScheduledTaskRunner(KeycloakSessionFactory keycloakSessionFactory, ProviderSessionFactory providerSessionFactory, ScheduledTask task) {
this.keycloakSessionFactory = keycloakSessionFactory;
public ScheduledTaskRunner(ProviderSessionFactory providerSessionFactory, ScheduledTask task) {
this.providerSessionFactory = providerSessionFactory;
this.task = task;
}
@Override
public void run() {
KeycloakSession keycloakSession = keycloakSessionFactory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession keycloakSession = providerSession.getProvider(KeycloakSession.class);
try {
keycloakSession.getTransaction().begin();
task.run(keycloakSession, providerSession);
@ -38,11 +36,6 @@ public class ScheduledTaskRunner implements Runnable {
keycloakSession.getTransaction().rollback();
} finally {
try {
keycloakSession.close();
} catch (Throwable t) {
logger.error("Failed to close KeycloakSession", t);
}
try {
providerSession.close();
} catch (Throwable t) {

View file

@ -0,0 +1,129 @@
package org.keycloak.services.util;
import org.codehaus.jackson.JsonNode;
import org.keycloak.Config;
import org.keycloak.util.StringPropertyReplacer;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class JsonConfigProvider implements Config.ConfigProvider {
private JsonNode config;
public JsonConfigProvider(JsonNode config) {
this.config = config;
}
@Override
public String getProvider(String spi) {
JsonNode n = getNode(spi, "provider");
return n != null ? StringPropertyReplacer.replaceProperties(n.getTextValue()) : null;
}
@Override
public Config.Scope scope(String... path) {
return new JsonScope(getNode(path));
}
private JsonNode getNode(String... path) {
JsonNode n = config;
for (String p : path) {
n = n.get(p);
if (n == null) {
return null;
}
}
return n;
}
public class JsonScope implements Config.Scope {
private JsonNode config;
public JsonScope(JsonNode config) {
this.config = config;
}
@Override
public String get(String key) {
return get(key, null);
}
@Override
public String get(String key, String defaultValue) {
if (config == null) {
return defaultValue;
}
JsonNode n = config.get(key);
if (n == null) {
return defaultValue;
}
return StringPropertyReplacer.replaceProperties(n.getTextValue());
}
@Override
public Integer getInt(String key) {
return getInt(key, null);
}
@Override
public Integer getInt(String key, Integer defaultValue) {
if (config == null) {
return defaultValue;
}
JsonNode n = config.get(key);
if (n == null) {
return defaultValue;
}
if (n.isTextual()) {
return Integer.parseInt(StringPropertyReplacer.replaceProperties(n.getTextValue()));
} else {
return n.getIntValue();
}
}
@Override
public Long getLong(String key) {
return getLong(key, null);
}
@Override
public Long getLong(String key, Long defaultValue) {
if (config == null) {
return defaultValue;
}
JsonNode n = config.get(key);
if (n == null) {
return defaultValue;
}
if (n.isTextual()) {
return Long.parseLong(StringPropertyReplacer.replaceProperties(n.getTextValue()));
} else {
return n.getLongValue();
}
}
@Override
public Boolean getBoolean(String key) {
return getBoolean(key, null);
}
@Override
public Boolean getBoolean(String key, Boolean defaultValue) {
if (config == null) {
return defaultValue;
}
JsonNode n = config.get(key);
if (n == null) {
return defaultValue;
}
if (n.isTextual()) {
return Boolean.parseBoolean(StringPropertyReplacer.replaceProperties(n.getTextValue()));
} else {
return n.getBooleanValue();
}
}
}
}

View file

@ -2,8 +2,6 @@ package org.keycloak.social;
import org.keycloak.util.ProviderLoader;
import java.util.ServiceLoader;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/

View file

@ -90,10 +90,9 @@ public class SimpleHttpTest {
@Test
public void testGetCustomHeader() throws IOException {
JsonNode o = SimpleHttp.doGet("http://localhost:8081/tojson").header("Accept", "application/json").header("Authorization", "bearer dsfsadfsdf").asJson();
JsonNode o = SimpleHttp.doGet("http://localhost:8081/tojson").header("Authorization", "bearer dsfsadfsdf").asJson();
JsonNode h = o.get("headers");
assertEquals("application/json", h.get("Accept"));
assertEquals("bearer dsfsadfsdf", h.get("Authorization").getTextValue());
}

View file

@ -11,7 +11,7 @@ To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrows
Mongo
-----
The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model=mongo` when executing it.
The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model.provider=mongo` when executing it.
Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished.
So you don't need to have Mongo installed on your laptop to run mongo execution tests.
@ -52,11 +52,11 @@ For example to use the example themes run the server with:
To start a Keycloak server with identity model data persisted in Mongo database instead of default JPA/H2 you can run:
mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo
mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo
By default it's using database `keycloak` on localhost/27017 and it uses already existing data from this DB (no cleanup of existing data during bootstrap). Assumption is that you already have DB running on localhost/27017 . Use system properties to configure things differently:
mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost -Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak -Dkeycloak.mongo.clearCollectionsOnStartup=false
mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo -Dkeycloak.model.mongo.host=localhost -Dkeycloak.model.mongo.port=27017 -Dkeycloak.model.mongo.db=keycloak -Dkeycloak.model.mongo.clearOnStartup=false
TOTP codes
----------

View file

@ -430,10 +430,10 @@
</activation>
<properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
</properties>
<build>
@ -452,16 +452,17 @@
</goals>
<configuration>
<systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db>
<keycloak.model.provider>mongo</keycloak.model.provider>
<keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
<keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
<keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
<keycloak.audit>mongo</keycloak.audit>
<keycloak.audit.mongo.host>${keycloak.mongo.host}</keycloak.audit.mongo.host>
<keycloak.audit.mongo.port>${keycloak.mongo.port}</keycloak.audit.mongo.port>
<keycloak.audit.mongo.db>${keycloak.mongo.db}</keycloak.audit.mongo.db>
<keycloak.audit.provider>mongo</keycloak.audit.provider>
<keycloak.audit.mongo.host>${keycloak.model.mongo.host}</keycloak.audit.mongo.host>
<keycloak.audit.mongo.port>${keycloak.model.mongo.port}</keycloak.audit.mongo.port>
<keycloak.audit.mongo.db>${keycloak.model.mongo.db}</keycloak.audit.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup>
<keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
</systemPropertyVariables>
</configuration>
</execution>
@ -486,7 +487,7 @@
<goal>start</goal>
</goals>
<configuration>
<port>${keycloak.mongo.port}</port>
<port>${keycloak.model.mongo.port}</port>
<logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile>
</configuration>

View file

@ -30,18 +30,16 @@ import io.undertow.servlet.api.FilterInfo;
import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.filters.ClientConnectionFilter;
import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.theme.DefaultKeycloakThemeProvider;
import org.keycloak.util.JsonSerialization;
import javax.servlet.DispatcherType;
@ -129,9 +127,8 @@ public class KeycloakServer {
throw new RuntimeException("Invalid resources directory");
}
if (Config.getThemeDir() == null) {
System.setProperty(DefaultKeycloakThemeProvider.class.getName() + ".disabled", "");
Config.setThemeDir(file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
if (!System.getProperties().containsKey("keycloak.theme.dir")) {
System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
}
config.setResourcesHome(dir.getAbsolutePath());
@ -162,8 +159,6 @@ public class KeycloakServer {
private KeycloakServerConfig config;
private KeycloakSessionFactory factory;
private ProviderSessionFactory providerSessionFactory;
private UndertowJaxrsServer server;
@ -176,10 +171,6 @@ public class KeycloakServer {
this.config = config;
}
public KeycloakSessionFactory getKeycloakSessionFactory() {
return factory;
}
public ProviderSessionFactory getProviderSessionFactory() {
return providerSessionFactory;
}
@ -194,7 +185,8 @@ public class KeycloakServer {
}
public void importRealm(RealmRepresentation rep) {
KeycloakSession session = factory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
@ -217,12 +209,13 @@ public class KeycloakServer {
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}
protected void setupDevConfig() {
KeycloakSession session = factory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
@ -234,7 +227,7 @@ public class KeycloakServer {
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}
@ -266,7 +259,6 @@ public class KeycloakServer {
server.deploy(di);
factory = ((KeycloakApplication) deployment.getApplication()).getFactory();
providerSessionFactory = ((KeycloakApplication) deployment.getApplication()).getProviderSessionFactory();
setupDevConfig();
@ -289,7 +281,6 @@ public class KeycloakServer {
public void stop() {
providerSessionFactory.close();
factory.close();
server.stop();
info("Stopped Keycloak");

View file

@ -0,0 +1,26 @@
{
"admin": {
"realm": "keycloak-admin"
},
"audit": {
"provider": "${keycloak.audit.provider:jpa}"
},
"model": {
"provider": "${keycloak.model.provider:jpa}"
},
"timer": {
"provider": "basic"
},
"theme": {
"default": "keycloak",
"dir": "${keycloak.theme.dir}"
},
"scheduled": {
"interval": 900
}
}

View file

@ -4,10 +4,10 @@ import org.hamcrest.CoreMatchers;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.jboss.logging.Logger;
import org.junit.Assert;
import org.junit.rules.TestRule;
import org.junit.runners.model.Statement;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.audit.Details;
@ -58,11 +58,6 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
return "assert-events";
}
@Override
public boolean lazyLoad() {
return false;
}
@Override
public Statement apply(final Statement base, org.junit.runner.Description description) {
return new Statement() {
@ -194,7 +189,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
}
@Override
public void init() {
public void init(Config.Scope config) {
}
@Override

View file

@ -22,18 +22,14 @@
package org.keycloak.testsuite.account;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.keycloak.audit.Details;
import org.keycloak.audit.Event;
import org.keycloak.audit.jpa.JpaAuditProviderFactory;
import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Config;
import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel;

View file

@ -32,6 +32,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.idm.RealmRepresentation;
@ -171,11 +172,11 @@ public class AdapterTest {
System.out.println(pageSource);
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("demo");
ProviderSession providerSession = keycloakRule.startSession();
RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1);
keycloakRule.stopSession(session, true);
keycloakRule.stopSession(providerSession, true);
Thread.sleep(2000);
@ -184,10 +185,10 @@ public class AdapterTest {
driver.navigate().to("http://localhost:8081/product-portal");
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession();
realm = session.getRealmByName("demo");
providerSession = keycloakRule.startSession();
realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
realm.setSsoSessionIdleTimeout(originalIdle);
keycloakRule.stopSession(session, true);
keycloakRule.stopSession(providerSession, true);
}
@Test
public void testLoginSSOMax() throws Exception {
@ -202,11 +203,11 @@ public class AdapterTest {
System.out.println(pageSource);
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("demo");
ProviderSession providerSession = keycloakRule.startSession();
RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
int original = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1);
keycloakRule.stopSession(session, true);
keycloakRule.stopSession(providerSession, true);
Thread.sleep(2000);
@ -215,9 +216,9 @@ public class AdapterTest {
driver.navigate().to("http://localhost:8081/product-portal");
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession();
realm = session.getRealmByName("demo");
providerSession = keycloakRule.startSession();
realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
realm.setSsoSessionMaxLifespan(original);
keycloakRule.stopSession(session, true);
keycloakRule.stopSession(providerSession, true);
}
}

View file

@ -29,14 +29,12 @@ import org.keycloak.OAuth2Constants;
import org.keycloak.audit.Details;
import org.keycloak.audit.Errors;
import org.keycloak.audit.Event;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
@ -47,7 +45,11 @@ import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.Time;
import org.openqa.selenium.WebDriver;
import static org.hamcrest.Matchers.*;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.greaterThanOrEqualTo;
import static org.hamcrest.Matchers.lessThan;
import static org.hamcrest.Matchers.lessThanOrEqualTo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
@ -181,8 +183,8 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("test");
ProviderSession session = keycloakRule.startSession();
RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
UserSessionModel userSession = realm.getUserSession(sessionId);
int last = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false);
@ -197,7 +199,7 @@ public class RefreshTokenTest {
Assert.assertEquals(200, tokenResponse.getStatusCode());
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
userSession = realm.getUserSession(sessionId);
int next = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false);
@ -208,7 +210,7 @@ public class RefreshTokenTest {
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int lastAccessTokenLifespan = realm.getAccessTokenLifespan();
realm.setAccessTokenLifespan(100000);
keycloakRule.stopSession(session, true);
@ -217,7 +219,7 @@ public class RefreshTokenTest {
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
userSession = realm.getUserSession(sessionId);
next = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false);
@ -226,7 +228,7 @@ public class RefreshTokenTest {
Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6)));
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1);
keycloakRule.stopSession(session, true);
@ -243,7 +245,7 @@ public class RefreshTokenTest {
events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
realm.setSsoSessionIdleTimeout(originalIdle);
realm.setAccessTokenLifespan(lastAccessTokenLifespan);
keycloakRule.stopSession(session, true);
@ -266,8 +268,8 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("test");
ProviderSession session = keycloakRule.startSession();
RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int maxLifespan = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1);
keycloakRule.stopSession(session, true);
@ -281,7 +283,7 @@ public class RefreshTokenTest {
assertNull(tokenResponse.getRefreshToken());
session = keycloakRule.startSession();
realm = session.getRealmByName("test");
realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
realm.setSsoSessionMaxLifespan(maxLifespan);
keycloakRule.stopSession(session, true);

View file

@ -6,10 +6,11 @@ import io.undertow.servlet.api.SecurityConstraint;
import io.undertow.servlet.api.ServletInfo;
import io.undertow.servlet.api.WebResourceCollection;
import org.junit.rules.ExternalResource;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.ModelToRepresentation;
@ -33,31 +34,33 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
server = new KeycloakServer();
server.start();
setupKeycloak();
}
public UserRepresentation getUser(String realm, String name) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try {
UserModel user = session.getRealmByName(realm).getUser(name);
return user != null ? ModelToRepresentation.toRepresentation(user) : null;
} finally {
session.close();
providerSession.close();
}
}
public UserRepresentation getUserById(String realm, String id) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try {
return ModelToRepresentation.toRepresentation(session.getRealmByName(realm).getUserById(id));
} finally {
session.close();
providerSession.close();
}
}
protected void setupKeycloak() {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
@ -69,9 +72,8 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
session.getTransaction().commit();
} finally {
session.close();
providerSession.close();
}
}
protected void configure(RealmManager manager, RealmModel adminRealm) {
@ -134,15 +136,16 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
return JsonSerialization.readValue(bytes, RealmRepresentation.class);
}
public KeycloakSession startSession() {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
public ProviderSession startSession() {
ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
return session;
return providerSession;
}
public void stopSession(KeycloakSession session, boolean commit) {
public void stopSession(ProviderSession session, boolean commit) {
if (commit) {
session.getTransaction().commit();
session.getProvider(KeycloakSession.class).getTransaction().commit();
}
session.close();
}

View file

@ -21,7 +21,7 @@
*/
package org.keycloak.testsuite.rule;
import org.keycloak.models.Config;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
@ -61,8 +61,8 @@ public class KeycloakRule extends AbstractKeycloakRule {
}
public void configure(KeycloakSetup configurer) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin();
try {
@ -77,17 +77,16 @@ public class KeycloakRule extends AbstractKeycloakRule {
session.getTransaction().commit();
} finally {
providerSession.close();
session.close();
}
}
public void removeUserSession(String sessionId) {
KeycloakSession keycloakSession = startSession();
RealmModel realm = keycloakSession.getRealm("test");
ProviderSession providerSession = startSession();
RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealm("test");
UserSessionModel session = realm.getUserSession(sessionId);
assertNotNull(session);
realm.removeUserSession(session);
stopSession(keycloakSession, true);
stopSession(providerSession, true);
}
public abstract static class KeycloakSetup {

View file

@ -6,6 +6,8 @@ import org.apache.jmeter.samplers.SampleResult;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.resources.KeycloakApplication;
import java.util.concurrent.Callable;
@ -18,17 +20,17 @@ import java.util.concurrent.atomic.AtomicInteger;
public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
private static FutureTask<KeycloakSessionFactory> factoryProvider = new FutureTask<KeycloakSessionFactory>(new Callable() {
private static FutureTask<ProviderSessionFactory> factoryProvider = new FutureTask<ProviderSessionFactory>(new Callable() {
@Override
public KeycloakSessionFactory call() throws Exception {
return KeycloakApplication.createSessionFactory();
public ProviderSessionFactory call() throws Exception {
return KeycloakApplication.createProviderSessionFactory();
}
});
private static AtomicInteger counter = new AtomicInteger();
private KeycloakSessionFactory factory;
private ProviderSessionFactory factory;
// private KeycloakSession identitySession;
private Worker worker;
private boolean setupSuccess = false;
@ -42,7 +44,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
worker = getWorker();
factory = getFactory();
KeycloakSession identitySession = factory.createSession();
ProviderSession providerSession = factory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = identitySession.getTransaction();
transaction.begin();
@ -56,11 +59,11 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
} else {
transaction.rollback();
}
identitySession.close();
providerSession.close();
}
}
private static KeycloakSessionFactory getFactory() {
private static ProviderSessionFactory getFactory() {
factoryProvider.run();
try {
return factoryProvider.get();
@ -98,7 +101,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
return result;
}
KeycloakSession identitySession = factory.createSession();
ProviderSession providerSession = factory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = identitySession.getTransaction();
try {
transaction.begin();
@ -114,7 +118,7 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
} finally {
result.sampleEnd();
result.setSuccessful(true);
identitySession.close();
providerSession.close();
}
return result;

View file

@ -8,11 +8,11 @@ keycloak.jpa.hbm2ddl.auto=create
## Configure MongoDB (Useful just when keycloak.sessionFactory=mongo)
keycloak.mongodb.host=localhost
keycloak.mongodb.port=27017
keycloak.mongodb.databaseName=keycloakPerfTest
keycloak.model.mongo.host=localhost
keycloak.model.mongo.port=27017
keycloak.model.mongo.databaseName=keycloakPerfTest
# Should be DB dropped at startup of the test?
keycloak.mongodb.dropDatabaseOnStartup=true
keycloak.model.mongo.dropDatabaseOnStartup=true
## Specify Keycloak worker class

View file

@ -7,6 +7,6 @@ import org.keycloak.provider.Provider;
*/
public interface TimerProvider extends Provider {
public void schedule(Runnable runnable, String config);
public void schedule(Runnable runnable, long interval);
}

View file

@ -0,0 +1,25 @@
package org.keycloak.timer;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class TimerSpi implements Spi {
@Override
public String getName() {
return "timer";
}
@Override
public Class<? extends Provider> getProviderClass() {
return TimerProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return TimerProviderFactory.class;
}
}

View file

@ -0,0 +1 @@
org.keycloak.timer.TimerSpi

View file

@ -18,9 +18,7 @@ public class BasicTimerProvider implements TimerProvider {
}
@Override
public void schedule(final Runnable runnable, String config) {
long interval = Long.parseLong(config);
public void schedule(final Runnable runnable, final long interval) {
TimerTask task = new TimerTask() {
@Override
public void run() {

View file

@ -1,5 +1,6 @@
package org.keycloak.timer.basic;
import org.keycloak.Config;
import org.keycloak.provider.ProviderSession;
import org.keycloak.timer.TimerProvider;
import org.keycloak.timer.TimerProviderFactory;
@ -19,7 +20,7 @@ public class BasicTimerProviderFactory implements TimerProviderFactory {
}
@Override
public void init() {
public void init(Config.Scope config) {
timer = new Timer();
}
@ -34,8 +35,4 @@ public class BasicTimerProviderFactory implements TimerProviderFactory {
return "basic";
}
@Override
public boolean lazyLoad() {
return true;
}
}