Added 'keycloak-server.json' for configuring the server

Added SPI interface to detect SPI's
Converted Model to SPI/Provider to be loaded through ProviderSessionFactory
This commit is contained in:
Stian Thorgersen 2014-05-15 17:01:19 +01:00
parent a3ed02ea16
commit 855269f35d
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.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderFactoryLoader;
import java.util.HashMap; import java.util.HashMap;
import java.util.LinkedList;
import java.util.List; 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; package org.keycloak.audit.log;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener; import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory; import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -21,7 +21,7 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
} }
@Override @Override
@ -33,9 +33,4 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory {
return ID; return ID;
} }
@Override
public boolean lazyLoad() {
return false;
}
} }

View file

@ -1,9 +1,9 @@
package org.keycloak.audit.jpa; package org.keycloak.audit.jpa;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence; import javax.persistence.Persistence;
@ -22,7 +22,7 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store"); emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store");
} }
@ -36,9 +36,4 @@ public class JpaAuditProviderFactory implements AuditProviderFactory {
return ID; 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.DB;
import com.mongodb.MongoClient; import com.mongodb.MongoClient;
import com.mongodb.MongoCredential;
import com.mongodb.ServerAddress;
import com.mongodb.WriteConcern; import com.mongodb.WriteConcern;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import java.net.UnknownHostException; import java.net.UnknownHostException;
import java.util.Collections;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -29,11 +32,28 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
try { 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); 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) { } catch (UnknownHostException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
@ -49,9 +69,4 @@ public class MongoAuditProviderFactory implements AuditProviderFactory {
return ID; 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.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.keycloak.Config;
import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProvider;
import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.AuditProviderFactory;
import org.keycloak.audit.Event; import org.keycloak.audit.Event;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.ServiceLoader;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@ -23,9 +24,14 @@ public abstract class AbstractAuditProviderTest {
@Before @Before
public void before() { public void before() {
ProviderFactoryLoader<AuditProvider> loader = ProviderFactoryLoader.create(AuditProviderFactory.class); String providerId = getProviderId();
factory = loader.find(getProviderId()); ServiceLoader<AuditProviderFactory> factories = ServiceLoader.load(AuditProviderFactory.class);
factory.init(); for (AuditProviderFactory f : factories) {
if (f.getId().equals(providerId)) {
factory = f;
factory.init(Config.scope("audit", providerId));
}
}
provider = factory.create(null); 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; package org.keycloak.authentication.model;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants; import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory; import org.keycloak.authentication.AuthenticationProviderFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
/** /**
* @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a> * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
@ -17,7 +17,7 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
} }
@Override @Override
@ -29,8 +29,4 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio
return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL; return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL;
} }
@Override
public boolean lazyLoad() {
return false;
}
} }

View file

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

View file

@ -1,28 +1,25 @@
package org.keycloak.authentication.picketlink; 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.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.AuthProviderConstants;
import org.keycloak.authentication.AuthProviderStatus;
import org.keycloak.authentication.AuthUser; import org.keycloak.authentication.AuthUser;
import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderException; import org.keycloak.authentication.AuthenticationProviderException;
import org.keycloak.models.RealmModel;
import org.keycloak.picketlink.IdentityManagerProvider; import org.keycloak.picketlink.IdentityManagerProvider;
import org.keycloak.util.ProviderLoader;
import org.picketlink.idm.IdentityManagementException; import org.picketlink.idm.IdentityManagementException;
import org.picketlink.idm.IdentityManager; import org.picketlink.idm.IdentityManager;
import org.picketlink.idm.PartitionManager;
import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.Credentials;
import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.Password;
import org.picketlink.idm.credential.UsernamePasswordCredentials; import org.picketlink.idm.credential.UsernamePasswordCredentials;
import org.picketlink.idm.model.basic.BasicModel; import org.picketlink.idm.model.basic.BasicModel;
import org.picketlink.idm.model.basic.User; 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 * AuthenticationProvider, which delegates authentication to picketlink
* *

View file

@ -1,5 +1,6 @@
package org.keycloak.authentication.picketlink; package org.keycloak.authentication.picketlink;
import org.keycloak.Config;
import org.keycloak.authentication.AuthProviderConstants; import org.keycloak.authentication.AuthProviderConstants;
import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProvider;
import org.keycloak.authentication.AuthenticationProviderFactory; import org.keycloak.authentication.AuthenticationProviderFactory;
@ -17,7 +18,7 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
} }
@Override @Override
@ -29,8 +30,4 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr
return AuthProviderConstants.PROVIDER_NAME_PICKETLINK; 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; package org.keycloak.provider;
import org.keycloak.Config;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @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 T create(ProviderSession providerSession);
public void init(); public void init(Config.Scope config);
public void close(); public void close();
public String getId(); 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; package org.keycloak.provider;
import java.util.Set;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
@ -11,12 +9,6 @@ public interface ProviderSessionFactory {
void close(); 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(); 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 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: host, port and name of mongo database. So you can start keycloak with the command like this:
<programlisting><![CDATA[ <programlisting><![CDATA[
./standalone.sh -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost ./standalone.sh -Dkeycloak.model=mongo -Dkeycloak.model.mongo.host=localhost
-Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak -Dkeycloak.model.mongoport=27017 -Dkeycloak.model.mongo.db=keycloak
]]></programlisting> ]]></programlisting>
Note that when you install MongoDB on your laptop, it's usually on localhost/270717 by default. That's why properties 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 <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.mongo.db</literal> 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: has default value <literal>keycloak</literal> for name of underlying database. So the example above could be simplified like:
<programlisting><![CDATA[ <programlisting><![CDATA[
./standalone.sh -Dkeycloak.model=mongo ./standalone.sh -Dkeycloak.model=mongo

View file

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

View file

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

View file

@ -32,13 +32,6 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-audit-api</artifactId>
<version>${project.version}</version>
<scope>provided</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jboss.resteasy</groupId> <groupId>org.jboss.resteasy</groupId>
<artifactId>jaxrs-api</artifactId> <artifactId>jaxrs-api</artifactId>
@ -121,6 +114,12 @@
<version>${project.version}</version> <version>${project.version}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.keycloak</groupId>
<artifactId>keycloak-model-tests</artifactId>
<version>${project.version}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.hibernate.javax.persistence</groupId> <groupId>org.hibernate.javax.persistence</groupId>
<artifactId>hibernate-jpa-2.0-api</artifactId> <artifactId>hibernate-jpa-2.0-api</artifactId>
@ -149,10 +148,10 @@
</dependencies> </dependencies>
<properties> <properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host> <keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port> <keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db> <keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup> <keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
</properties> </properties>
<build> <build>
@ -179,10 +178,10 @@
</goals> </goals>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host> <keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port> <keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db> <keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup> <keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
</systemPropertyVariables> </systemPropertyVariables>
</configuration> </configuration>
</execution> </execution>
@ -207,7 +206,7 @@
<goal>start</goal> <goal>start</goal>
</goals> </goals>
<configuration> <configuration>
<port>${keycloak.mongo.port}</port> <port>${keycloak.model.mongo.port}</port>
<logging>file</logging> <logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile> <logFile>${project.build.directory}/mongodb.log</logFile>
</configuration> </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.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter; import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader; import org.keycloak.exportimport.io.ImportReader;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.util.ProviderLoader; import org.keycloak.util.ProviderLoader;
/** /**
@ -21,8 +21,8 @@ public class ExportImportProviderImpl implements ExportImportProvider {
public static final String ACTION_IMPORT = "import"; public static final String ACTION_IMPORT = "import";
@Override @Override
public void checkExportImport(KeycloakSessionFactory identitySessionFactory) { public void checkExportImport(ProviderSessionFactory providerSessionFactory) {
String exportImportAction = Config.getExportImportAction(); String exportImportAction = ExportImportConfig.getAction();
boolean export = false; boolean export = false;
boolean importt = false; boolean importt = false;
@ -35,7 +35,8 @@ public class ExportImportProviderImpl implements ExportImportProvider {
} }
if (export || importt) { if (export || importt) {
KeycloakSession session = identitySessionFactory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = session.getTransaction(); KeycloakTransaction transaction = session.getTransaction();
try { try {
transaction.begin(); transaction.begin();
@ -63,13 +64,13 @@ public class ExportImportProviderImpl implements ExportImportProvider {
} }
throw new RuntimeException(e); throw new RuntimeException(e);
} finally { } finally {
session.close(); providerSession.close();
} }
} }
} }
private ExportImportIOProvider getProvider() { private ExportImportIOProvider getProvider() {
String providerId = Config.getExportImportProvider(); String providerId = ExportImportConfig.getProvider();
logger.infof("Requested migration provider: " + providerId); logger.infof("Requested migration provider: " + providerId);
Iterable<ExportImportIOProvider> providers = ProviderLoader.load(ExportImportIOProvider.class); 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.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel; import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;

View file

@ -1,11 +1,11 @@
package org.keycloak.exportimport.io.directory; 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.ExportImportIOProvider;
import org.keycloak.exportimport.io.ExportWriter; import org.keycloak.exportimport.io.ExportWriter;
import org.keycloak.exportimport.io.ImportReader; 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 * 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 @Override
public ExportWriter getExportWriter() { public ExportWriter getExportWriter() {
String dir = Config.getExportImportDir(); String dir = ExportImportConfig.getDir();
return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter(); return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter();
} }
@Override @Override
public ImportReader getImportReader() { public ImportReader getImportReader() {
String dir = Config.getExportImportDir(); String dir = ExportImportConfig.getDir();
return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader(); return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader();
} }

View file

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

View file

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

View file

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

View file

@ -1,11 +1,10 @@
package org.keycloak.exportimport; package org.keycloak.exportimport;
import java.io.File;
import org.junit.Assert; import org.junit.Assert;
import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider; import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider;
import org.keycloak.models.Config; import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.models.KeycloakSessionFactory;
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 * 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 @Override
protected void exportModel(KeycloakSessionFactory factory) { protected void exportModel(ProviderSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT); ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT);
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID); ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID);
File zipFile = getZipFile(); File zipFile = getZipFile();
Config.setExportImportZipFile(zipFile.getAbsolutePath()); ExportImportConfig.setZipFile(zipFile.getAbsolutePath());
Config.setExportImportZipPassword("password123"); ExportImportConfig.setZipPassword("password123");
if (zipFile.exists()) { if (zipFile.exists()) {
zipFile.delete(); zipFile.delete();
@ -42,12 +41,12 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase {
} }
@Override @Override
protected void importModel(KeycloakSessionFactory factory) { protected void importModel(ProviderSessionFactory factory) {
Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT); ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT);
Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID); ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID);
File zipFile = getZipFile(); File zipFile = getZipFile();
Config.setExportImportZipFile(zipFile.getAbsolutePath()); ExportImportConfig.setZipFile(zipFile.getAbsolutePath());
Config.setExportImportZipPassword("password-invalid"); ExportImportConfig.setZipPassword("password-invalid");
// Try invalid password // Try invalid password
try { try {
@ -55,7 +54,7 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase {
Assert.fail("Not expected to be here. Exception should be thrown"); Assert.fail("Not expected to be here. Exception should be thrown");
} catch (Exception e) {}; } catch (Exception e) {};
Config.setExportImportZipPassword("password123"); ExportImportConfig.setZipPassword("password123");
new ExportImportProviderImpl().checkExportImport(factory); new ExportImportProviderImpl().checkExportImport(factory);
if (zipFile.exists()) { if (zipFile.exists()) {

View file

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

View file

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

View file

@ -2,7 +2,7 @@ package org.keycloak.theme;
import org.keycloak.freemarker.Theme; import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeProvider; import org.keycloak.freemarker.ThemeProvider;
import org.keycloak.models.Config; import org.keycloak.Config;
import java.io.File; import java.io.File;
import java.io.FileFilter; import java.io.FileFilter;
@ -19,7 +19,7 @@ public class FolderThemeProvider implements ThemeProvider {
private File rootDir; private File rootDir;
public FolderThemeProvider() { public FolderThemeProvider() {
String d = Config.getThemeDir(); String d = Config.scope("theme").get("dir");
if (d != null) { if (d != null) {
rootDir = new File(d); 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; package org.keycloak.models;
import org.keycloak.provider.Provider;
import java.util.List; import java.util.List;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface KeycloakSession { public interface KeycloakSession extends Provider {
KeycloakTransaction getTransaction(); KeycloakTransaction getTransaction();
RealmModel createRealm(String name); RealmModel createRealm(String name);

View file

@ -1,10 +1,13 @@
package org.keycloak.models; package org.keycloak.models;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderSession;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public interface KeycloakSessionFactory { public interface KeycloakSessionFactory extends ProviderFactory<KeycloakSession> {
KeycloakSession createSession(); KeycloakSession create(ProviderSession providerSession);
void close(); 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> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </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> <dependency>
<groupId>org.keycloak</groupId> <groupId>org.keycloak</groupId>
<artifactId>keycloak-model-api</artifactId> <artifactId>keycloak-model-api</artifactId>

View file

@ -1,28 +1,53 @@
package org.keycloak.models.jpa; package org.keycloak.models.jpa;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.provider.ProviderSession;
import javax.persistence.EntityManagerFactory; import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import java.util.Properties;
/** /**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a> * @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
* @version $Revision: 1 $ * @version $Revision: 1 $
*/ */
public class JpaKeycloakSessionFactory implements KeycloakSessionFactory { public class JpaKeycloakSessionFactory implements KeycloakSessionFactory {
protected EntityManagerFactory factory;
public JpaKeycloakSessionFactory(EntityManagerFactory factory) { protected EntityManagerFactory emf;
this.factory = factory;
@Override
public void init(Config.Scope config) {
emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", getHibernateProperties());
} }
@Override @Override
public KeycloakSession createSession() { public String getId() {
return new JpaKeycloakSession(factory.createEntityManager()); return "jpa";
}
@Override
public KeycloakSession create(ProviderSession providerSession) {
return new JpaKeycloakSession(emf.createEntityManager());
} }
@Override @Override
public void close() { 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> <version>${project.version}</version>
<scope>provided</scope> <scope>provided</scope>
</dependency> </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> <dependency>
<groupId>org.jboss.logging</groupId> <groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId> <artifactId>jboss-logging</artifactId>
@ -103,10 +91,10 @@
</dependencies> </dependencies>
<properties> <properties>
<keycloak.mongo.host>localhost</keycloak.mongo.host> <keycloak.model.mongo.host>localhost</keycloak.model.mongo.host>
<keycloak.mongo.port>27018</keycloak.mongo.port> <keycloak.model.mongo.port>27018</keycloak.model.mongo.port>
<keycloak.mongo.db>keycloak</keycloak.mongo.db> <keycloak.model.mongo.db>keycloak</keycloak.model.mongo.db>
<keycloak.mongo.clearOnStartup>true</keycloak.mongo.clearOnStartup> <keycloak.model.mongo.clearOnStartup>true</keycloak.model.mongo.clearOnStartup>
</properties> </properties>
<build> <build>
@ -133,10 +121,10 @@
</goals> </goals>
<configuration> <configuration>
<systemPropertyVariables> <systemPropertyVariables>
<keycloak.mongo.host>${keycloak.mongo.host}</keycloak.mongo.host> <keycloak.model.mongo.host>${keycloak.model.mongo.host}</keycloak.model.mongo.host>
<keycloak.mongo.port>${keycloak.mongo.port}</keycloak.mongo.port> <keycloak.model.mongo.port>${keycloak.model.mongo.port}</keycloak.model.mongo.port>
<keycloak.mongo.db>${keycloak.mongo.db}</keycloak.mongo.db> <keycloak.model.mongo.db>${keycloak.model.mongo.db}</keycloak.model.mongo.db>
<keycloak.mongo.clearOnStartup>${keycloak.mongo.clearOnStartup}</keycloak.mongo.clearOnStartup> <keycloak.model.mongo.clearOnStartup>${keycloak.model.mongo.clearOnStartup}</keycloak.model.mongo.clearOnStartup>
</systemPropertyVariables> </systemPropertyVariables>
<dependenciesToScan> <dependenciesToScan>
<dependency>org.keycloak:keycloak-model-tests</dependency> <dependency>org.keycloak:keycloak-model-tests</dependency>
@ -164,7 +152,7 @@
<goal>start</goal> <goal>start</goal>
</goals> </goals>
<configuration> <configuration>
<port>${keycloak.mongo.port}</port> <port>${keycloak.model.mongo.port}</port>
<logging>file</logging> <logging>file</logging>
<logFile>${project.build.directory}/mongodb.log</logFile> <logFile>${project.build.directory}/mongodb.log</logFile>
</configuration> </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; 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.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.entities.AuthenticationLinkEntity; 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.entities.SocialLinkEntity;
import org.keycloak.models.mongo.api.MongoStore; import org.keycloak.models.mongo.api.MongoStore;
import org.keycloak.models.mongo.impl.MongoStoreImpl; 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.MongoApplicationEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity; import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity; 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.MongoUserEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity;
import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity; 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 * KeycloakSessionFactory implementation based on MongoDB
@ -27,7 +35,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEnti
public class MongoKeycloakSessionFactory implements KeycloakSessionFactory { public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class); 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, MongoRealmEntity.class,
MongoUserEntity.class, MongoUserEntity.class,
MongoRoleEntity.class, MongoRoleEntity.class,
@ -42,21 +50,50 @@ public class MongoKeycloakSessionFactory implements KeycloakSessionFactory {
MongoUserSessionEntity.class MongoUserSessionEntity.class
}; };
private final MongoClientProvider mongoClientProvider; private MongoClient client;
private final MongoStore mongoStore;
public MongoKeycloakSessionFactory(MongoClientProvider provider) { private MongoStore mongoStore;
this.mongoClientProvider = provider;
this.mongoStore = new MongoStoreImpl(provider.getDB(), provider.clearCollectionsOnStartup(), MANAGED_ENTITY_TYPES); @Override
public String getId() {
return "mongo";
} }
@Override @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); return new MongoKeycloakSession(mongoStore);
} }
@Override @Override
public void close() { 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.Before;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
@ -28,7 +27,6 @@ import org.keycloak.util.JsonSerialization;
*/ */
public class AbstractModelTest { public class AbstractModelTest {
protected static KeycloakSessionFactory factory;
protected static ProviderSessionFactory providerSessionFactory; protected static ProviderSessionFactory providerSessionFactory;
protected KeycloakSession identitySession; protected KeycloakSession identitySession;
@ -37,41 +35,40 @@ public class AbstractModelTest {
@BeforeClass @BeforeClass
public static void beforeClass() { public static void beforeClass() {
factory = KeycloakApplication.createSessionFactory();
providerSessionFactory = KeycloakApplication.createProviderSessionFactory(); providerSessionFactory = KeycloakApplication.createProviderSessionFactory();
KeycloakSession identitySession = factory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
try { try {
identitySession.getTransaction().begin(); identitySession.getTransaction().begin();
new ApplianceBootstrap().bootstrap(identitySession, "/auth"); new ApplianceBootstrap().bootstrap(identitySession, "/auth");
identitySession.getTransaction().commit(); identitySession.getTransaction().commit();
} finally { } finally {
identitySession.close(); providerSession.close();
} }
} }
@AfterClass @AfterClass
public static void afterClass() { public static void afterClass() {
providerSessionFactory.close(); providerSessionFactory.close();
factory.close();
} }
@Before @Before
public void before() throws Exception { public void before() throws Exception {
identitySession = factory.createSession(); providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
identitySession.getTransaction().begin(); identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession); realmManager = new RealmManager(identitySession);
providerSession = providerSessionFactory.createSession();
} }
@After @After
public void after() throws Exception { public void after() throws Exception {
identitySession.getTransaction().commit(); identitySession.getTransaction().commit();
providerSession.close(); providerSession.close();
identitySession.close();
identitySession = factory.createSession(); providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
try { try {
identitySession.getTransaction().begin(); identitySession.getTransaction().begin();
@ -84,7 +81,7 @@ public class AbstractModelTest {
identitySession.getTransaction().commit(); identitySession.getTransaction().commit();
} finally { } finally {
identitySession.close(); providerSession.close();
} }
} }
@ -103,8 +100,10 @@ public class AbstractModelTest {
} }
protected void resetSession() { protected void resetSession() {
identitySession.close(); providerSession.close();
identitySession = factory.createSession();
providerSession = providerSessionFactory.createSession();
identitySession = providerSession.getProvider(KeycloakSession.class);
identitySession.getTransaction().begin(); identitySession.getTransaction().begin();
realmManager = new RealmManager(identitySession); realmManager = new RealmManager(identitySession);
} }

View file

@ -164,7 +164,7 @@ public class AuthenticationManagerTest extends AbstractModelTest {
realm.setAccessTokenLifespan(1000); realm.setAccessTokenLifespan(1000);
realm.addRequiredCredential(CredentialRepresentation.PASSWORD); realm.addRequiredCredential(CredentialRepresentation.PASSWORD);
realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER)); realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER));
protector = new BruteForceProtector(factory); protector = new BruteForceProtector(providerSessionFactory);
protector.start(); protector.start();
am = new AuthenticationManager(providerSession, protector); 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; package org.keycloak.picketlink.realm;
import org.keycloak.Config;
import org.keycloak.picketlink.IdentityManagerProvider; import org.keycloak.picketlink.IdentityManagerProvider;
import org.keycloak.picketlink.IdentityManagerProviderFactory; import org.keycloak.picketlink.IdentityManagerProviderFactory;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
@ -20,7 +21,7 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
partitionManagerRegistry = new PartitionManagerRegistry(); partitionManagerRegistry = new PartitionManagerRegistry();
} }
@ -33,8 +34,4 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi
return "realm"; 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.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
@ -22,7 +23,8 @@ public class UpsSecurityApplication extends KeycloakApplication {
@Override @Override
protected void setupDefaultRealm(String contextPath) { protected void setupDefaultRealm(String contextPath) {
super.setupDefaultRealm(contextPath); super.setupDefaultRealm(contextPath);
KeycloakSession session = factory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
// disable master realm by deleting the admin user. // disable master realm by deleting the admin user.
@ -33,7 +35,7 @@ public class UpsSecurityApplication extends KeycloakApplication {
if (admin != null) master.removeUser(admin.getLoginName()); if (admin != null) master.removeUser(admin.getLoginName());
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }

View file

@ -225,6 +225,39 @@
<scope>provided</scope> <scope>provided</scope>
</dependency> </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 --> <!-- export/import -->
<dependency> <dependency>
<groupId>org.keycloak</groupId> <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> <properties>
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="jboss.as.jpa.managed" value="false" />
</properties> </properties>
</persistence-unit> </persistence-unit>
@ -35,6 +36,7 @@
<properties> <properties>
<property name="hibernate.hbm2ddl.auto" value="update" /> <property name="hibernate.hbm2ddl.auto" value="update" />
<property name="jboss.as.jpa.managed" value="false" />
</properties> </properties>
</persistence-unit> </persistence-unit>

View file

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

View file

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

View file

@ -1,71 +1,99 @@
package org.keycloak.services; package org.keycloak.services;
import org.jboss.logging.Logger;
import org.keycloak.Config;
import org.keycloak.provider.Provider; import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory; import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.provider.Spi;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.ServiceLoader;
import java.util.Set; import java.util.Set;
public class DefaultProviderSessionFactory implements ProviderSessionFactory { public class DefaultProviderSessionFactory implements ProviderSessionFactory {
private Map<Class<? extends Provider>, ProviderFactoryLoader> loaders = new HashMap<Class<? extends Provider>, ProviderFactoryLoader>(); private static final Logger log = Logger.getLogger(DefaultProviderSessionFactory.class);
private Map<Class<? extends Provider>, String> defaultFactories = new HashMap<Class<? extends Provider>, String>();
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() { public ProviderSession createSession() {
return new DefaultProviderSession(this); 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() { public void close() {
for (ProviderFactoryLoader loader : loaders.values()) { for (Map<String, ProviderFactory> factories : factoriesMap.values()) {
loader.close(); 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); ResteasyProviderFactory.pushContext(ProviderSession.class, providerSession);
KeycloakSessionFactory factory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName()); KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
if (factory == null) throw new ServletException("Factory was null");
KeycloakSession session = factory.createSession();
ResteasyProviderFactory.pushContext(KeycloakSession.class, session); ResteasyProviderFactory.pushContext(KeycloakSession.class, session);
KeycloakTransaction tx = session.getTransaction(); KeycloakTransaction tx = session.getTransaction();
ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx); ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx);
@ -56,7 +54,6 @@ public class KeycloakSessionServletFilter implements Filter {
if (tx.isActive()) tx.rollback(); if (tx.isActive()) tx.rollback();
throw ex; throw ex;
} finally { } finally {
session.close();
providerSession.close(); providerSession.close();
ResteasyProviderFactory.clearContextData(); ResteasyProviderFactory.clearContextData();
} }

View file

@ -6,14 +6,15 @@ import org.jboss.logging.Logger;
import org.keycloak.models.AdminRoles; import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.AuthenticationProviderModel; import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserCredentialModel; import org.keycloak.models.UserCredentialModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.CredentialRepresentation; import org.keycloak.representations.idm.CredentialRepresentation;
import java.util.Collections; import java.util.Collections;
@ -26,26 +27,25 @@ public class ApplianceBootstrap {
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class); private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
public void bootstrap(KeycloakSessionFactory factory, String contextPath) { public void bootstrap(ProviderSessionFactory factory, String contextPath) {
KeycloakSession session = factory.createSession(); ProviderSession providerSession = factory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
try { try {
bootstrap(session, contextPath); bootstrap(session, contextPath);
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }
public void bootstrap(KeycloakSession session, String contextPath) { public void bootstrap(KeycloakSession session, String contextPath) {
if (session.getRealm(Config.getAdminRealm()) != null) { String adminRealmName = Config.getAdminRealm();
if (session.getRealm(adminRealmName) != null) {
return; return;
} }
String adminRealmName = Config.getAdminRealm();
logger.info("Initializing " + adminRealmName + " realm"); logger.info("Initializing " + adminRealmName + " realm");
RealmManager manager = new RealmManager(session); 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.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UsernameLoginFailureModel; import org.keycloak.models.UsernameLoginFailureModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.ClientConnection; import org.keycloak.services.ClientConnection;
import java.util.ArrayList; import java.util.ArrayList;
@ -25,7 +27,7 @@ public class BruteForceProtector implements Runnable {
protected volatile boolean run = true; protected volatile boolean run = true;
protected int maxDeltaTimeSeconds = 60 * 60 * 12; // 12 hours protected int maxDeltaTimeSeconds = 60 * 60 * 12; // 12 hours
protected KeycloakSessionFactory factory; protected ProviderSessionFactory factory;
protected CountDownLatch shutdownLatch = new CountDownLatch(1); protected CountDownLatch shutdownLatch = new CountDownLatch(1);
protected volatile long failures; protected volatile long failures;
@ -73,7 +75,7 @@ public class BruteForceProtector implements Runnable {
} }
} }
public BruteForceProtector(KeycloakSessionFactory factory) { public BruteForceProtector(ProviderSessionFactory factory) {
this.factory = factory; this.factory = factory;
} }
@ -160,7 +162,8 @@ public class BruteForceProtector implements Runnable {
events.add(take); events.add(take);
queue.drainTo(events, TRANSACTION_SIZE); queue.drainTo(events, TRANSACTION_SIZE);
Collections.sort(events); // we sort to avoid deadlock due to ordered updates. Maybe I'm overthinking this. 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(); session.getTransaction().begin();
try { try {
for (LoginEvent event : events) { for (LoginEvent event : events) {
@ -179,7 +182,7 @@ public class BruteForceProtector implements Runnable {
} }
} }
events.clear(); events.clear();
session.close(); providerSession.close();
} }
} catch (Exception e) { } catch (Exception e) {
logger.error("Failed processing event", 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.AuthenticationLinkModel;
import org.keycloak.models.AuthenticationProviderModel; import org.keycloak.models.AuthenticationProviderModel;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.OAuthClientModel; import org.keycloak.models.OAuthClientModel;

View file

@ -1,39 +1,30 @@
package org.keycloak.services.resources; 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.logging.Logger;
import org.jboss.resteasy.core.Dispatcher;
import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.Config;
import org.keycloak.SkeletonKeyContextResolver; 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.exportimport.ExportImportProvider;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.ModelProvider;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.ProviderFactoryLoader;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.DefaultProviderSessionFactory; import org.keycloak.services.DefaultProviderSessionFactory;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.ApplianceBootstrap;
import org.keycloak.services.managers.BruteForceProtector; import org.keycloak.services.managers.BruteForceProtector;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.managers.SocialRequestManager; import org.keycloak.services.managers.SocialRequestManager;
import org.keycloak.services.managers.TokenManager; import org.keycloak.services.managers.TokenManager;
import org.keycloak.services.resources.admin.AdminRoot; import org.keycloak.services.resources.admin.AdminRoot;
import org.keycloak.models.utils.ModelProviderUtils;
import org.keycloak.services.scheduled.ClearExpiredAuditEvents; import org.keycloak.services.scheduled.ClearExpiredAuditEvents;
import org.keycloak.services.scheduled.ClearExpiredUserSessions; import org.keycloak.services.scheduled.ClearExpiredUserSessions;
import org.keycloak.services.scheduled.ScheduledTaskRunner; import org.keycloak.services.scheduled.ScheduledTaskRunner;
import org.keycloak.services.util.JsonConfigProvider;
import org.keycloak.timer.TimerProvider; import org.keycloak.timer.TimerProvider;
import org.keycloak.timer.TimerProviderFactory;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import org.keycloak.util.ProviderLoader; import org.keycloak.util.ProviderLoader;
@ -41,12 +32,13 @@ import javax.servlet.ServletContext;
import javax.ws.rs.core.Application; import javax.ws.rs.core.Application;
import javax.ws.rs.core.Context; import javax.ws.rs.core.Context;
import javax.ws.rs.core.UriInfo; import javax.ws.rs.core.UriInfo;
import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.URI; import java.net.URI;
import java.util.Date; import java.net.URL;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
import java.util.Set; import java.util.Set;
@ -63,22 +55,21 @@ public class KeycloakApplication extends Application {
protected Set<Object> singletons = new HashSet<Object>(); protected Set<Object> singletons = new HashSet<Object>();
protected Set<Class<?>> classes = new HashSet<Class<?>>(); protected Set<Class<?>> classes = new HashSet<Class<?>>();
protected KeycloakSessionFactory factory;
protected ProviderSessionFactory providerSessionFactory; protected ProviderSessionFactory providerSessionFactory;
protected String contextPath; protected String contextPath;
public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) { public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
loadConfig();
this.providerSessionFactory = createProviderSessionFactory();
dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this); dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
this.contextPath = context.getContextPath(); this.contextPath = context.getContextPath();
this.factory = createSessionFactory(); BruteForceProtector protector = new BruteForceProtector(providerSessionFactory);
BruteForceProtector protector = new BruteForceProtector(factory);
dispatcher.getDefaultContextObjects().put(BruteForceProtector.class, protector); dispatcher.getDefaultContextObjects().put(BruteForceProtector.class, protector);
ResteasyProviderFactory.pushContext(BruteForceProtector.class, protector); // for injection ResteasyProviderFactory.pushContext(BruteForceProtector.class, protector); // for injection
protector.start(); protector.start();
context.setAttribute(BruteForceProtector.class.getName(), protector); context.setAttribute(BruteForceProtector.class.getName(), protector);
this.providerSessionFactory = createProviderSessionFactory();
context.setAttribute(KeycloakSessionFactory.class.getName(), factory);
context.setAttribute(ProviderSessionFactory.class.getName(), this.providerSessionFactory); context.setAttribute(ProviderSessionFactory.class.getName(), this.providerSessionFactory);
TokenManager tokenManager = new TokenManager(); TokenManager tokenManager = new TokenManager();
@ -95,7 +86,7 @@ public class KeycloakApplication extends Application {
setupDefaultRealm(context.getContextPath()); setupDefaultRealm(context.getContextPath());
setupScheduledTasks(providerSessionFactory, factory); setupScheduledTasks(providerSessionFactory);
importRealms(context); importRealms(context);
checkExportImportProvider(); checkExportImportProvider();
@ -115,55 +106,38 @@ public class KeycloakApplication extends Application {
return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build(); return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
} }
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);
}
}
protected void setupDefaultRealm(String contextPath) { protected void setupDefaultRealm(String contextPath) {
new ApplianceBootstrap().bootstrap(factory, contextPath); new ApplianceBootstrap().bootstrap(providerSessionFactory, contextPath);
}
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");
} }
public static DefaultProviderSessionFactory createProviderSessionFactory() { public static DefaultProviderSessionFactory createProviderSessionFactory() {
DefaultProviderSessionFactory factory = new DefaultProviderSessionFactory(); 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(); factory.init();
return factory; return factory;
} }
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory, final KeycloakSessionFactory keycloakSessionFactory) { public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory) {
ProviderFactory<TimerProvider> timerFactory = providerSessionFactory.getProviderFactory(TimerProvider.class); long interval = Config.scope("scheduled").getLong("interval") * 1000;
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 KeycloakSessionFactory getFactory() { TimerProvider timer = providerSessionFactory.createSession().getProvider(TimerProvider.class);
return factory; timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredAuditEvents()), interval);
timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredUserSessions()), interval);
} }
public ProviderSessionFactory getProviderSessionFactory() { public ProviderSessionFactory getProviderSessionFactory() {
@ -215,7 +189,8 @@ public class KeycloakApplication extends Application {
} }
public void importRealm(RealmRepresentation rep, String from) { public void importRealm(RealmRepresentation rep, String from) {
KeycloakSession session = factory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try { try {
session.getTransaction().begin(); session.getTransaction().begin();
RealmManager manager = new RealmManager(session); RealmManager manager = new RealmManager(session);
@ -238,7 +213,7 @@ public class KeycloakApplication extends Application {
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }
@ -255,7 +230,7 @@ public class KeycloakApplication extends Application {
if (providers.hasNext()) { if (providers.hasNext()) {
ExportImportProvider exportImport = providers.next(); ExportImportProvider exportImport = providers.next();
exportImport.checkExportImport(factory); exportImport.checkExportImport(providerSessionFactory);
} else { } else {
log.warn("No ExportImportProvider found!"); log.warn("No ExportImportProvider found!");
} }

View file

@ -10,14 +10,12 @@ import org.keycloak.freemarker.Theme;
import org.keycloak.freemarker.ThemeLoader; import org.keycloak.freemarker.ThemeLoader;
import org.keycloak.models.AdminRoles; import org.keycloak.models.AdminRoles;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Config;
import org.keycloak.models.Constants; import org.keycloak.models.Constants;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel; import org.keycloak.models.RoleModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.services.ForbiddenException;
import org.keycloak.services.managers.AppAuthManager; import org.keycloak.services.managers.AppAuthManager;
import org.keycloak.services.managers.ApplicationManager; import org.keycloak.services.managers.ApplicationManager;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
@ -280,12 +278,7 @@ public class AdminConsole {
try { try {
//logger.info("getting resource: " + path + " uri: " + uriInfo.getRequestUri().toString()); //logger.info("getting resource: " + path + " uri: " + uriInfo.getRequestUri().toString());
String themeName = realm.getAdminTheme(); Theme theme = ThemeLoader.createTheme(realm.getAdminTheme(), Theme.Type.ADMIN);
if (themeName == null || themeName.trim().equals("")) {
themeName = Config.getThemeAdmin();
}
Theme theme = ThemeLoader.createTheme(themeName, Theme.Type.ADMIN);
InputStream resource = theme.getResourceAsStream(path); InputStream resource = theme.getResourceAsStream(path);
if (resource != null) { if (resource != null) {
String contentType = mimeTypes.getContentType(path); 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 static final Logger logger = Logger.getLogger(ScheduledTaskRunner.class);
private final KeycloakSessionFactory keycloakSessionFactory;
private final ProviderSessionFactory providerSessionFactory; private final ProviderSessionFactory providerSessionFactory;
private final ScheduledTask task; private final ScheduledTask task;
public ScheduledTaskRunner(KeycloakSessionFactory keycloakSessionFactory, ProviderSessionFactory providerSessionFactory, ScheduledTask task) { public ScheduledTaskRunner(ProviderSessionFactory providerSessionFactory, ScheduledTask task) {
this.keycloakSessionFactory = keycloakSessionFactory;
this.providerSessionFactory = providerSessionFactory; this.providerSessionFactory = providerSessionFactory;
this.task = task; this.task = task;
} }
@Override @Override
public void run() { public void run() {
KeycloakSession keycloakSession = keycloakSessionFactory.createSession();
ProviderSession providerSession = providerSessionFactory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession keycloakSession = providerSession.getProvider(KeycloakSession.class);
try { try {
keycloakSession.getTransaction().begin(); keycloakSession.getTransaction().begin();
task.run(keycloakSession, providerSession); task.run(keycloakSession, providerSession);
@ -38,11 +36,6 @@ public class ScheduledTaskRunner implements Runnable {
keycloakSession.getTransaction().rollback(); keycloakSession.getTransaction().rollback();
} finally { } finally {
try {
keycloakSession.close();
} catch (Throwable t) {
logger.error("Failed to close KeycloakSession", t);
}
try { try {
providerSession.close(); providerSession.close();
} catch (Throwable t) { } 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 org.keycloak.util.ProviderLoader;
import java.util.ServiceLoader;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */

View file

@ -90,10 +90,9 @@ public class SimpleHttpTest {
@Test @Test
public void testGetCustomHeader() throws IOException { 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"); JsonNode h = o.get("headers");
assertEquals("application/json", h.get("Accept"));
assertEquals("bearer dsfsadfsdf", h.get("Authorization").getTextValue()); 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 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. 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. 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: 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: 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 TOTP codes
---------- ----------

View file

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

View file

@ -30,18 +30,16 @@ import io.undertow.servlet.api.FilterInfo;
import org.jboss.logging.Logger; import org.jboss.logging.Logger;
import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer;
import org.jboss.resteasy.spi.ResteasyDeployment; import org.jboss.resteasy.spi.ResteasyDeployment;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.services.filters.ClientConnectionFilter; import org.keycloak.services.filters.ClientConnectionFilter;
import org.keycloak.services.filters.KeycloakSessionServletFilter; import org.keycloak.services.filters.KeycloakSessionServletFilter;
import org.keycloak.services.managers.RealmManager; import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.theme.DefaultKeycloakThemeProvider;
import org.keycloak.util.JsonSerialization; import org.keycloak.util.JsonSerialization;
import javax.servlet.DispatcherType; import javax.servlet.DispatcherType;
@ -129,9 +127,8 @@ public class KeycloakServer {
throw new RuntimeException("Invalid resources directory"); throw new RuntimeException("Invalid resources directory");
} }
if (Config.getThemeDir() == null) { if (!System.getProperties().containsKey("keycloak.theme.dir")) {
System.setProperty(DefaultKeycloakThemeProvider.class.getName() + ".disabled", ""); System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
Config.setThemeDir(file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath());
} }
config.setResourcesHome(dir.getAbsolutePath()); config.setResourcesHome(dir.getAbsolutePath());
@ -162,8 +159,6 @@ public class KeycloakServer {
private KeycloakServerConfig config; private KeycloakServerConfig config;
private KeycloakSessionFactory factory;
private ProviderSessionFactory providerSessionFactory; private ProviderSessionFactory providerSessionFactory;
private UndertowJaxrsServer server; private UndertowJaxrsServer server;
@ -176,10 +171,6 @@ public class KeycloakServer {
this.config = config; this.config = config;
} }
public KeycloakSessionFactory getKeycloakSessionFactory() {
return factory;
}
public ProviderSessionFactory getProviderSessionFactory() { public ProviderSessionFactory getProviderSessionFactory() {
return providerSessionFactory; return providerSessionFactory;
} }
@ -194,7 +185,8 @@ public class KeycloakServer {
} }
public void importRealm(RealmRepresentation rep) { public void importRealm(RealmRepresentation rep) {
KeycloakSession session = factory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
try { try {
@ -217,12 +209,13 @@ public class KeycloakServer {
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }
protected void setupDevConfig() { protected void setupDevConfig() {
KeycloakSession session = factory.createSession(); ProviderSession providerSession = providerSessionFactory.createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
try { try {
@ -234,7 +227,7 @@ public class KeycloakServer {
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }
@ -266,7 +259,6 @@ public class KeycloakServer {
server.deploy(di); server.deploy(di);
factory = ((KeycloakApplication) deployment.getApplication()).getFactory();
providerSessionFactory = ((KeycloakApplication) deployment.getApplication()).getProviderSessionFactory(); providerSessionFactory = ((KeycloakApplication) deployment.getApplication()).getProviderSessionFactory();
setupDevConfig(); setupDevConfig();
@ -289,7 +281,6 @@ public class KeycloakServer {
public void stop() { public void stop() {
providerSessionFactory.close(); providerSessionFactory.close();
factory.close();
server.stop(); server.stop();
info("Stopped Keycloak"); 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.Description;
import org.hamcrest.Matcher; import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher; import org.hamcrest.TypeSafeMatcher;
import org.jboss.logging.Logger;
import org.junit.Assert; import org.junit.Assert;
import org.junit.rules.TestRule; import org.junit.rules.TestRule;
import org.junit.runners.model.Statement; import org.junit.runners.model.Statement;
import org.keycloak.Config;
import org.keycloak.audit.AuditListener; import org.keycloak.audit.AuditListener;
import org.keycloak.audit.AuditListenerFactory; import org.keycloak.audit.AuditListenerFactory;
import org.keycloak.audit.Details; import org.keycloak.audit.Details;
@ -58,11 +58,6 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
return "assert-events"; return "assert-events";
} }
@Override
public boolean lazyLoad() {
return false;
}
@Override @Override
public Statement apply(final Statement base, org.junit.runner.Description description) { public Statement apply(final Statement base, org.junit.runner.Description description) {
return new Statement() { return new Statement() {
@ -194,7 +189,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory {
} }
@Override @Override
public void init() { public void init(Config.Scope config) {
} }
@Override @Override

View file

@ -22,18 +22,14 @@
package org.keycloak.testsuite.account; package org.keycloak.testsuite.account;
import org.junit.After; import org.junit.After;
import org.junit.AfterClass;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Before; import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.ClassRule; import org.junit.ClassRule;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.audit.Details; import org.keycloak.audit.Details;
import org.keycloak.audit.Event; import org.keycloak.audit.Event;
import org.keycloak.audit.jpa.JpaAuditProviderFactory;
import org.keycloak.models.ApplicationModel; import org.keycloak.models.ApplicationModel;
import org.keycloak.models.Config;
import org.keycloak.models.PasswordPolicy; import org.keycloak.models.PasswordPolicy;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserCredentialModel; 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.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.adapters.action.SessionStats;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
@ -171,11 +172,11 @@ public class AdapterTest {
System.out.println(pageSource); System.out.println(pageSource);
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession(); ProviderSession providerSession = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("demo"); RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
int originalIdle = realm.getSsoSessionIdleTimeout(); int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1); realm.setSsoSessionIdleTimeout(1);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(providerSession, true);
Thread.sleep(2000); Thread.sleep(2000);
@ -184,10 +185,10 @@ public class AdapterTest {
driver.navigate().to("http://localhost:8081/product-portal"); driver.navigate().to("http://localhost:8081/product-portal");
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession(); providerSession = keycloakRule.startSession();
realm = session.getRealmByName("demo"); realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
realm.setSsoSessionIdleTimeout(originalIdle); realm.setSsoSessionIdleTimeout(originalIdle);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(providerSession, true);
} }
@Test @Test
public void testLoginSSOMax() throws Exception { public void testLoginSSOMax() throws Exception {
@ -202,11 +203,11 @@ public class AdapterTest {
System.out.println(pageSource); System.out.println(pageSource);
Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen"));
KeycloakSession session = keycloakRule.startSession(); ProviderSession providerSession = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("demo"); RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
int original = realm.getSsoSessionMaxLifespan(); int original = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1); realm.setSsoSessionMaxLifespan(1);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(providerSession, true);
Thread.sleep(2000); Thread.sleep(2000);
@ -215,9 +216,9 @@ public class AdapterTest {
driver.navigate().to("http://localhost:8081/product-portal"); driver.navigate().to("http://localhost:8081/product-portal");
Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL));
session = keycloakRule.startSession(); providerSession = keycloakRule.startSession();
realm = session.getRealmByName("demo"); realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo");
realm.setSsoSessionMaxLifespan(original); 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.Details;
import org.keycloak.audit.Errors; import org.keycloak.audit.Errors;
import org.keycloak.audit.Event; import org.keycloak.audit.Event;
import org.keycloak.models.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.AccessToken; import org.keycloak.representations.AccessToken;
import org.keycloak.representations.RefreshToken; import org.keycloak.representations.RefreshToken;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.AssertEvents;
import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient;
import org.keycloak.testsuite.OAuthClient.AccessTokenResponse; import org.keycloak.testsuite.OAuthClient.AccessTokenResponse;
@ -47,7 +45,11 @@ import org.keycloak.testsuite.rule.WebRule;
import org.keycloak.util.Time; import org.keycloak.util.Time;
import org.openqa.selenium.WebDriver; 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.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;
@ -181,8 +183,8 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId(); String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession(); ProviderSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("test"); RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
UserSessionModel userSession = realm.getUserSession(sessionId); UserSessionModel userSession = realm.getUserSession(sessionId);
int last = userSession.getLastSessionRefresh(); int last = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false); keycloakRule.stopSession(session, false);
@ -197,7 +199,7 @@ public class RefreshTokenTest {
Assert.assertEquals(200, tokenResponse.getStatusCode()); Assert.assertEquals(200, tokenResponse.getStatusCode());
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
userSession = realm.getUserSession(sessionId); userSession = realm.getUserSession(sessionId);
int next = userSession.getLastSessionRefresh(); int next = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false); keycloakRule.stopSession(session, false);
@ -208,7 +210,7 @@ public class RefreshTokenTest {
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int lastAccessTokenLifespan = realm.getAccessTokenLifespan(); int lastAccessTokenLifespan = realm.getAccessTokenLifespan();
realm.setAccessTokenLifespan(100000); realm.setAccessTokenLifespan(100000);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(session, true);
@ -217,7 +219,7 @@ public class RefreshTokenTest {
tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password");
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
userSession = realm.getUserSession(sessionId); userSession = realm.getUserSession(sessionId);
next = userSession.getLastSessionRefresh(); next = userSession.getLastSessionRefresh();
keycloakRule.stopSession(session, false); keycloakRule.stopSession(session, false);
@ -226,7 +228,7 @@ public class RefreshTokenTest {
Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6))); Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6)));
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int originalIdle = realm.getSsoSessionIdleTimeout(); int originalIdle = realm.getSsoSessionIdleTimeout();
realm.setSsoSessionIdleTimeout(1); realm.setSsoSessionIdleTimeout(1);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(session, true);
@ -243,7 +245,7 @@ public class RefreshTokenTest {
events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN);
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
realm.setSsoSessionIdleTimeout(originalIdle); realm.setSsoSessionIdleTimeout(originalIdle);
realm.setAccessTokenLifespan(lastAccessTokenLifespan); realm.setAccessTokenLifespan(lastAccessTokenLifespan);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(session, true);
@ -266,8 +268,8 @@ public class RefreshTokenTest {
String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId(); String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId();
KeycloakSession session = keycloakRule.startSession(); ProviderSession session = keycloakRule.startSession();
RealmModel realm = session.getRealmByName("test"); RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
int maxLifespan = realm.getSsoSessionMaxLifespan(); int maxLifespan = realm.getSsoSessionMaxLifespan();
realm.setSsoSessionMaxLifespan(1); realm.setSsoSessionMaxLifespan(1);
keycloakRule.stopSession(session, true); keycloakRule.stopSession(session, true);
@ -281,7 +283,7 @@ public class RefreshTokenTest {
assertNull(tokenResponse.getRefreshToken()); assertNull(tokenResponse.getRefreshToken());
session = keycloakRule.startSession(); session = keycloakRule.startSession();
realm = session.getRealmByName("test"); realm = session.getProvider(KeycloakSession.class).getRealmByName("test");
realm.setSsoSessionMaxLifespan(maxLifespan); realm.setSsoSessionMaxLifespan(maxLifespan);
keycloakRule.stopSession(session, true); 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.ServletInfo;
import io.undertow.servlet.api.WebResourceCollection; import io.undertow.servlet.api.WebResourceCollection;
import org.junit.rules.ExternalResource; import org.junit.rules.ExternalResource;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
import org.keycloak.provider.ProviderSession;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.services.managers.ModelToRepresentation; import org.keycloak.services.managers.ModelToRepresentation;
@ -33,31 +34,33 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
server = new KeycloakServer(); server = new KeycloakServer();
server.start(); server.start();
setupKeycloak(); setupKeycloak();
} }
public UserRepresentation getUser(String realm, String name) { public UserRepresentation getUser(String realm, String name) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try { try {
UserModel user = session.getRealmByName(realm).getUser(name); UserModel user = session.getRealmByName(realm).getUser(name);
return user != null ? ModelToRepresentation.toRepresentation(user) : null; return user != null ? ModelToRepresentation.toRepresentation(user) : null;
} finally { } finally {
session.close(); providerSession.close();
} }
} }
public UserRepresentation getUserById(String realm, String id) { public UserRepresentation getUserById(String realm, String id) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
try { try {
return ModelToRepresentation.toRepresentation(session.getRealmByName(realm).getUserById(id)); return ModelToRepresentation.toRepresentation(session.getRealmByName(realm).getUserById(id));
} finally { } finally {
session.close(); providerSession.close();
} }
} }
protected void setupKeycloak() { protected void setupKeycloak() {
KeycloakSession session = server.getKeycloakSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
try { try {
@ -69,9 +72,8 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
session.close(); providerSession.close();
} }
} }
protected void configure(RealmManager manager, RealmModel adminRealm) { protected void configure(RealmManager manager, RealmModel adminRealm) {
@ -134,15 +136,16 @@ public abstract class AbstractKeycloakRule extends ExternalResource {
return JsonSerialization.readValue(bytes, RealmRepresentation.class); return JsonSerialization.readValue(bytes, RealmRepresentation.class);
} }
public KeycloakSession startSession() { public ProviderSession startSession() {
KeycloakSession session = server.getKeycloakSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
return session; return providerSession;
} }
public void stopSession(KeycloakSession session, boolean commit) { public void stopSession(ProviderSession session, boolean commit) {
if (commit) { if (commit) {
session.getTransaction().commit(); session.getProvider(KeycloakSession.class).getTransaction().commit();
} }
session.close(); session.close();
} }

View file

@ -21,7 +21,7 @@
*/ */
package org.keycloak.testsuite.rule; package org.keycloak.testsuite.rule;
import org.keycloak.models.Config; import org.keycloak.Config;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
@ -61,8 +61,8 @@ public class KeycloakRule extends AbstractKeycloakRule {
} }
public void configure(KeycloakSetup configurer) { public void configure(KeycloakSetup configurer) {
KeycloakSession session = server.getKeycloakSessionFactory().createSession();
ProviderSession providerSession = server.getProviderSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession();
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
session.getTransaction().begin(); session.getTransaction().begin();
try { try {
@ -77,17 +77,16 @@ public class KeycloakRule extends AbstractKeycloakRule {
session.getTransaction().commit(); session.getTransaction().commit();
} finally { } finally {
providerSession.close(); providerSession.close();
session.close();
} }
} }
public void removeUserSession(String sessionId) { public void removeUserSession(String sessionId) {
KeycloakSession keycloakSession = startSession(); ProviderSession providerSession = startSession();
RealmModel realm = keycloakSession.getRealm("test"); RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealm("test");
UserSessionModel session = realm.getUserSession(sessionId); UserSessionModel session = realm.getUserSession(sessionId);
assertNotNull(session); assertNotNull(session);
realm.removeUserSession(session); realm.removeUserSession(session);
stopSession(keycloakSession, true); stopSession(providerSession, true);
} }
public abstract static class KeycloakSetup { 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.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.KeycloakTransaction; import org.keycloak.models.KeycloakTransaction;
import org.keycloak.provider.ProviderSession;
import org.keycloak.provider.ProviderSessionFactory;
import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.services.resources.KeycloakApplication;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -18,17 +20,17 @@ import java.util.concurrent.atomic.AtomicInteger;
public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { 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 @Override
public KeycloakSessionFactory call() throws Exception { public ProviderSessionFactory call() throws Exception {
return KeycloakApplication.createSessionFactory(); return KeycloakApplication.createProviderSessionFactory();
} }
}); });
private static AtomicInteger counter = new AtomicInteger(); private static AtomicInteger counter = new AtomicInteger();
private KeycloakSessionFactory factory; private ProviderSessionFactory factory;
// private KeycloakSession identitySession; // private KeycloakSession identitySession;
private Worker worker; private Worker worker;
private boolean setupSuccess = false; private boolean setupSuccess = false;
@ -42,7 +44,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
worker = getWorker(); worker = getWorker();
factory = getFactory(); factory = getFactory();
KeycloakSession identitySession = factory.createSession(); ProviderSession providerSession = factory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = identitySession.getTransaction(); KeycloakTransaction transaction = identitySession.getTransaction();
transaction.begin(); transaction.begin();
@ -56,11 +59,11 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
} else { } else {
transaction.rollback(); transaction.rollback();
} }
identitySession.close(); providerSession.close();
} }
} }
private static KeycloakSessionFactory getFactory() { private static ProviderSessionFactory getFactory() {
factoryProvider.run(); factoryProvider.run();
try { try {
return factoryProvider.get(); return factoryProvider.get();
@ -98,7 +101,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
return result; return result;
} }
KeycloakSession identitySession = factory.createSession(); ProviderSession providerSession = factory.createSession();
KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class);
KeycloakTransaction transaction = identitySession.getTransaction(); KeycloakTransaction transaction = identitySession.getTransaction();
try { try {
transaction.begin(); transaction.begin();
@ -114,7 +118,7 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient {
} finally { } finally {
result.sampleEnd(); result.sampleEnd();
result.setSuccessful(true); result.setSuccessful(true);
identitySession.close(); providerSession.close();
} }
return result; return result;

View file

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

View file

@ -7,6 +7,6 @@ import org.keycloak.provider.Provider;
*/ */
public interface TimerProvider extends 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 @Override
public void schedule(final Runnable runnable, String config) { public void schedule(final Runnable runnable, final long interval) {
long interval = Long.parseLong(config);
TimerTask task = new TimerTask() { TimerTask task = new TimerTask() {
@Override @Override
public void run() { public void run() {

View file

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