Merge remote-tracking branch 'upstream/master'
This commit is contained in:
commit
c2bf6c3822
100 files changed
+1414
-1383
No files matched your search
@@ -102,6 +102,7 @@
|
||||
<artifactId>jboss-servlet-api_3.0_spec</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
|
||||
@@ -6,6 +6,8 @@ import org.keycloak.provider.ProviderSession;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@@ -13,6 +15,7 @@ import java.util.Set;
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
*/
|
||||
public class DefaultProviderSession implements ProviderSession {
|
||||
|
||||
private DefaultProviderSessionFactory factory;
|
||||
private Map<Integer, Provider> providers = new HashMap<Integer, Provider>();
|
||||
|
||||
@@ -21,8 +24,16 @@ public class DefaultProviderSession implements ProviderSession {
|
||||
}
|
||||
|
||||
public <T extends Provider> T getProvider(Class<T> clazz) {
|
||||
String id = factory.getDefaultProvider(clazz);
|
||||
return id != null ? getProvider(clazz, id) : null;
|
||||
Integer hash = clazz.hashCode();
|
||||
T provider = (T) providers.get(hash);
|
||||
if (provider == null) {
|
||||
ProviderFactory<T> providerFactory = factory.getProviderFactory(clazz);
|
||||
if (providerFactory != null) {
|
||||
provider = providerFactory.create(this);
|
||||
providers.put(hash, provider);
|
||||
}
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
public <T extends Provider> T getProvider(Class<T> clazz, String id) {
|
||||
@@ -39,15 +50,14 @@ public class DefaultProviderSession implements ProviderSession {
|
||||
}
|
||||
|
||||
public <T extends Provider> Set<String> listProviderIds(Class<T> clazz) {
|
||||
return factory.providerIds(clazz);
|
||||
return factory.getAllProviderIds(clazz);
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Provider> Set<T> getAllProviders(Class<T> clazz) {
|
||||
Set<String> providerIds = listProviderIds(clazz);
|
||||
Set<T> providers = new HashSet<T>();
|
||||
for (String providerId : providerIds) {
|
||||
providers.add(getProvider(clazz, providerId));
|
||||
for (String id : listProviderIds(clazz)) {
|
||||
providers.add(getProvider(clazz, id));
|
||||
}
|
||||
return providers;
|
||||
}
|
||||
|
||||
@@ -1,71 +1,99 @@
|
||||
package org.keycloak.services;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.ProviderFactoryLoader;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.provider.ProviderSessionFactory;
|
||||
import org.keycloak.provider.Spi;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.Set;
|
||||
|
||||
public class DefaultProviderSessionFactory implements ProviderSessionFactory {
|
||||
|
||||
private Map<Class<? extends Provider>, ProviderFactoryLoader> loaders = new HashMap<Class<? extends Provider>, ProviderFactoryLoader>();
|
||||
private Map<Class<? extends Provider>, String> defaultFactories = new HashMap<Class<? extends Provider>, String>();
|
||||
private static final Logger log = Logger.getLogger(DefaultProviderSessionFactory.class);
|
||||
|
||||
private Map<Class<? extends Provider>, String> provider = new HashMap<Class<? extends Provider>, String>();
|
||||
private Map<Class<? extends Provider>, Map<String, ProviderFactory>> factoriesMap = new HashMap<Class<? extends Provider>, Map<String, ProviderFactory>>();
|
||||
|
||||
public void init() {
|
||||
for (Spi spi : ServiceLoader.load(Spi.class)) {
|
||||
Map<String, ProviderFactory> factories = new HashMap<String, ProviderFactory>();
|
||||
factoriesMap.put(spi.getProviderClass(), factories);
|
||||
|
||||
String provider = Config.getProvider(spi.getName());
|
||||
if (provider != null) {
|
||||
this.provider.put(spi.getProviderClass(), provider);
|
||||
|
||||
ProviderFactory factory = loadProviderFactory(spi, provider);
|
||||
Config.Scope scope = Config.scope(spi.getName(), provider);
|
||||
factory.init(scope);
|
||||
log.debug("Initialized " + factory.getClass().getName() + " (config = " + scope + ")");
|
||||
|
||||
factories.put(factory.getId(), factory);
|
||||
|
||||
log.info("Loaded SPI " + spi.getName() + " (provider = " + provider + ")");
|
||||
} else {
|
||||
for (ProviderFactory factory : ServiceLoader.load(spi.getProviderFactoryClass())) {
|
||||
Config.Scope scope = Config.scope(spi.getName(), factory.getId());
|
||||
factory.init(scope);
|
||||
log.debug("Initialized " + factory.getClass().getName() + " (config = " + scope + ")");
|
||||
|
||||
factories.put(factory.getId(), factory);
|
||||
}
|
||||
|
||||
if (factories.size() == 1) {
|
||||
provider = factories.values().iterator().next().getId();
|
||||
this.provider.put(spi.getProviderClass(), provider);
|
||||
|
||||
log.info("Loaded SPI " + spi.getName() + " (provider = " + provider + ")");
|
||||
} else {
|
||||
log.info("Loaded SPI " + spi.getName() + " (providers = " + factories.keySet() + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private ProviderFactory loadProviderFactory(Spi spi, String id) {
|
||||
for (ProviderFactory factory : ServiceLoader.load(spi.getProviderFactoryClass())) {
|
||||
if (factory.getId().equals(id)){
|
||||
return factory;
|
||||
}
|
||||
}
|
||||
throw new RuntimeException("Failed to find provider " + id + " for " + spi.getName());
|
||||
}
|
||||
|
||||
public ProviderSession createSession() {
|
||||
return new DefaultProviderSession(this);
|
||||
}
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
|
||||
return getProviderFactory(clazz, provider.get(clazz));
|
||||
}
|
||||
|
||||
<T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
|
||||
return factoriesMap.get(clazz).get(id);
|
||||
}
|
||||
|
||||
<T extends Provider> Set<String> getAllProviderIds(Class<T> clazz) {
|
||||
Set<String> ids = new HashSet<String>();
|
||||
for (ProviderFactory f : factoriesMap.get(clazz).values()) {
|
||||
ids.add(f.getId());
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
|
||||
public void close() {
|
||||
for (ProviderFactoryLoader loader : loaders.values()) {
|
||||
loader.close();
|
||||
for (Map<String, ProviderFactory> factories : factoriesMap.values()) {
|
||||
for (ProviderFactory factory : factories.values()) {
|
||||
factory.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz) {
|
||||
String id = defaultFactories.get(clazz);
|
||||
if (id == null) {
|
||||
return null;
|
||||
}
|
||||
return getProviderFactory(clazz, id);
|
||||
}
|
||||
|
||||
public <T extends Provider> ProviderFactory<T> getProviderFactory(Class<T> clazz, String id) {
|
||||
ProviderFactoryLoader loader = getLoader(clazz);
|
||||
return loader != null ? loader.find(id) : null;
|
||||
}
|
||||
|
||||
public Set<String> providerIds(Class<? extends Provider> clazz) {
|
||||
ProviderFactoryLoader loader = getLoader(clazz);
|
||||
return loader != null ? loader.providerIds() : null;
|
||||
}
|
||||
|
||||
public String getDefaultProvider(Class<? extends Provider> clazz) {
|
||||
return defaultFactories.get(clazz);
|
||||
}
|
||||
|
||||
public void registerLoader(Class<? extends Provider> clazz, ProviderFactoryLoader loader) {
|
||||
loaders.put(clazz, loader);
|
||||
|
||||
}
|
||||
|
||||
public void registerLoader(Class<? extends Provider> clazz, ProviderFactoryLoader loader, String defaultProvider) {
|
||||
loaders.put(clazz, loader);
|
||||
defaultFactories.put(clazz, defaultProvider);
|
||||
|
||||
}
|
||||
|
||||
public void init() {
|
||||
for (ProviderFactoryLoader l : loaders.values()) {
|
||||
l.init();
|
||||
}
|
||||
}
|
||||
|
||||
private <T extends Provider> ProviderFactoryLoader getLoader(Class<T> clazz) {
|
||||
return loaders.get(clazz);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-4
@@ -32,9 +32,7 @@ public class KeycloakSessionServletFilter implements Filter {
|
||||
|
||||
ResteasyProviderFactory.pushContext(ProviderSession.class, providerSession);
|
||||
|
||||
KeycloakSessionFactory factory = (KeycloakSessionFactory) servletRequest.getServletContext().getAttribute(KeycloakSessionFactory.class.getName());
|
||||
if (factory == null) throw new ServletException("Factory was null");
|
||||
KeycloakSession session = factory.createSession();
|
||||
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
||||
ResteasyProviderFactory.pushContext(KeycloakSession.class, session);
|
||||
KeycloakTransaction tx = session.getTransaction();
|
||||
ResteasyProviderFactory.pushContext(KeycloakTransaction.class, tx);
|
||||
@@ -56,7 +54,6 @@ public class KeycloakSessionServletFilter implements Filter {
|
||||
if (tx.isActive()) tx.rollback();
|
||||
throw ex;
|
||||
} finally {
|
||||
session.close();
|
||||
providerSession.close();
|
||||
ResteasyProviderFactory.clearContextData();
|
||||
}
|
||||
|
||||
@@ -6,14 +6,15 @@ import org.jboss.logging.Logger;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserCredentialModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.provider.ProviderSessionFactory;
|
||||
import org.keycloak.representations.idm.CredentialRepresentation;
|
||||
|
||||
import java.util.Collections;
|
||||
@@ -26,26 +27,25 @@ public class ApplianceBootstrap {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ApplianceBootstrap.class);
|
||||
|
||||
public void bootstrap(KeycloakSessionFactory factory, String contextPath) {
|
||||
KeycloakSession session = factory.createSession();
|
||||
public void bootstrap(ProviderSessionFactory factory, String contextPath) {
|
||||
ProviderSession providerSession = factory.createSession();
|
||||
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
||||
session.getTransaction().begin();
|
||||
|
||||
try {
|
||||
bootstrap(session, contextPath);
|
||||
session.getTransaction().commit();
|
||||
} finally {
|
||||
session.close();
|
||||
providerSession.close();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void bootstrap(KeycloakSession session, String contextPath) {
|
||||
if (session.getRealm(Config.getAdminRealm()) != null) {
|
||||
String adminRealmName = Config.getAdminRealm();
|
||||
if (session.getRealm(adminRealmName) != null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String adminRealmName = Config.getAdminRealm();
|
||||
|
||||
logger.info("Initializing " + adminRealmName + " realm");
|
||||
|
||||
RealmManager manager = new RealmManager(session);
|
||||
|
||||
@@ -6,6 +6,8 @@ import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UsernameLoginFailureModel;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.provider.ProviderSessionFactory;
|
||||
import org.keycloak.services.ClientConnection;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -25,7 +27,7 @@ public class BruteForceProtector implements Runnable {
|
||||
|
||||
protected volatile boolean run = true;
|
||||
protected int maxDeltaTimeSeconds = 60 * 60 * 12; // 12 hours
|
||||
protected KeycloakSessionFactory factory;
|
||||
protected ProviderSessionFactory factory;
|
||||
protected CountDownLatch shutdownLatch = new CountDownLatch(1);
|
||||
|
||||
protected volatile long failures;
|
||||
@@ -73,7 +75,7 @@ public class BruteForceProtector implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
public BruteForceProtector(KeycloakSessionFactory factory) {
|
||||
public BruteForceProtector(ProviderSessionFactory factory) {
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
@@ -160,7 +162,8 @@ public class BruteForceProtector implements Runnable {
|
||||
events.add(take);
|
||||
queue.drainTo(events, TRANSACTION_SIZE);
|
||||
Collections.sort(events); // we sort to avoid deadlock due to ordered updates. Maybe I'm overthinking this.
|
||||
KeycloakSession session = factory.createSession();
|
||||
ProviderSession providerSession = factory.createSession();
|
||||
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
||||
session.getTransaction().begin();
|
||||
try {
|
||||
for (LoginEvent event : events) {
|
||||
@@ -179,7 +182,7 @@ public class BruteForceProtector implements Runnable {
|
||||
}
|
||||
}
|
||||
events.clear();
|
||||
session.close();
|
||||
providerSession.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("Failed processing event", e);
|
||||
|
||||
@@ -7,7 +7,7 @@ import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.AuthenticationLinkModel;
|
||||
import org.keycloak.models.AuthenticationProviderModel;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.OAuthClientModel;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -8,7 +10,7 @@ import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.jboss.resteasy.spi.HttpRequest;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.util.CollectionUtil;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
@@ -16,20 +18,25 @@ import org.keycloak.models.UserModel;
|
||||
public class Cors {
|
||||
|
||||
public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1);
|
||||
public static final String DEFAULT_ALLOW_METHODS = "GET, OPTIONS";
|
||||
public static final String DEFAULT_ALLOW_METHODS = "GET, HEAD, OPTIONS";
|
||||
public static final String DEFAULT_ALLOW_HEADERS = "Origin, Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers";
|
||||
|
||||
public static final String ORIGIN = "Origin";
|
||||
public static final String ORIGIN_HEADER = "Origin";
|
||||
public static final String AUTHORIZATION_HEADER = "Authorization";
|
||||
|
||||
public static final String ACCESS_CONTROL_ALLOW_ORIGIN = "Access-Control-Allow-Origin";
|
||||
public static final String ACCESS_CONTROL_ALLOW_METHODS = "Access-Control-Allow-Methods";
|
||||
public static final String ACCESS_CONTROL_ALLOW_HEADERS = "Access-Control-Allow-Headers";
|
||||
public static final String ACCESS_CONTROL_EXPOSE_HEADERS = "Access-Control-Expose-Headers";
|
||||
public static final String ACCESS_CONTROL_ALLOW_CREDENTIALS = "Access-Control-Allow-Credentials";
|
||||
public static final String ACCESS_CONTROL_MAX_AGE = "Access-Control-Max-Age";
|
||||
|
||||
|
||||
private HttpRequest request;
|
||||
private ResponseBuilder response;
|
||||
private Set<String> allowedOrigins;
|
||||
private String[] allowedMethods;
|
||||
private Set<String> allowedMethods;
|
||||
private Set<String> exposedHeaders;
|
||||
|
||||
private boolean preflight;
|
||||
private boolean auth;
|
||||
@@ -61,12 +68,17 @@ public class Cors {
|
||||
}
|
||||
|
||||
public Cors allowedMethods(String... allowedMethods) {
|
||||
this.allowedMethods = allowedMethods;
|
||||
this.allowedMethods = new HashSet<String>(Arrays.asList(allowedMethods));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Cors exposedHeaders(String... exposedHeaders) {
|
||||
this.exposedHeaders = new HashSet<String>(Arrays.asList(exposedHeaders));
|
||||
return this;
|
||||
}
|
||||
|
||||
public Response build() {
|
||||
String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN);
|
||||
String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER);
|
||||
if (origin == null) {
|
||||
return response.build();
|
||||
}
|
||||
@@ -78,21 +90,20 @@ public class Cors {
|
||||
response.header(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
|
||||
|
||||
if (allowedMethods != null) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int i = 0; i < allowedMethods.length; i++) {
|
||||
if (i > 0) {
|
||||
sb.append(", ");
|
||||
}
|
||||
sb.append(allowedMethods[i]);
|
||||
}
|
||||
response.header(ACCESS_CONTROL_ALLOW_METHODS, sb.toString());
|
||||
response.header(ACCESS_CONTROL_ALLOW_METHODS, CollectionUtil.join(allowedMethods));
|
||||
} else {
|
||||
response.header(ACCESS_CONTROL_ALLOW_METHODS, DEFAULT_ALLOW_METHODS);
|
||||
}
|
||||
|
||||
if (exposedHeaders != null) {
|
||||
response.header(ACCESS_CONTROL_EXPOSE_HEADERS, CollectionUtil.join(exposedHeaders));
|
||||
}
|
||||
|
||||
response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth));
|
||||
if (auth) {
|
||||
response.header(ACCESS_CONTROL_ALLOW_HEADERS, "Authorization");
|
||||
response.header(ACCESS_CONTROL_ALLOW_HEADERS, String.format("%s, %s", DEFAULT_ALLOW_HEADERS, AUTHORIZATION_HEADER));
|
||||
} else {
|
||||
response.header(ACCESS_CONTROL_ALLOW_HEADERS, DEFAULT_ALLOW_HEADERS);
|
||||
}
|
||||
|
||||
response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE);
|
||||
|
||||
@@ -1,39 +1,30 @@
|
||||
package org.keycloak.services.resources;
|
||||
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.codehaus.jackson.JsonNode;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
import org.jboss.logging.Logger;
|
||||
import org.jboss.resteasy.core.Dispatcher;
|
||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
||||
import org.keycloak.Config;
|
||||
import org.keycloak.SkeletonKeyContextResolver;
|
||||
import org.keycloak.audit.AuditListener;
|
||||
import org.keycloak.audit.AuditListenerFactory;
|
||||
import org.keycloak.audit.AuditProvider;
|
||||
import org.keycloak.audit.AuditProviderFactory;
|
||||
import org.keycloak.authentication.AuthenticationProvider;
|
||||
import org.keycloak.authentication.AuthenticationProviderFactory;
|
||||
import org.keycloak.exportimport.ExportImportProvider;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.ModelProvider;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.provider.ProviderFactory;
|
||||
import org.keycloak.provider.ProviderFactoryLoader;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.provider.ProviderSessionFactory;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.DefaultProviderSessionFactory;
|
||||
import org.keycloak.provider.ProviderSessionFactory;
|
||||
import org.keycloak.services.managers.ApplianceBootstrap;
|
||||
import org.keycloak.services.managers.BruteForceProtector;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
import org.keycloak.services.managers.SocialRequestManager;
|
||||
import org.keycloak.services.managers.TokenManager;
|
||||
import org.keycloak.services.resources.admin.AdminRoot;
|
||||
import org.keycloak.models.utils.ModelProviderUtils;
|
||||
import org.keycloak.services.scheduled.ClearExpiredAuditEvents;
|
||||
import org.keycloak.services.scheduled.ClearExpiredUserSessions;
|
||||
import org.keycloak.services.scheduled.ScheduledTaskRunner;
|
||||
import org.keycloak.services.util.JsonConfigProvider;
|
||||
import org.keycloak.timer.TimerProvider;
|
||||
import org.keycloak.timer.TimerProviderFactory;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.util.ProviderLoader;
|
||||
|
||||
@@ -41,12 +32,13 @@ import javax.servlet.ServletContext;
|
||||
import javax.ws.rs.core.Application;
|
||||
import javax.ws.rs.core.Context;
|
||||
import javax.ws.rs.core.UriInfo;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URI;
|
||||
import java.util.Date;
|
||||
import java.net.URL;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
@@ -63,22 +55,21 @@ public class KeycloakApplication extends Application {
|
||||
protected Set<Object> singletons = new HashSet<Object>();
|
||||
protected Set<Class<?>> classes = new HashSet<Class<?>>();
|
||||
|
||||
protected KeycloakSessionFactory factory;
|
||||
protected ProviderSessionFactory providerSessionFactory;
|
||||
protected String contextPath;
|
||||
|
||||
public KeycloakApplication(@Context ServletContext context, @Context Dispatcher dispatcher) {
|
||||
loadConfig();
|
||||
|
||||
this.providerSessionFactory = createProviderSessionFactory();
|
||||
|
||||
dispatcher.getDefaultContextObjects().put(KeycloakApplication.class, this);
|
||||
this.contextPath = context.getContextPath();
|
||||
this.factory = createSessionFactory();
|
||||
BruteForceProtector protector = new BruteForceProtector(factory);
|
||||
BruteForceProtector protector = new BruteForceProtector(providerSessionFactory);
|
||||
dispatcher.getDefaultContextObjects().put(BruteForceProtector.class, protector);
|
||||
ResteasyProviderFactory.pushContext(BruteForceProtector.class, protector); // for injection
|
||||
protector.start();
|
||||
context.setAttribute(BruteForceProtector.class.getName(), protector);
|
||||
this.providerSessionFactory = createProviderSessionFactory();
|
||||
context.setAttribute(KeycloakSessionFactory.class.getName(), factory);
|
||||
|
||||
context.setAttribute(ProviderSessionFactory.class.getName(), this.providerSessionFactory);
|
||||
|
||||
TokenManager tokenManager = new TokenManager();
|
||||
@@ -95,7 +86,7 @@ public class KeycloakApplication extends Application {
|
||||
|
||||
setupDefaultRealm(context.getContextPath());
|
||||
|
||||
setupScheduledTasks(providerSessionFactory, factory);
|
||||
setupScheduledTasks(providerSessionFactory);
|
||||
importRealms(context);
|
||||
|
||||
checkExportImportProvider();
|
||||
@@ -115,55 +106,38 @@ public class KeycloakApplication extends Application {
|
||||
return uriInfo.getBaseUriBuilder().replacePath(getContextPath()).build();
|
||||
}
|
||||
|
||||
protected void setupDefaultRealm(String contextPath) {
|
||||
new ApplianceBootstrap().bootstrap(factory, contextPath);
|
||||
protected void loadConfig() {
|
||||
try {
|
||||
URL config = Thread.currentThread().getContextClassLoader().getResource("META-INF/keycloak-server.json");
|
||||
|
||||
if (config != null) {
|
||||
JsonNode node = new ObjectMapper().readTree(config);
|
||||
Config.init(new JsonConfigProvider(node));
|
||||
|
||||
log.info("Loaded config from " + config);
|
||||
return;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException("Failed to load config", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static KeycloakSessionFactory createSessionFactory() {
|
||||
ModelProvider provider = ModelProviderUtils.getConfiguredModelProvider();
|
||||
|
||||
if (provider != null) {
|
||||
log.debug("Model provider: " + provider.getId());
|
||||
return provider.createFactory();
|
||||
}
|
||||
|
||||
throw new RuntimeException("Model provider not found");
|
||||
protected void setupDefaultRealm(String contextPath) {
|
||||
new ApplianceBootstrap().bootstrap(providerSessionFactory, contextPath);
|
||||
}
|
||||
|
||||
public static DefaultProviderSessionFactory createProviderSessionFactory() {
|
||||
DefaultProviderSessionFactory factory = new DefaultProviderSessionFactory();
|
||||
|
||||
factory.registerLoader(AuditProvider.class, ProviderFactoryLoader.create(AuditProviderFactory.class), Config.getAuditProvider());
|
||||
factory.registerLoader(AuditListener.class, ProviderFactoryLoader.create(AuditListenerFactory.class));
|
||||
factory.registerLoader(TimerProvider.class, ProviderFactoryLoader.create(TimerProviderFactory.class), Config.getTimerProvider());
|
||||
try {
|
||||
Class identityManagerProvider = Class.forName("org.keycloak.picketlink.IdentityManagerProvider");
|
||||
Class identityManagerProviderFactory = Class.forName("org.keycloak.picketlink.IdentityManagerProviderFactory");
|
||||
factory.registerLoader(identityManagerProvider, ProviderFactoryLoader.create(identityManagerProviderFactory), Config.getIdentityManagerProvider());
|
||||
} catch (ClassNotFoundException e) {
|
||||
log.warn("Picketlink libraries not installed for IdentityManagerProviderFactory");
|
||||
}
|
||||
|
||||
factory.registerLoader(AuthenticationProvider.class, ProviderFactoryLoader.create(AuthenticationProviderFactory.class));
|
||||
factory.init();
|
||||
|
||||
return factory;
|
||||
}
|
||||
|
||||
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory, final KeycloakSessionFactory keycloakSessionFactory) {
|
||||
ProviderFactory<TimerProvider> timerFactory = providerSessionFactory.getProviderFactory(TimerProvider.class);
|
||||
if (timerFactory == null) {
|
||||
log.error("Can't setup schedule tasks, no timer provider found");
|
||||
return;
|
||||
}
|
||||
TimerProvider timer = timerFactory.create(null);
|
||||
timer.schedule(new ScheduledTaskRunner(keycloakSessionFactory, providerSessionFactory, new ClearExpiredAuditEvents()), Config.getAuditExpirationSchedule());
|
||||
timer.schedule(new ScheduledTaskRunner(keycloakSessionFactory, providerSessionFactory, new ClearExpiredUserSessions()), Config.getUserExpirationSchedule());
|
||||
}
|
||||
public static void setupScheduledTasks(final ProviderSessionFactory providerSessionFactory) {
|
||||
long interval = Config.scope("scheduled").getLong("interval") * 1000;
|
||||
|
||||
public KeycloakSessionFactory getFactory() {
|
||||
return factory;
|
||||
TimerProvider timer = providerSessionFactory.createSession().getProvider(TimerProvider.class);
|
||||
timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredAuditEvents()), interval);
|
||||
timer.schedule(new ScheduledTaskRunner(providerSessionFactory, new ClearExpiredUserSessions()), interval);
|
||||
}
|
||||
|
||||
public ProviderSessionFactory getProviderSessionFactory() {
|
||||
@@ -215,7 +189,8 @@ public class KeycloakApplication extends Application {
|
||||
}
|
||||
|
||||
public void importRealm(RealmRepresentation rep, String from) {
|
||||
KeycloakSession session = factory.createSession();
|
||||
ProviderSession providerSession = providerSessionFactory.createSession();
|
||||
KeycloakSession session = providerSession.getProvider(KeycloakSession.class);
|
||||
try {
|
||||
session.getTransaction().begin();
|
||||
RealmManager manager = new RealmManager(session);
|
||||
@@ -238,7 +213,7 @@ public class KeycloakApplication extends Application {
|
||||
|
||||
session.getTransaction().commit();
|
||||
} finally {
|
||||
session.close();
|
||||
providerSession.close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +230,7 @@ public class KeycloakApplication extends Application {
|
||||
|
||||
if (providers.hasNext()) {
|
||||
ExportImportProvider exportImport = providers.next();
|
||||
exportImport.checkExportImport(factory);
|
||||
exportImport.checkExportImport(providerSessionFactory);
|
||||
} else {
|
||||
log.warn("No ExportImportProvider found!");
|
||||
}
|
||||
|
||||
@@ -125,8 +125,7 @@ public class TokenService {
|
||||
}
|
||||
|
||||
public static UriBuilder tokenServiceBaseUrl(UriBuilder baseUriBuilder) {
|
||||
UriBuilder base = baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getTokenService");
|
||||
return base;
|
||||
return baseUriBuilder.path(RealmsResource.class).path(RealmsResource.class, "getTokenService");
|
||||
}
|
||||
|
||||
public static UriBuilder accessCodeToTokenUrl(UriInfo uriInfo) {
|
||||
@@ -295,7 +294,7 @@ public class TokenService {
|
||||
|
||||
ClientModel client = authorizeClient(authorizationHeader, form, audit);
|
||||
String refreshToken = form.getFirst(OAuth2Constants.REFRESH_TOKEN);
|
||||
AccessToken accessToken = null;
|
||||
AccessToken accessToken;
|
||||
try {
|
||||
accessToken = tokenManager.refreshAccessToken(uriInfo, realm, client, refreshToken, audit);
|
||||
} catch (OAuthErrorException e) {
|
||||
@@ -314,7 +313,7 @@ public class TokenService {
|
||||
|
||||
audit.success();
|
||||
|
||||
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").build();
|
||||
return Cors.add(request, Response.ok(res, MediaType.APPLICATION_JSON_TYPE)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
|
||||
}
|
||||
|
||||
@Path("auth/request/login")
|
||||
@@ -502,7 +501,7 @@ public class TokenService {
|
||||
credentials.setValue(formData.getFirst("password"));
|
||||
|
||||
boolean passwordUpdateSuccessful;
|
||||
String passwordUpdateError = null;
|
||||
String passwordUpdateError;
|
||||
try {
|
||||
passwordUpdateSuccessful = AuthenticationProviderManager.getManager(realm, providerSession).updatePassword(user, formData.getFirst("password"));
|
||||
passwordUpdateError = "Password update failed";
|
||||
@@ -658,12 +657,12 @@ public class TokenService {
|
||||
|
||||
audit.success();
|
||||
|
||||
return Cors.add(request, Response.ok(res)).auth().allowedOrigins(client).allowedMethods("POST").build();
|
||||
return Cors.add(request, Response.ok(res)).auth().allowedOrigins(client).allowedMethods("POST").exposedHeaders(Cors.ACCESS_CONTROL_ALLOW_METHODS).build();
|
||||
}
|
||||
|
||||
protected ClientModel authorizeClient(String authorizationHeader, MultivaluedMap<String, String> formData, Audit audit) {
|
||||
String client_id = null;
|
||||
String clientSecret = null;
|
||||
String client_id;
|
||||
String clientSecret;
|
||||
if (authorizationHeader != null) {
|
||||
String[] usernameSecret = BasicAuthHelper.parseHeader(authorizationHeader);
|
||||
if (usernameSecret == null) {
|
||||
@@ -1008,11 +1007,7 @@ public class TokenService {
|
||||
}
|
||||
|
||||
private boolean checkSsl() {
|
||||
if (realm.isSslNotRequired()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return uriInfo.getBaseUri().getScheme().equals("https");
|
||||
return realm.isSslNotRequired() || uriInfo.getBaseUri().getScheme().equals("https");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -10,14 +10,12 @@ import org.keycloak.freemarker.Theme;
|
||||
import org.keycloak.freemarker.ThemeLoader;
|
||||
import org.keycloak.models.AdminRoles;
|
||||
import org.keycloak.models.ApplicationModel;
|
||||
import org.keycloak.models.Config;
|
||||
import org.keycloak.models.Constants;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.RoleModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.provider.ProviderSession;
|
||||
import org.keycloak.services.ForbiddenException;
|
||||
import org.keycloak.services.managers.AppAuthManager;
|
||||
import org.keycloak.services.managers.ApplicationManager;
|
||||
import org.keycloak.services.managers.RealmManager;
|
||||
@@ -280,12 +278,7 @@ public class AdminConsole {
|
||||
|
||||
try {
|
||||
//logger.info("getting resource: " + path + " uri: " + uriInfo.getRequestUri().toString());
|
||||
String themeName = realm.getAdminTheme();
|
||||
if (themeName == null || themeName.trim().equals("")) {
|
||||
themeName = Config.getThemeAdmin();
|
||||
}
|
||||
|
||||
Theme theme = ThemeLoader.createTheme(themeName, Theme.Type.ADMIN);
|
||||
Theme theme = ThemeLoader.createTheme(realm.getAdminTheme(), Theme.Type.ADMIN);
|
||||
InputStream resource = theme.getResourceAsStream(path);
|
||||
if (resource != null) {
|
||||
String contentType = mimeTypes.getContentType(path);
|
||||
|
||||
@@ -13,20 +13,18 @@ public class ScheduledTaskRunner implements Runnable {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(ScheduledTaskRunner.class);
|
||||
|
||||
private final KeycloakSessionFactory keycloakSessionFactory;
|
||||
private final ProviderSessionFactory providerSessionFactory;
|
||||
private final ScheduledTask task;
|
||||
|
||||
public ScheduledTaskRunner(KeycloakSessionFactory keycloakSessionFactory, ProviderSessionFactory providerSessionFactory, ScheduledTask task) {
|
||||
this.keycloakSessionFactory = keycloakSessionFactory;
|
||||
public ScheduledTaskRunner(ProviderSessionFactory providerSessionFactory, ScheduledTask task) {
|
||||
this.providerSessionFactory = providerSessionFactory;
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
KeycloakSession keycloakSession = keycloakSessionFactory.createSession();
|
||||
ProviderSession providerSession = providerSessionFactory.createSession();
|
||||
KeycloakSession keycloakSession = providerSession.getProvider(KeycloakSession.class);
|
||||
try {
|
||||
keycloakSession.getTransaction().begin();
|
||||
task.run(keycloakSession, providerSession);
|
||||
@@ -38,11 +36,6 @@ public class ScheduledTaskRunner implements Runnable {
|
||||
|
||||
keycloakSession.getTransaction().rollback();
|
||||
} finally {
|
||||
try {
|
||||
keycloakSession.close();
|
||||
} catch (Throwable t) {
|
||||
logger.error("Failed to close KeycloakSession", t);
|
||||
}
|
||||
try {
|
||||
providerSession.close();
|
||||
} catch (Throwable t) {
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user