From 5533357c467207c3b8ec4a08c299ea5932515d1c Mon Sep 17 00:00:00 2001 From: Jeroen Rosenberg Date: Fri, 16 May 2014 14:09:50 +0200 Subject: [PATCH 1/5] Added HEAD as default allowed method for Cors --- .../src/main/java/org/keycloak/services/resources/Cors.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index a391ab36fe..42a4e7e4d9 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -16,7 +16,7 @@ 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 ORIGIN = "Origin"; From 4c281a39bb039e4e862ae615f4b6721944a9410d Mon Sep 17 00:00:00 2001 From: Jeroen Rosenberg Date: Fri, 16 May 2014 14:10:13 +0200 Subject: [PATCH 2/5] Unused import --- services/src/main/java/org/keycloak/services/resources/Cors.java | 1 - 1 file changed, 1 deletion(-) diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index 42a4e7e4d9..6b445213c1 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -8,7 +8,6 @@ import javax.ws.rs.core.Response.ResponseBuilder; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.models.ClientModel; -import org.keycloak.models.UserModel; /** * @author Stian Thorgersen From cedf43c0847e530b41c2d0d6e64a0c559ffa9f91 Mon Sep 17 00:00:00 2001 From: Jeroen Rosenberg Date: Fri, 16 May 2014 14:28:49 +0200 Subject: [PATCH 3/5] Refactored joining of allowed headers to be more efficient --- .../org/keycloak/util/CollectionUtil.java | 26 +++++++++++++++++++ .../org/keycloak/services/resources/Cors.java | 24 ++++++++--------- 2 files changed, 37 insertions(+), 13 deletions(-) create mode 100644 core/src/main/java/org/keycloak/util/CollectionUtil.java diff --git a/core/src/main/java/org/keycloak/util/CollectionUtil.java b/core/src/main/java/org/keycloak/util/CollectionUtil.java new file mode 100644 index 0000000000..41df40e698 --- /dev/null +++ b/core/src/main/java/org/keycloak/util/CollectionUtil.java @@ -0,0 +1,26 @@ +package org.keycloak.util; + +import java.util.Collection; +import java.util.Iterator; + +/** + * @author Jeroen Rosenberg + */ +public class CollectionUtil { + + public static String join(Collection strings) { + return join(strings, ", "); + } + + public static String join(Collection strings, String separator) { + Iterator iter = strings.iterator(); + StringBuilder sb = new StringBuilder(); + if(iter.hasNext()){ + sb.append(iter.next()); + while(iter.hasNext()){ + sb.append(separator).append(iter.next()); + } + } + return sb.toString(); + } +} diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index 6b445213c1..ade3df29f8 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -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,6 +10,7 @@ import javax.ws.rs.core.Response.ResponseBuilder; import org.jboss.resteasy.spi.HttpRequest; import org.keycloak.models.ClientModel; +import org.keycloak.util.CollectionUtil; /** * @author Stian Thorgersen @@ -17,7 +20,8 @@ public class Cors { public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1); public static final String DEFAULT_ALLOW_METHODS = "GET, HEAD, OPTIONS"; - 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"; @@ -25,10 +29,11 @@ public class Cors { 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 allowedOrigins; - private String[] allowedMethods; + private Set allowedMethods; private boolean preflight; private boolean auth; @@ -60,12 +65,12 @@ public class Cors { } public Cors allowedMethods(String... allowedMethods) { - this.allowedMethods = allowedMethods; + this.allowedMethods = new HashSet(Arrays.asList(allowedMethods)); 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(); } @@ -77,21 +82,14 @@ 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); } response.header(ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.toString(auth)); if (auth) { - response.header(ACCESS_CONTROL_ALLOW_HEADERS, "Authorization"); + response.header(ACCESS_CONTROL_ALLOW_HEADERS, AUTHORIZATION_HEADER); } response.header(ACCESS_CONTROL_MAX_AGE, DEFAULT_MAX_AGE); From 84f13eadc181c81ec35e7d50a8d6748adddccb6b Mon Sep 17 00:00:00 2001 From: Jeroen Rosenberg Date: Fri, 16 May 2014 16:39:17 +0200 Subject: [PATCH 4/5] * Possibility to add exposed headers * Default allow headers * Fix warnings, unused import, redundancies --- .../org/keycloak/services/resources/Cors.java | 16 +++++++++++++- .../services/resources/TokenService.java | 22 +++++++------------ 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/services/src/main/java/org/keycloak/services/resources/Cors.java b/services/src/main/java/org/keycloak/services/resources/Cors.java index ade3df29f8..9dbcd39188 100755 --- a/services/src/main/java/org/keycloak/services/resources/Cors.java +++ b/services/src/main/java/org/keycloak/services/resources/Cors.java @@ -19,6 +19,7 @@ public class Cors { public static final long DEFAULT_MAX_AGE = TimeUnit.HOURS.toSeconds(1); 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_HEADER = "Origin"; public static final String AUTHORIZATION_HEADER = "Authorization"; @@ -26,6 +27,7 @@ public class Cors { 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"; @@ -34,6 +36,7 @@ public class Cors { private ResponseBuilder response; private Set allowedOrigins; private Set allowedMethods; + private Set exposedHeaders; private boolean preflight; private boolean auth; @@ -69,6 +72,11 @@ public class Cors { return this; } + public Cors exposedHeaders(String... exposedHeaders) { + this.exposedHeaders = new HashSet(Arrays.asList(exposedHeaders)); + return this; + } + public Response build() { String origin = request.getHttpHeaders().getRequestHeaders().getFirst(ORIGIN_HEADER); if (origin == null) { @@ -87,9 +95,15 @@ public class Cors { 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_HEADER); + 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); diff --git a/services/src/main/java/org/keycloak/services/resources/TokenService.java b/services/src/main/java/org/keycloak/services/resources/TokenService.java index a262c83ea2..1aa7bafe84 100755 --- a/services/src/main/java/org/keycloak/services/resources/TokenService.java +++ b/services/src/main/java/org/keycloak/services/resources/TokenService.java @@ -58,7 +58,6 @@ import javax.ws.rs.core.Context; import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.MultivaluedMap; -import javax.ws.rs.core.NewCookie; import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; import javax.ws.rs.core.UriBuilder; @@ -126,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 +293,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(realm, client, refreshToken, audit); } catch (OAuthErrorException e) { @@ -313,7 +311,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") @@ -501,7 +499,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"; @@ -654,12 +652,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 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) { @@ -1011,11 +1009,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"); } } From 855269f35d409b8dd7355f9c9f5a5165c5e8ad1c Mon Sep 17 00:00:00 2001 From: Stian Thorgersen Date: Thu, 15 May 2014 17:01:19 +0100 Subject: [PATCH 5/5] 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 --- .../main/java/org/keycloak/audit/Audit.java | 2 - .../org/keycloak/audit/AuditListenerSpi.java | 27 ++ .../org/keycloak/audit/AuditProviderSpi.java | 27 ++ .../services/org.keycloak.provider.Spi | 2 + .../log/JBossLoggingAuditListenerFactory.java | 9 +- .../audit/jpa/JpaAuditProviderFactory.java | 9 +- .../mongo/MongoAuditProviderFactory.java | 33 ++- .../tests/AbstractAuditProviderTest.java | 14 +- .../authentication/AuthenticationSpi.java | 27 ++ .../services/org.keycloak.provider.Spi | 1 + ...nalModelAuthenticationProviderFactory.java | 8 +- .../ModelAuthenticationProviderFactory.java | 7 +- .../PicketlinkAuthenticationProvider.java | 15 +- ...cketlinkAuthenticationProviderFactory.java | 7 +- core/src/main/java/org/keycloak/Config.java | 129 +++++++++ .../keycloak/provider/ProviderFactory.java | 6 +- .../provider/ProviderFactoryLoader.java | 102 ------- .../provider/ProviderSessionFactory.java | 8 - .../main/java/org/keycloak/provider/Spi.java | 12 + .../keycloak/util/StringPropertyReplacer.java | 263 ++++++++++++++++++ .../en/en-US/modules/server-installation.xml | 8 +- export-import/export-import-api/pom.xml | 6 + .../exportimport/ExportImportProvider.java | 5 +- export-import/export-import-impl/pom.xml | 31 +-- .../exportimport/ExportImportConfig.java | 54 ++++ .../ExportImportProviderImpl.java | 15 +- .../keycloak/exportimport/ModelImporter.java | 2 +- .../TmpDirExportImportIOProvider.java | 10 +- .../io/zip/EncryptedZIPIOProvider.java | 10 +- .../exportimport/ExportImportTestBase.java | 34 ++- .../JPAToMongoExportImportTest.java | 15 +- .../MongoToJPAExportImportTest.java | 29 +- .../org/keycloak/freemarker/ThemeLoader.java | 4 +- .../theme/DefaultKeycloakThemeProvider.java | 4 - .../keycloak/theme/FolderThemeProvider.java | 4 +- .../main/java/org/keycloak/models/Config.java | 183 ------------ .../org/keycloak/models/KeycloakSession.java | 4 +- .../models/KeycloakSessionFactory.java | 7 +- .../java/org/keycloak/models/ModelSpi.java | 27 ++ .../models/utils/ModelProviderUtils.java | 49 ---- .../services/org.keycloak.provider.Spi | 1 + model/jpa/pom.xml | 18 -- .../models/jpa/JpaKeycloakSessionFactory.java | 37 ++- .../keycloak/models/jpa/JpaModelProvider.java | 41 --- ...org.keycloak.models.KeycloakSessionFactory | 1 + .../org.keycloak.models.ModelProvider | 1 - model/mongo/pom.xml | 30 +- .../mongo/keycloak/MongoModelProvider.java | 27 -- .../adapters/MongoKeycloakSessionFactory.java | 55 +++- .../keycloak/config/MongoClientProvider.java | 21 -- .../config/MongoClientProviderHolder.java | 20 -- .../SystemPropertiesMongoClientProvider.java | 104 ------- ...org.keycloak.models.KeycloakSessionFactory | 1 + .../org.keycloak.models.ModelProvider | 1 - .../keycloak/models/mongo/test/Address.java | 31 --- .../models/mongo/test/AddressWithFlats.java | 23 -- .../models/mongo/test/MongoStoreTest.java | 140 ---------- .../keycloak/models/mongo/test/Person.java | 114 -------- .../model/test/AbstractModelTest.java | 29 +- .../model/test/AuthenticationManagerTest.java | 2 +- .../picketlink/IdentityManagerSpi.java | 25 ++ .../services/org.keycloak.provider.Spi | 1 + .../RealmIdentityManagerProviderFactory.java | 7 +- .../ups/security/UpsSecurityApplication.java | 6 +- server/pom.xml | 33 +++ .../resources/META-INF/keycloak-server.json | 26 ++ .../main/resources/META-INF/persistence.xml | 2 + services/pom.xml | 1 + .../services/DefaultProviderSession.java | 22 +- .../DefaultProviderSessionFactory.java | 124 +++++---- .../filters/KeycloakSessionServletFilter.java | 5 +- .../services/managers/ApplianceBootstrap.java | 18 +- .../managers/BruteForceProtector.java | 11 +- .../services/managers/RealmManager.java | 2 +- .../resources/KeycloakApplication.java | 103 +++---- .../resources/admin/AdminConsole.java | 9 +- .../scheduled/ScheduledTaskRunner.java | 11 +- .../services/util/JsonConfigProvider.java | 129 +++++++++ .../org/keycloak/social/SocialLoader.java | 2 - .../keycloak/social/utils/SimpleHttpTest.java | 3 +- testsuite/integration/README.md | 6 +- testsuite/integration/pom.xml | 27 +- .../keycloak/testutils/KeycloakServer.java | 27 +- .../resources/META-INF/keycloak-server.json | 26 ++ .../org/keycloak/testsuite/AssertEvents.java | 9 +- .../testsuite/account/AccountTest.java | 4 - .../testsuite/adapter/AdapterTest.java | 25 +- .../testsuite/oauth/RefreshTokenTest.java | 28 +- .../testsuite/rule/AbstractKeycloakRule.java | 31 ++- .../keycloak/testsuite/rule/KeycloakRule.java | 11 +- .../BaseJMeterPerformanceTest.java | 22 +- .../src/test/jmeter/system.properties | 8 +- .../org/keycloak/timer/TimerProvider.java | 2 +- .../java/org/keycloak/timer/TimerSpi.java | 25 ++ .../services/org.keycloak.provider.Spi | 1 + .../timer/basic/BasicTimerProvider.java | 4 +- .../basic/BasicTimerProviderFactory.java | 7 +- 97 files changed, 1354 insertions(+), 1355 deletions(-) create mode 100644 audit/api/src/main/java/org/keycloak/audit/AuditListenerSpi.java create mode 100644 audit/api/src/main/java/org/keycloak/audit/AuditProviderSpi.java create mode 100644 audit/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi create mode 100644 authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationSpi.java create mode 100644 authentication/authentication-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi create mode 100644 core/src/main/java/org/keycloak/Config.java delete mode 100644 core/src/main/java/org/keycloak/provider/ProviderFactoryLoader.java create mode 100644 core/src/main/java/org/keycloak/provider/Spi.java create mode 100644 core/src/main/java/org/keycloak/util/StringPropertyReplacer.java create mode 100644 export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java delete mode 100644 model/api/src/main/java/org/keycloak/models/Config.java create mode 100644 model/api/src/main/java/org/keycloak/models/ModelSpi.java delete mode 100644 model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java create mode 100644 model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi delete mode 100755 model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java create mode 100644 model/jpa/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory delete mode 100644 model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider delete mode 100755 model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java delete mode 100644 model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProvider.java delete mode 100644 model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProviderHolder.java delete mode 100755 model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/SystemPropertiesMongoClientProvider.java create mode 100644 model/mongo/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory delete mode 100644 model/mongo/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider delete mode 100755 model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java delete mode 100644 model/mongo/src/test/java/org/keycloak/models/mongo/test/AddressWithFlats.java delete mode 100755 model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoStoreTest.java delete mode 100755 model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java create mode 100644 picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/IdentityManagerSpi.java create mode 100644 picketlink/keycloak-picketlink-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi create mode 100644 server/src/main/resources/META-INF/keycloak-server.json create mode 100644 services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java create mode 100644 testsuite/integration/src/main/resources/META-INF/keycloak-server.json create mode 100644 timer/api/src/main/java/org/keycloak/timer/TimerSpi.java create mode 100644 timer/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi diff --git a/audit/api/src/main/java/org/keycloak/audit/Audit.java b/audit/api/src/main/java/org/keycloak/audit/Audit.java index 1ccdca830a..625074f733 100644 --- a/audit/api/src/main/java/org/keycloak/audit/Audit.java +++ b/audit/api/src/main/java/org/keycloak/audit/Audit.java @@ -5,10 +5,8 @@ import org.keycloak.models.ClientModel; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; -import org.keycloak.provider.ProviderFactoryLoader; import java.util.HashMap; -import java.util.LinkedList; import java.util.List; /** diff --git a/audit/api/src/main/java/org/keycloak/audit/AuditListenerSpi.java b/audit/api/src/main/java/org/keycloak/audit/AuditListenerSpi.java new file mode 100644 index 0000000000..e2f0b9cf46 --- /dev/null +++ b/audit/api/src/main/java/org/keycloak/audit/AuditListenerSpi.java @@ -0,0 +1,27 @@ +package org.keycloak.audit; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class AuditListenerSpi implements Spi { + + @Override + public String getName() { + return "audit-listener"; + } + + @Override + public Class getProviderClass() { + return AuditListener.class; + } + + @Override + public Class getProviderFactoryClass() { + return AuditListenerFactory.class; + } + +} diff --git a/audit/api/src/main/java/org/keycloak/audit/AuditProviderSpi.java b/audit/api/src/main/java/org/keycloak/audit/AuditProviderSpi.java new file mode 100644 index 0000000000..249f7eab01 --- /dev/null +++ b/audit/api/src/main/java/org/keycloak/audit/AuditProviderSpi.java @@ -0,0 +1,27 @@ +package org.keycloak.audit; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class AuditProviderSpi implements Spi { + + @Override + public String getName() { + return "audit"; + } + + @Override + public Class getProviderClass() { + return AuditProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return AuditProviderFactory.class; + } + +} diff --git a/audit/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/audit/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..2a1aeb8ea4 --- /dev/null +++ b/audit/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1,2 @@ +org.keycloak.audit.AuditProviderSpi +org.keycloak.audit.AuditListenerSpi \ No newline at end of file diff --git a/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListenerFactory.java b/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListenerFactory.java index 1f936afce4..1bafa9f3af 100644 --- a/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListenerFactory.java +++ b/audit/jboss-logging/src/main/java/org/keycloak/audit/log/JBossLoggingAuditListenerFactory.java @@ -1,10 +1,10 @@ package org.keycloak.audit.log; import org.jboss.logging.Logger; +import org.keycloak.Config; import org.keycloak.audit.AuditListener; import org.keycloak.audit.AuditListenerFactory; import org.keycloak.provider.ProviderSession; -import org.keycloak.provider.ProviderSessionFactory; /** * @author Stian Thorgersen @@ -21,7 +21,7 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory { } @Override - public void init() { + public void init(Config.Scope config) { } @Override @@ -33,9 +33,4 @@ public class JBossLoggingAuditListenerFactory implements AuditListenerFactory { return ID; } - @Override - public boolean lazyLoad() { - return false; - } - } diff --git a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java index c7d7a1a96f..01f72e26fb 100644 --- a/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java +++ b/audit/jpa/src/main/java/org/keycloak/audit/jpa/JpaAuditProviderFactory.java @@ -1,9 +1,9 @@ package org.keycloak.audit.jpa; +import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; import org.keycloak.provider.ProviderSession; -import org.keycloak.provider.ProviderSessionFactory; import javax.persistence.EntityManagerFactory; import javax.persistence.Persistence; @@ -22,7 +22,7 @@ public class JpaAuditProviderFactory implements AuditProviderFactory { } @Override - public void init() { + public void init(Config.Scope config) { emf = Persistence.createEntityManagerFactory("jpa-keycloak-audit-store"); } @@ -36,9 +36,4 @@ public class JpaAuditProviderFactory implements AuditProviderFactory { return ID; } - @Override - public boolean lazyLoad() { - return true; - } - } diff --git a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java index 8f78fb4371..4ffcda1b16 100644 --- a/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java +++ b/audit/mongo/src/main/java/org/keycloak/audit/mongo/MongoAuditProviderFactory.java @@ -2,13 +2,16 @@ package org.keycloak.audit.mongo; import com.mongodb.DB; import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; import com.mongodb.WriteConcern; +import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; import org.keycloak.provider.ProviderSession; -import org.keycloak.provider.ProviderSessionFactory; import java.net.UnknownHostException; +import java.util.Collections; /** * @author Stian Thorgersen @@ -29,11 +32,28 @@ public class MongoAuditProviderFactory implements AuditProviderFactory { } @Override - public void init() { + public void init(Config.Scope config) { try { - client = new MongoClient(System.getProperty(MONGO_HOST, "localhost"), Integer.parseInt(System.getProperty(MONGO_PORT, "27017"))); + String host = config.get("host", ServerAddress.defaultHost()); + int port = config.getInt("port", ServerAddress.defaultPort()); + String dbName = config.get("db", "keycloak-audit"); + boolean clearOnStartup = config.getBoolean("clearOnStartup", false); + + String user = config.get("user"); + String password = config.get("password"); + if (user != null && password != null) { + MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray()); + client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential)); + } else { + client = new MongoClient(host, port); + } + client.setWriteConcern(WriteConcern.UNACKNOWLEDGED); - db = client.getDB(System.getProperty(MONGO_DB_NAME, "keycloak-audit")); + db = client.getDB(dbName); + + if (clearOnStartup) { + db.getCollection("audit").drop(); + } } catch (UnknownHostException e) { throw new RuntimeException(e); } @@ -49,9 +69,4 @@ public class MongoAuditProviderFactory implements AuditProviderFactory { return ID; } - @Override - public boolean lazyLoad() { - return true; - } - } diff --git a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java index 3de7473a82..b24ee37a09 100644 --- a/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java +++ b/audit/tests/src/main/java/org/keycloak/audit/tests/AbstractAuditProviderTest.java @@ -4,14 +4,15 @@ import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; +import org.keycloak.Config; import org.keycloak.audit.AuditProvider; import org.keycloak.audit.AuditProviderFactory; import org.keycloak.audit.Event; import org.keycloak.provider.ProviderFactory; -import org.keycloak.provider.ProviderFactoryLoader; import java.util.HashMap; import java.util.Map; +import java.util.ServiceLoader; /** * @author Stian Thorgersen @@ -23,9 +24,14 @@ public abstract class AbstractAuditProviderTest { @Before public void before() { - ProviderFactoryLoader loader = ProviderFactoryLoader.create(AuditProviderFactory.class); - factory = loader.find(getProviderId()); - factory.init(); + String providerId = getProviderId(); + ServiceLoader factories = ServiceLoader.load(AuditProviderFactory.class); + for (AuditProviderFactory f : factories) { + if (f.getId().equals(providerId)) { + factory = f; + factory.init(Config.scope("audit", providerId)); + } + } provider = factory.create(null); } diff --git a/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationSpi.java b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationSpi.java new file mode 100644 index 0000000000..875b150ca9 --- /dev/null +++ b/authentication/authentication-api/src/main/java/org/keycloak/authentication/AuthenticationSpi.java @@ -0,0 +1,27 @@ +package org.keycloak.authentication; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class AuthenticationSpi implements Spi { + + @Override + public String getName() { + return "authentication"; + } + + @Override + public Class getProviderClass() { + return AuthenticationProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return AuthenticationProviderFactory.class; + } + +} diff --git a/authentication/authentication-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/authentication/authentication-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..9305401896 --- /dev/null +++ b/authentication/authentication-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1 @@ +org.keycloak.authentication.AuthenticationSpi \ No newline at end of file diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java index 3f58d4e33d..4a208c0928 100644 --- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java +++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ExternalModelAuthenticationProviderFactory.java @@ -1,10 +1,10 @@ package org.keycloak.authentication.model; +import org.keycloak.Config; import org.keycloak.authentication.AuthProviderConstants; import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProviderFactory; import org.keycloak.provider.ProviderSession; -import org.keycloak.provider.ProviderSessionFactory; /** * @author Marek Posolda @@ -17,7 +17,7 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio } @Override - public void init() { + public void init(Config.Scope config) { } @Override @@ -29,8 +29,4 @@ public class ExternalModelAuthenticationProviderFactory implements Authenticatio return AuthProviderConstants.PROVIDER_NAME_EXTERNAL_MODEL; } - @Override - public boolean lazyLoad() { - return false; - } } diff --git a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java index 8b9e6c1666..47ef6375e9 100644 --- a/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java +++ b/authentication/authentication-model/src/main/java/org/keycloak/authentication/model/ModelAuthenticationProviderFactory.java @@ -1,5 +1,6 @@ package org.keycloak.authentication.model; +import org.keycloak.Config; import org.keycloak.authentication.AuthProviderConstants; import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProviderFactory; @@ -16,7 +17,7 @@ public class ModelAuthenticationProviderFactory implements AuthenticationProvide } @Override - public void init() { + public void init(Config.Scope config) { } @Override @@ -28,8 +29,4 @@ public class ModelAuthenticationProviderFactory implements AuthenticationProvide return AuthProviderConstants.PROVIDER_NAME_MODEL; } - @Override - public boolean lazyLoad() { - return false; - } } diff --git a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java index 0027be6fdd..ce69bd3062 100755 --- a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java +++ b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProvider.java @@ -1,28 +1,25 @@ package org.keycloak.authentication.picketlink; -import java.util.Collections; -import java.util.List; -import java.util.Map; - import org.jboss.logging.Logger; -import org.jboss.resteasy.spi.ResteasyProviderFactory; -import org.keycloak.models.RealmModel; -import org.keycloak.authentication.AuthProviderStatus; import org.keycloak.authentication.AuthProviderConstants; +import org.keycloak.authentication.AuthProviderStatus; import org.keycloak.authentication.AuthUser; import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProviderException; +import org.keycloak.models.RealmModel; import org.keycloak.picketlink.IdentityManagerProvider; -import org.keycloak.util.ProviderLoader; import org.picketlink.idm.IdentityManagementException; import org.picketlink.idm.IdentityManager; -import org.picketlink.idm.PartitionManager; import org.picketlink.idm.credential.Credentials; import org.picketlink.idm.credential.Password; import org.picketlink.idm.credential.UsernamePasswordCredentials; import org.picketlink.idm.model.basic.BasicModel; import org.picketlink.idm.model.basic.User; +import java.util.Collections; +import java.util.List; +import java.util.Map; + /** * AuthenticationProvider, which delegates authentication to picketlink * diff --git a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProviderFactory.java b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProviderFactory.java index db1ece3e0f..29cdb6fd86 100644 --- a/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProviderFactory.java +++ b/authentication/authentication-picketlink/src/main/java/org/keycloak/authentication/picketlink/PicketlinkAuthenticationProviderFactory.java @@ -1,5 +1,6 @@ package org.keycloak.authentication.picketlink; +import org.keycloak.Config; import org.keycloak.authentication.AuthProviderConstants; import org.keycloak.authentication.AuthenticationProvider; import org.keycloak.authentication.AuthenticationProviderFactory; @@ -17,7 +18,7 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr } @Override - public void init() { + public void init(Config.Scope config) { } @Override @@ -29,8 +30,4 @@ public class PicketlinkAuthenticationProviderFactory implements AuthenticationPr return AuthProviderConstants.PROVIDER_NAME_PICKETLINK; } - @Override - public boolean lazyLoad() { - return false; - } } diff --git a/core/src/main/java/org/keycloak/Config.java b/core/src/main/java/org/keycloak/Config.java new file mode 100644 index 0000000000..34d0fa4518 --- /dev/null +++ b/core/src/main/java/org/keycloak/Config.java @@ -0,0 +1,129 @@ +package org.keycloak; + +/** + * @author Stian Thorgersen + */ +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 Stian Thorgersen + */ + 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); + + } +} diff --git a/core/src/main/java/org/keycloak/provider/ProviderFactory.java b/core/src/main/java/org/keycloak/provider/ProviderFactory.java index 996d2c5356..0e49732268 100644 --- a/core/src/main/java/org/keycloak/provider/ProviderFactory.java +++ b/core/src/main/java/org/keycloak/provider/ProviderFactory.java @@ -1,5 +1,7 @@ package org.keycloak.provider; +import org.keycloak.Config; + /** * @author Stian Thorgersen */ @@ -7,12 +9,10 @@ public interface ProviderFactory { public T create(ProviderSession providerSession); - public void init(); + public void init(Config.Scope config); public void close(); public String getId(); - public boolean lazyLoad(); - } diff --git a/core/src/main/java/org/keycloak/provider/ProviderFactoryLoader.java b/core/src/main/java/org/keycloak/provider/ProviderFactoryLoader.java deleted file mode 100644 index 8c3f62a262..0000000000 --- a/core/src/main/java/org/keycloak/provider/ProviderFactoryLoader.java +++ /dev/null @@ -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 Stian Thorgersen - */ -public class ProviderFactoryLoader implements Iterable> { - - private final Map> factories = new HashMap>(); - - private ProviderFactoryLoader(ServiceLoader 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 service) { - return new ProviderFactoryLoader(ServiceLoader.load(service)); - } - - public static ProviderFactoryLoader create(Class service, ClassLoader loader) { - return new ProviderFactoryLoader(ServiceLoader.load(service, loader)); - } - - public ProviderFactory find(String id) { - return factories.get(id); - } - - @Override - public Iterator> iterator() { - return factories.values().iterator(); - } - - public Set 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 implements ProviderFactory { - - private final ProviderFactory factory; - - private volatile boolean initialized = false; - - private LazyProviderFactory(ProviderFactory 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; - } - } - -} diff --git a/core/src/main/java/org/keycloak/provider/ProviderSessionFactory.java b/core/src/main/java/org/keycloak/provider/ProviderSessionFactory.java index 2e347dfc15..93536d0a29 100755 --- a/core/src/main/java/org/keycloak/provider/ProviderSessionFactory.java +++ b/core/src/main/java/org/keycloak/provider/ProviderSessionFactory.java @@ -1,7 +1,5 @@ package org.keycloak.provider; -import java.util.Set; - /** * @author Stian Thorgersen */ @@ -11,12 +9,6 @@ public interface ProviderSessionFactory { void close(); - ProviderFactory getProviderFactory(Class clazz); - - ProviderFactory getProviderFactory(Class clazz, String id); - - Set providerIds(Class clazz); - void init(); } diff --git a/core/src/main/java/org/keycloak/provider/Spi.java b/core/src/main/java/org/keycloak/provider/Spi.java new file mode 100644 index 0000000000..03b9afcc24 --- /dev/null +++ b/core/src/main/java/org/keycloak/provider/Spi.java @@ -0,0 +1,12 @@ +package org.keycloak.provider; + +/** + * @author Stian Thorgersen + */ +public interface Spi { + + public String getName(); + public Class getProviderClass(); + public Class getProviderFactoryClass(); + +} diff --git a/core/src/main/java/org/keycloak/util/StringPropertyReplacer.java b/core/src/main/java/org/keycloak/util/StringPropertyReplacer.java new file mode 100644 index 0000000000..d46f69900f --- /dev/null +++ b/core/src/main/java/org/keycloak/util/StringPropertyReplacer.java @@ -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 Jason Dillon + * @author Scott Stark + * @author Claudio Vesco + * @author Adrian Brock + * @author Dimitris Andreadis + * @version $Revision: 2898 $ + */ +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; + } +} \ No newline at end of file diff --git a/docbook/reference/en/en-US/modules/server-installation.xml b/docbook/reference/en/en-US/modules/server-installation.xml index 873fd6eb8f..70f14f4f23 100755 --- a/docbook/reference/en/en-US/modules/server-installation.xml +++ b/docbook/reference/en/en-US/modules/server-installation.xml @@ -252,12 +252,12 @@ keycloak-war-dist-all-1.0-beta-1-SNAPSHOT/ First you need to specify that you want to use mongo instead of default jpa model, and you may also specify host, port and name of mongo database. So you can start keycloak with the command like this: Note that when you install MongoDB on your laptop, it's usually on localhost/270717 by default. That's why properties - keycloak.mongo.host and keycloak.mongo.port are not mandatory, but they already have - default values localhost and 27017 . Similarly property keycloak.mongo.db + keycloak.model.mongo.host and keycloak.model.mongo.port are not mandatory, but they already have + default values localhost and 27017 . Similarly property keycloak.model.mongo.db has default value keycloak for name of underlying database. So the example above could be simplified like: + + org.keycloak + keycloak-core + ${project.version} + provided + org.keycloak keycloak-model-api diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java index 9ada46bfca..97983706af 100644 --- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java +++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/ExportImportProvider.java @@ -1,11 +1,12 @@ package org.keycloak.exportimport; -import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderSessionFactory; /** * @author Marek Posolda */ public interface ExportImportProvider { - void checkExportImport(KeycloakSessionFactory identitySessionFactory); + void checkExportImport(ProviderSessionFactory identitySessionFactory); + } diff --git a/export-import/export-import-impl/pom.xml b/export-import/export-import-impl/pom.xml index a4898e50a9..17a566ee7f 100644 --- a/export-import/export-import-impl/pom.xml +++ b/export-import/export-import-impl/pom.xml @@ -32,13 +32,6 @@ ${project.version} provided - - org.keycloak - keycloak-audit-api - ${project.version} - provided - - org.jboss.resteasy jaxrs-api @@ -121,6 +114,12 @@ ${project.version} test + + org.keycloak + keycloak-model-tests + ${project.version} + test + org.hibernate.javax.persistence hibernate-jpa-2.0-api @@ -149,10 +148,10 @@ - localhost - 27018 - keycloak - true + localhost + 27018 + keycloak + true @@ -179,10 +178,10 @@ - ${keycloak.mongo.host} - ${keycloak.mongo.port} - ${keycloak.mongo.db} - ${keycloak.mongo.clearOnStartup} + ${keycloak.model.mongo.host} + ${keycloak.model.mongo.port} + ${keycloak.model.mongo.db} + ${keycloak.model.mongo.clearOnStartup} @@ -207,7 +206,7 @@ start - ${keycloak.mongo.port} + ${keycloak.model.mongo.port} file ${project.build.directory}/mongodb.log diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java new file mode 100644 index 0000000000..d87b55cf32 --- /dev/null +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportConfig.java @@ -0,0 +1,54 @@ +package org.keycloak.exportimport; + +/** + * @author Stian Thorgersen + */ +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); + } + +} diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java index 32e0b84f74..de84633082 100644 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ExportImportProviderImpl.java @@ -4,10 +4,10 @@ import org.jboss.logging.Logger; import org.keycloak.exportimport.io.ExportImportIOProvider; import org.keycloak.exportimport.io.ExportWriter; import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.models.Config; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakTransaction; +import org.keycloak.provider.ProviderSession; +import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.util.ProviderLoader; /** @@ -21,8 +21,8 @@ public class ExportImportProviderImpl implements ExportImportProvider { public static final String ACTION_IMPORT = "import"; @Override - public void checkExportImport(KeycloakSessionFactory identitySessionFactory) { - String exportImportAction = Config.getExportImportAction(); + public void checkExportImport(ProviderSessionFactory providerSessionFactory) { + String exportImportAction = ExportImportConfig.getAction(); boolean export = false; boolean importt = false; @@ -35,7 +35,8 @@ public class ExportImportProviderImpl implements ExportImportProvider { } if (export || importt) { - KeycloakSession session = identitySessionFactory.createSession(); + ProviderSession providerSession = providerSessionFactory.createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); KeycloakTransaction transaction = session.getTransaction(); try { transaction.begin(); @@ -63,13 +64,13 @@ public class ExportImportProviderImpl implements ExportImportProvider { } throw new RuntimeException(e); } finally { - session.close(); + providerSession.close(); } } } private ExportImportIOProvider getProvider() { - String providerId = Config.getExportImportProvider(); + String providerId = ExportImportConfig.getProvider(); logger.infof("Requested migration provider: " + providerId); Iterable providers = ProviderLoader.load(ExportImportIOProvider.class); diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java index 3bfc9aa788..15f9fae027 100755 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/ModelImporter.java @@ -13,7 +13,7 @@ import org.keycloak.models.ApplicationModel; import org.keycloak.models.AuthenticationLinkModel; import org.keycloak.models.AuthenticationProviderModel; import org.keycloak.models.ClientModel; -import org.keycloak.models.Config; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.OAuthClientModel; import org.keycloak.models.PasswordPolicy; diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java index 962c3acef8..b785c4174a 100644 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/directory/TmpDirExportImportIOProvider.java @@ -1,11 +1,11 @@ package org.keycloak.exportimport.io.directory; -import java.io.File; - +import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.io.ExportImportIOProvider; import org.keycloak.exportimport.io.ExportWriter; import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.models.Config; + +import java.io.File; /** * Export/import into JSON files inside "tmp" directory. This implementation is used mainly for testing @@ -19,13 +19,13 @@ public class TmpDirExportImportIOProvider implements ExportImportIOProvider { @Override public ExportWriter getExportWriter() { - String dir = Config.getExportImportDir(); + String dir = ExportImportConfig.getDir(); return dir!=null ? new TmpDirExportWriter(new File(dir)) : new TmpDirExportWriter(); } @Override public ImportReader getImportReader() { - String dir = Config.getExportImportDir(); + String dir = ExportImportConfig.getDir(); return dir!=null ? new TmpDirImportReader(new File(dir)) : new TmpDirImportReader(); } diff --git a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java index 837157e5e6..31883acb14 100644 --- a/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java +++ b/export-import/export-import-impl/src/main/java/org/keycloak/exportimport/io/zip/EncryptedZIPIOProvider.java @@ -1,10 +1,10 @@ package org.keycloak.exportimport.io.zip; import org.jboss.logging.Logger; +import org.keycloak.exportimport.ExportImportConfig; import org.keycloak.exportimport.io.ExportImportIOProvider; import org.keycloak.exportimport.io.ExportWriter; import org.keycloak.exportimport.io.ImportReader; -import org.keycloak.models.Config; /** * @author Marek Posolda @@ -22,8 +22,8 @@ public class EncryptedZIPIOProvider implements ExportImportIOProvider { @Override public ExportWriter getExportWriter() { - String zipFile = Config.getExportImportZipFile(); - String zipPassword = Config.getExportImportZipPassword(); + String zipFile = ExportImportConfig.getZipFile(); + String zipPassword = ExportImportConfig.getZipPassword(); logger.infof("Using zip for export: " + zipFile); if (zipFile==null || zipPassword==null) { @@ -35,8 +35,8 @@ public class EncryptedZIPIOProvider implements ExportImportIOProvider { @Override public ImportReader getImportReader() { - String zipFile = Config.getExportImportZipFile(); - String zipPassword = Config.getExportImportZipPassword(); + String zipFile = ExportImportConfig.getZipFile(); + String zipPassword = ExportImportConfig.getZipPassword(); logger.infof("Using zip for import: " + zipFile); if (zipFile==null || zipPassword==null) { diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java index 1e39f24323..49817c7e0a 100644 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java +++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/ExportImportTestBase.java @@ -1,36 +1,43 @@ package org.keycloak.exportimport; -import java.util.Iterator; - +import org.junit.After; import org.junit.Assert; import org.junit.Test; import org.keycloak.model.test.AbstractModelTest; import org.keycloak.model.test.ImportTest; -import org.keycloak.models.Config; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; +import org.keycloak.provider.ProviderSession; +import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.managers.ApplianceBootstrap; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; import org.keycloak.util.ProviderLoader; +import java.util.Iterator; + /** * @author Marek Posolda */ public abstract class ExportImportTestBase { - protected KeycloakSessionFactory factory; + protected ProviderSessionFactory factory; + protected ProviderSession providerSession; protected KeycloakSession identitySession; protected RealmManager realmManager; + @After + public void after() { + System.getProperties().remove("keycloak.model.provider"); + } + @Test public void testExportImport() throws Exception { // Init JPA model - Config.setModelProvider(getExportModelProvider()); - factory = KeycloakApplication.createSessionFactory(); + System.setProperty("keycloak.model.provider", getExportModelProvider()); + factory = KeycloakApplication.createProviderSessionFactory(); // Bootstrap admin realm beginTransaction(); @@ -59,8 +66,8 @@ public abstract class ExportImportTestBase { factory.close(); // Bootstrap mongo session and factory - Config.setModelProvider(getImportModelProvider()); - factory = KeycloakApplication.createSessionFactory(); + System.setProperty("keycloak.model.provider", getImportModelProvider()); + factory = KeycloakApplication.createProviderSessionFactory(); // Full import of previous export into mongo importModel(factory); @@ -83,19 +90,20 @@ public abstract class ExportImportTestBase { protected abstract String getImportModelProvider(); - protected abstract void exportModel(KeycloakSessionFactory factory); + protected abstract void exportModel(ProviderSessionFactory factory); - protected abstract void importModel(KeycloakSessionFactory factory); + protected abstract void importModel(ProviderSessionFactory factory); protected void beginTransaction() { - identitySession = factory.createSession(); + providerSession = factory.createSession(); + identitySession = providerSession.getProvider(KeycloakSession.class); identitySession.getTransaction().begin(); realmManager = new RealmManager(identitySession); } protected void commitTransaction() { identitySession.getTransaction().commit(); - identitySession.close(); + providerSession.close(); } protected ExportImportProvider getExportImportProvider() { diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java index 8175bdb498..52ee05476f 100644 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java +++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/JPAToMongoExportImportTest.java @@ -1,8 +1,7 @@ package org.keycloak.exportimport; import org.keycloak.exportimport.io.directory.TmpDirExportImportIOProvider; -import org.keycloak.models.Config; -import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderSessionFactory; /** * Test for full export of data from JPA and import them to Mongo. Using "directory" provider @@ -22,16 +21,16 @@ public class JPAToMongoExportImportTest extends ExportImportTestBase { } @Override - protected void exportModel(KeycloakSessionFactory factory) { - Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT); - Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID); + protected void exportModel(ProviderSessionFactory factory) { + ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT); + ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID); getExportImportProvider().checkExportImport(factory); } @Override - protected void importModel(KeycloakSessionFactory factory) { - Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT); - Config.setExportImportProvider(TmpDirExportImportIOProvider.PROVIDER_ID); + protected void importModel(ProviderSessionFactory factory) { + ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT); + ExportImportConfig.setProvider(TmpDirExportImportIOProvider.PROVIDER_ID); getExportImportProvider().checkExportImport(factory); } } diff --git a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java index e715c9c662..849c388a75 100644 --- a/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java +++ b/export-import/export-import-impl/src/test/java/org/keycloak/exportimport/MongoToJPAExportImportTest.java @@ -1,11 +1,10 @@ package org.keycloak.exportimport; -import java.io.File; - import org.junit.Assert; import org.keycloak.exportimport.io.zip.EncryptedZIPIOProvider; -import org.keycloak.models.Config; -import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderSessionFactory; + +import java.io.File; /** * Test for full export of data from Mongo and import them to JPA. Using export into encrypted ZIP and import from it @@ -27,12 +26,12 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase { } @Override - protected void exportModel(KeycloakSessionFactory factory) { - Config.setExportImportAction(ExportImportProviderImpl.ACTION_EXPORT); - Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID); + protected void exportModel(ProviderSessionFactory factory) { + ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_EXPORT); + ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID); File zipFile = getZipFile(); - Config.setExportImportZipFile(zipFile.getAbsolutePath()); - Config.setExportImportZipPassword("password123"); + ExportImportConfig.setZipFile(zipFile.getAbsolutePath()); + ExportImportConfig.setZipPassword("password123"); if (zipFile.exists()) { zipFile.delete(); @@ -42,12 +41,12 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase { } @Override - protected void importModel(KeycloakSessionFactory factory) { - Config.setExportImportAction(ExportImportProviderImpl.ACTION_IMPORT); - Config.setExportImportProvider(EncryptedZIPIOProvider.PROVIDER_ID); + protected void importModel(ProviderSessionFactory factory) { + ExportImportConfig.setAction(ExportImportProviderImpl.ACTION_IMPORT); + ExportImportConfig.setProvider(EncryptedZIPIOProvider.PROVIDER_ID); File zipFile = getZipFile(); - Config.setExportImportZipFile(zipFile.getAbsolutePath()); - Config.setExportImportZipPassword("password-invalid"); + ExportImportConfig.setZipFile(zipFile.getAbsolutePath()); + ExportImportConfig.setZipPassword("password-invalid"); // Try invalid password try { @@ -55,7 +54,7 @@ public class MongoToJPAExportImportTest extends ExportImportTestBase { Assert.fail("Not expected to be here. Exception should be thrown"); } catch (Exception e) {}; - Config.setExportImportZipPassword("password123"); + ExportImportConfig.setZipPassword("password123"); new ExportImportProviderImpl().checkExportImport(factory); if (zipFile.exists()) { diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeLoader.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeLoader.java index c64bff6713..23f99e3567 100755 --- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeLoader.java +++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/ThemeLoader.java @@ -1,6 +1,6 @@ package org.keycloak.freemarker; -import org.keycloak.models.Config; +import org.keycloak.Config; import org.keycloak.util.ProviderLoader; import java.io.IOException; @@ -20,7 +20,7 @@ public class ThemeLoader { public static Theme createTheme(String name, Theme.Type type) throws FreeMarkerException { if (name == null) { - name = Config.getThemeDefault(); + name = Config.scope("theme").get("default"); } List providers = new LinkedList(); diff --git a/forms/common-themes/src/main/java/org/keycloak/theme/DefaultKeycloakThemeProvider.java b/forms/common-themes/src/main/java/org/keycloak/theme/DefaultKeycloakThemeProvider.java index a323945959..8a8659c4d2 100644 --- a/forms/common-themes/src/main/java/org/keycloak/theme/DefaultKeycloakThemeProvider.java +++ b/forms/common-themes/src/main/java/org/keycloak/theme/DefaultKeycloakThemeProvider.java @@ -1,15 +1,11 @@ package org.keycloak.theme; import org.keycloak.freemarker.Theme; -import org.keycloak.freemarker.ThemeLoader; import org.keycloak.freemarker.ThemeProvider; -import org.keycloak.models.Config; import java.io.IOException; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; -import java.util.Map; import java.util.Set; /** diff --git a/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java b/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java index a76b32813d..101851c9b7 100644 --- a/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java +++ b/forms/common-themes/src/main/java/org/keycloak/theme/FolderThemeProvider.java @@ -2,7 +2,7 @@ package org.keycloak.theme; import org.keycloak.freemarker.Theme; import org.keycloak.freemarker.ThemeProvider; -import org.keycloak.models.Config; +import org.keycloak.Config; import java.io.File; import java.io.FileFilter; @@ -19,7 +19,7 @@ public class FolderThemeProvider implements ThemeProvider { private File rootDir; public FolderThemeProvider() { - String d = Config.getThemeDir(); + String d = Config.scope("theme").get("dir"); if (d != null) { rootDir = new File(d); } diff --git a/model/api/src/main/java/org/keycloak/models/Config.java b/model/api/src/main/java/org/keycloak/models/Config.java deleted file mode 100644 index f0416d1657..0000000000 --- a/model/api/src/main/java/org/keycloak/models/Config.java +++ /dev/null @@ -1,183 +0,0 @@ -package org.keycloak.models; - -import java.io.File; -import java.util.concurrent.TimeUnit; - -/** - * @author Stian Thorgersen - */ -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); - } - -} diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java index 9aa55ee2d1..aa61bec51b 100755 --- a/model/api/src/main/java/org/keycloak/models/KeycloakSession.java +++ b/model/api/src/main/java/org/keycloak/models/KeycloakSession.java @@ -1,12 +1,14 @@ package org.keycloak.models; +import org.keycloak.provider.Provider; + import java.util.List; /** * @author Bill Burke * @version $Revision: 1 $ */ -public interface KeycloakSession { +public interface KeycloakSession extends Provider { KeycloakTransaction getTransaction(); RealmModel createRealm(String name); diff --git a/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java b/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java index 3149f9b27b..dba6d84291 100755 --- a/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java +++ b/model/api/src/main/java/org/keycloak/models/KeycloakSessionFactory.java @@ -1,10 +1,13 @@ package org.keycloak.models; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.ProviderSession; + /** * @author Bill Burke * @version $Revision: 1 $ */ -public interface KeycloakSessionFactory { - KeycloakSession createSession(); +public interface KeycloakSessionFactory extends ProviderFactory { + KeycloakSession create(ProviderSession providerSession); void close(); } diff --git a/model/api/src/main/java/org/keycloak/models/ModelSpi.java b/model/api/src/main/java/org/keycloak/models/ModelSpi.java new file mode 100644 index 0000000000..42586e9ede --- /dev/null +++ b/model/api/src/main/java/org/keycloak/models/ModelSpi.java @@ -0,0 +1,27 @@ +package org.keycloak.models; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class ModelSpi implements Spi { + + @Override + public String getName() { + return "model"; + } + + @Override + public Class getProviderClass() { + return KeycloakSession.class; + } + + @Override + public Class getProviderFactoryClass() { + return KeycloakSessionFactory.class; + } + +} diff --git a/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java b/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java deleted file mode 100644 index 8ddedab298..0000000000 --- a/model/api/src/main/java/org/keycloak/models/utils/ModelProviderUtils.java +++ /dev/null @@ -1,49 +0,0 @@ -package org.keycloak.models.utils; - -import java.util.ServiceLoader; - -import org.keycloak.models.Config; -import org.keycloak.models.ModelProvider; - -/** - * @author Marek Posolda - */ -public class ModelProviderUtils { - - public static final String DEFAULT_MODEL_PROVIDER = "jpa"; - - public static Iterable getRegisteredProviders() { - return ServiceLoader.load(ModelProvider.class); - } - - public static ModelProvider getConfiguredModelProvider(Iterable 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()); - } - -} diff --git a/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..48ff59e26d --- /dev/null +++ b/model/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1 @@ +org.keycloak.models.ModelSpi \ No newline at end of file diff --git a/model/jpa/pom.xml b/model/jpa/pom.xml index 15ac823ca5..c12e9cde06 100755 --- a/model/jpa/pom.xml +++ b/model/jpa/pom.xml @@ -30,24 +30,6 @@ ${project.version} provided - - org.keycloak - keycloak-audit-api - ${project.version} - provided - - - org.keycloak - keycloak-audit-jpa - ${project.version} - provided - - - org.keycloak - keycloak-audit-jboss-logging - ${project.version} - provided - org.keycloak keycloak-model-api diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java index 67ba543832..6efe710f92 100755 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java +++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaKeycloakSessionFactory.java @@ -1,28 +1,53 @@ package org.keycloak.models.jpa; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; +import org.keycloak.provider.ProviderSession; import javax.persistence.EntityManagerFactory; +import javax.persistence.Persistence; +import java.util.Properties; /** * @author Bill Burke * @version $Revision: 1 $ */ public class JpaKeycloakSessionFactory implements KeycloakSessionFactory { - protected EntityManagerFactory factory; - public JpaKeycloakSessionFactory(EntityManagerFactory factory) { - this.factory = factory; + protected EntityManagerFactory emf; + + @Override + public void init(Config.Scope config) { + emf = Persistence.createEntityManagerFactory("jpa-keycloak-identity-store", getHibernateProperties()); } @Override - public KeycloakSession createSession() { - return new JpaKeycloakSession(factory.createEntityManager()); + public String getId() { + return "jpa"; + } + + @Override + public KeycloakSession create(ProviderSession providerSession) { + return new JpaKeycloakSession(emf.createEntityManager()); } @Override public void close() { - factory.close(); + emf.close(); } + + // Allows to override some properties in persistence.xml by system properties + protected Properties getHibernateProperties() { + Properties result = new Properties(); + + for (Object property : System.getProperties().keySet()) { + if (property.toString().startsWith("hibernate.")) { + String propValue = System.getProperty(property.toString()); + result.put(property, propValue); + } + } + return result; + } + } diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java deleted file mode 100755 index f49e1f2e2e..0000000000 --- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaModelProvider.java +++ /dev/null @@ -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 Bill Burke - * @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; - } -} diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory new file mode 100644 index 0000000000..d77ce1b877 --- /dev/null +++ b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory @@ -0,0 +1 @@ +org.keycloak.models.jpa.JpaKeycloakSessionFactory \ No newline at end of file diff --git a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider b/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider deleted file mode 100644 index 199922e3f1..0000000000 --- a/model/jpa/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.models.jpa.JpaModelProvider \ No newline at end of file diff --git a/model/mongo/pom.xml b/model/mongo/pom.xml index 01bce2f149..cab3668761 100755 --- a/model/mongo/pom.xml +++ b/model/mongo/pom.xml @@ -44,18 +44,6 @@ ${project.version} provided - - org.keycloak - keycloak-audit-api - ${project.version} - provided - - - org.keycloak - keycloak-audit-jboss-logging - ${project.version} - provided - org.jboss.logging jboss-logging @@ -103,10 +91,10 @@ - localhost - 27018 - keycloak - true + localhost + 27018 + keycloak + true @@ -133,10 +121,10 @@ - ${keycloak.mongo.host} - ${keycloak.mongo.port} - ${keycloak.mongo.db} - ${keycloak.mongo.clearOnStartup} + ${keycloak.model.mongo.host} + ${keycloak.model.mongo.port} + ${keycloak.model.mongo.db} + ${keycloak.model.mongo.clearOnStartup} org.keycloak:keycloak-model-tests @@ -164,7 +152,7 @@ start - ${keycloak.mongo.port} + ${keycloak.model.mongo.port} file ${project.build.directory}/mongodb.log diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java deleted file mode 100755 index 45197c70d8..0000000000 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/MongoModelProvider.java +++ /dev/null @@ -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 Bill Burke - * @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); - } -} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java index 6feb373324..142cdbe356 100755 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java +++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoKeycloakSessionFactory.java @@ -1,6 +1,11 @@ package org.keycloak.models.mongo.keycloak.adapters; +import com.mongodb.DB; +import com.mongodb.MongoClient; +import com.mongodb.MongoCredential; +import com.mongodb.ServerAddress; import org.jboss.logging.Logger; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.entities.AuthenticationLinkEntity; @@ -10,7 +15,6 @@ import org.keycloak.models.entities.RequiredCredentialEntity; import org.keycloak.models.entities.SocialLinkEntity; import org.keycloak.models.mongo.api.MongoStore; import org.keycloak.models.mongo.impl.MongoStoreImpl; -import org.keycloak.models.mongo.keycloak.config.MongoClientProvider; import org.keycloak.models.mongo.keycloak.entities.MongoApplicationEntity; import org.keycloak.models.mongo.keycloak.entities.MongoOAuthClientEntity; import org.keycloak.models.mongo.keycloak.entities.MongoRealmEntity; @@ -18,6 +22,10 @@ import org.keycloak.models.mongo.keycloak.entities.MongoRoleEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUserSessionEntity; import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEntity; +import org.keycloak.provider.ProviderSession; + +import java.net.UnknownHostException; +import java.util.Collections; /** * KeycloakSessionFactory implementation based on MongoDB @@ -27,7 +35,7 @@ import org.keycloak.models.mongo.keycloak.entities.MongoUsernameLoginFailureEnti public class MongoKeycloakSessionFactory implements KeycloakSessionFactory { protected static final Logger logger = Logger.getLogger(MongoKeycloakSessionFactory.class); - private static final Class[] MANAGED_ENTITY_TYPES = (Class[])new Class[] { + private static final Class[] MANAGED_ENTITY_TYPES = (Class[]) new Class[]{ MongoRealmEntity.class, MongoUserEntity.class, MongoRoleEntity.class, @@ -42,21 +50,50 @@ public class MongoKeycloakSessionFactory implements KeycloakSessionFactory { MongoUserSessionEntity.class }; - private final MongoClientProvider mongoClientProvider; - private final MongoStore mongoStore; + private MongoClient client; - public MongoKeycloakSessionFactory(MongoClientProvider provider) { - this.mongoClientProvider = provider; - this.mongoStore = new MongoStoreImpl(provider.getDB(), provider.clearCollectionsOnStartup(), MANAGED_ENTITY_TYPES); + private MongoStore mongoStore; + + @Override + public String getId() { + return "mongo"; } @Override - public KeycloakSession createSession() { + public void init(Config.Scope config) { + try { + String host = config.get("host", ServerAddress.defaultHost()); + int port = config.getInt("port", ServerAddress.defaultPort()); + String dbName = config.get("db", "keycloak-audit"); + boolean clearOnStartup = config.getBoolean("clearOnStartup", false); + + String user = config.get("user"); + String password = config.get("password"); + if (user != null && password != null) { + MongoCredential credential = MongoCredential.createMongoCRCredential(user, dbName, password.toCharArray()); + client = new MongoClient(new ServerAddress(host, port), Collections.singletonList(credential)); + } else { + client = new MongoClient(host, port); + } + + DB db = client.getDB(dbName); + + this.mongoStore = new MongoStoreImpl(db, clearOnStartup, MANAGED_ENTITY_TYPES); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + + } + + @Override + public KeycloakSession create(ProviderSession providerSession) { return new MongoKeycloakSession(mongoStore); } @Override public void close() { - this.mongoClientProvider.close(); + this.client.close(); } + } + diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProvider.java deleted file mode 100644 index 180f37dcdd..0000000000 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.keycloak.models.mongo.keycloak.config; - -import com.mongodb.DB; -import com.mongodb.MongoClient; - -/** - * @author Marek Posolda - */ -public interface MongoClientProvider { - - MongoClient getMongoClient(); - - DB getDB(); - - /** - * @return true if collections should be cleared on startup - */ - boolean clearCollectionsOnStartup(); - - void close(); -} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProviderHolder.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProviderHolder.java deleted file mode 100644 index 038e805c4d..0000000000 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/MongoClientProviderHolder.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.keycloak.models.mongo.keycloak.config; - -/** - * Provides {@link MongoClientProvider} instance - * - * @author Marek Posolda - */ -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; - } -} diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/SystemPropertiesMongoClientProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/SystemPropertiesMongoClientProvider.java deleted file mode 100755 index 4635fd4a3c..0000000000 --- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/config/SystemPropertiesMongoClientProvider.java +++ /dev/null @@ -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 Marek Posolda - */ -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); - } -} diff --git a/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory new file mode 100644 index 0000000000..47363dca90 --- /dev/null +++ b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.KeycloakSessionFactory @@ -0,0 +1 @@ +org.keycloak.models.mongo.keycloak.adapters.MongoKeycloakSessionFactory \ No newline at end of file diff --git a/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider b/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider deleted file mode 100644 index c9e753986d..0000000000 --- a/model/mongo/src/main/resources/META-INF/services/org.keycloak.models.ModelProvider +++ /dev/null @@ -1 +0,0 @@ -org.keycloak.models.mongo.keycloak.MongoModelProvider \ No newline at end of file diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java deleted file mode 100755 index d549fa79c8..0000000000 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Address.java +++ /dev/null @@ -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 Marek Posolda - */ -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; - } -} diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/AddressWithFlats.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/AddressWithFlats.java deleted file mode 100644 index 7e13e94896..0000000000 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/AddressWithFlats.java +++ /dev/null @@ -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 Marek Posolda - */ -public class AddressWithFlats extends Address { - - private List flatNumbers; - - public List getFlatNumbers() { - return flatNumbers; - } - - public void setFlatNumbers(List flatNumbers) { - this.flatNumbers = flatNumbers; - } -} diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoStoreTest.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoStoreTest.java deleted file mode 100755 index f48b372c58..0000000000 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/MongoStoreTest.java +++ /dev/null @@ -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 Marek Posolda - */ -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 addresses = new ArrayList(); - 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 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 List asList(T... objects) { - List list = new ArrayList(); - list.addAll(Arrays.asList(objects)); - return list; - } -} diff --git a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java b/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java deleted file mode 100755 index 3db661671c..0000000000 --- a/model/mongo/src/test/java/org/keycloak/models/mongo/test/Person.java +++ /dev/null @@ -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 Marek Posolda - */ -@MongoCollection(collectionName = "persons") -public class Person implements MongoIdentifiableEntity { - - private String id; - private String firstName; - private int age; - private List kids; - private List addresses; - private Address mainAddress; - private Gender gender; - private List genders; - private Map attributes = new HashMap(); - - 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 getGenders() { - return genders; - } - - public void setGenders(List genders) { - this.genders = genders; - } - - public List getKids() { - return kids; - } - - public void setKids(List kids) { - this.kids = kids; - } - - public List getAddresses() { - return addresses; - } - - public void setAddresses(List addresses) { - this.addresses = addresses; - } - - public Address getMainAddress() { - return mainAddress; - } - - public void setMainAddress(Address mainAddress) { - this.mainAddress = mainAddress; - } - - public Map getAttributes() { - return attributes; - } - - public void setAttributes(Map 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 - } -} diff --git a/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java b/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java index 2f7177b215..428d4c8bb1 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AbstractModelTest.java @@ -11,10 +11,9 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.RoleModel; -import org.keycloak.models.Config; +import org.keycloak.Config; import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.representations.idm.RealmRepresentation; @@ -28,7 +27,6 @@ import org.keycloak.util.JsonSerialization; */ public class AbstractModelTest { - protected static KeycloakSessionFactory factory; protected static ProviderSessionFactory providerSessionFactory; protected KeycloakSession identitySession; @@ -37,41 +35,40 @@ public class AbstractModelTest { @BeforeClass public static void beforeClass() { - factory = KeycloakApplication.createSessionFactory(); providerSessionFactory = KeycloakApplication.createProviderSessionFactory(); - KeycloakSession identitySession = factory.createSession(); + ProviderSession providerSession = providerSessionFactory.createSession(); + KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class); try { identitySession.getTransaction().begin(); new ApplianceBootstrap().bootstrap(identitySession, "/auth"); identitySession.getTransaction().commit(); } finally { - identitySession.close(); + providerSession.close(); } } @AfterClass public static void afterClass() { providerSessionFactory.close(); - factory.close(); } @Before public void before() throws Exception { - identitySession = factory.createSession(); + providerSession = providerSessionFactory.createSession(); + + identitySession = providerSession.getProvider(KeycloakSession.class); identitySession.getTransaction().begin(); realmManager = new RealmManager(identitySession); - - providerSession = providerSessionFactory.createSession(); } @After public void after() throws Exception { identitySession.getTransaction().commit(); providerSession.close(); - identitySession.close(); - identitySession = factory.createSession(); + providerSession = providerSessionFactory.createSession(); + identitySession = providerSession.getProvider(KeycloakSession.class); try { identitySession.getTransaction().begin(); @@ -84,7 +81,7 @@ public class AbstractModelTest { identitySession.getTransaction().commit(); } finally { - identitySession.close(); + providerSession.close(); } } @@ -103,8 +100,10 @@ public class AbstractModelTest { } protected void resetSession() { - identitySession.close(); - identitySession = factory.createSession(); + providerSession.close(); + + providerSession = providerSessionFactory.createSession(); + identitySession = providerSession.getProvider(KeycloakSession.class); identitySession.getTransaction().begin(); realmManager = new RealmManager(identitySession); } diff --git a/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java b/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java index 880ce1462c..a603226fdb 100755 --- a/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java +++ b/model/tests/src/test/java/org/keycloak/model/test/AuthenticationManagerTest.java @@ -164,7 +164,7 @@ public class AuthenticationManagerTest extends AbstractModelTest { realm.setAccessTokenLifespan(1000); realm.addRequiredCredential(CredentialRepresentation.PASSWORD); realm.setAuthenticationProviders(Arrays.asList(AuthenticationProviderModel.DEFAULT_PROVIDER)); - protector = new BruteForceProtector(factory); + protector = new BruteForceProtector(providerSessionFactory); protector.start(); am = new AuthenticationManager(providerSession, protector); diff --git a/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/IdentityManagerSpi.java b/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/IdentityManagerSpi.java new file mode 100644 index 0000000000..42111da0d3 --- /dev/null +++ b/picketlink/keycloak-picketlink-api/src/main/java/org/keycloak/picketlink/IdentityManagerSpi.java @@ -0,0 +1,25 @@ +package org.keycloak.picketlink; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class IdentityManagerSpi implements Spi { + @Override + public String getName() { + return "picketlink-identity-manager"; + } + + @Override + public Class getProviderClass() { + return IdentityManagerProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return IdentityManagerProviderFactory.class; + } +} diff --git a/picketlink/keycloak-picketlink-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/picketlink/keycloak-picketlink-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..ffcb6e5424 --- /dev/null +++ b/picketlink/keycloak-picketlink-api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1 @@ +org.keycloak.picketlink.IdentityManagerSpi \ No newline at end of file diff --git a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProviderFactory.java b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProviderFactory.java index 83b5970816..236cff6726 100644 --- a/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProviderFactory.java +++ b/picketlink/keycloak-picketlink-realm/src/main/java/org/keycloak/picketlink/realm/RealmIdentityManagerProviderFactory.java @@ -1,5 +1,6 @@ package org.keycloak.picketlink.realm; +import org.keycloak.Config; import org.keycloak.picketlink.IdentityManagerProvider; import org.keycloak.picketlink.IdentityManagerProviderFactory; import org.keycloak.provider.ProviderSession; @@ -20,7 +21,7 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi } @Override - public void init() { + public void init(Config.Scope config) { partitionManagerRegistry = new PartitionManagerRegistry(); } @@ -33,8 +34,4 @@ public class RealmIdentityManagerProviderFactory implements IdentityManagerProvi return "realm"; } - @Override - public boolean lazyLoad() { - return false; - } } diff --git a/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java b/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java index eddeace54d..b7b0d568d0 100755 --- a/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java +++ b/project-integrations/aerogear-ups/auth-server/src/main/java/org/aerogear/ups/security/UpsSecurityApplication.java @@ -4,6 +4,7 @@ import org.jboss.resteasy.core.Dispatcher; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderSession; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; @@ -22,7 +23,8 @@ public class UpsSecurityApplication extends KeycloakApplication { @Override protected void setupDefaultRealm(String contextPath) { super.setupDefaultRealm(contextPath); - KeycloakSession session = factory.createSession(); + ProviderSession providerSession = providerSessionFactory.createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); // disable master realm by deleting the admin user. @@ -33,7 +35,7 @@ public class UpsSecurityApplication extends KeycloakApplication { if (admin != null) master.removeUser(admin.getLoginName()); session.getTransaction().commit(); } finally { - session.close(); + providerSession.close(); } } diff --git a/server/pom.xml b/server/pom.xml index 3e29a02489..1276b48d31 100755 --- a/server/pom.xml +++ b/server/pom.xml @@ -225,6 +225,39 @@ provided + + + org.keycloak + keycloak-model-mongo + ${project.version} + + + org.keycloak + keycloak-audit-mongo + ${project.version} + + + org.mongodb + mongo-java-driver + + + org.picketlink + picketlink-common + + + + org.picketlink + picketlink-idm-api + + + org.picketlink + picketlink-idm-impl + + + org.picketlink + picketlink-idm-simple-schema + + org.keycloak diff --git a/server/src/main/resources/META-INF/keycloak-server.json b/server/src/main/resources/META-INF/keycloak-server.json new file mode 100644 index 0000000000..62405d9d43 --- /dev/null +++ b/server/src/main/resources/META-INF/keycloak-server.json @@ -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 + } +} \ No newline at end of file diff --git a/server/src/main/resources/META-INF/persistence.xml b/server/src/main/resources/META-INF/persistence.xml index 616f8e8c5e..2ec80b428e 100755 --- a/server/src/main/resources/META-INF/persistence.xml +++ b/server/src/main/resources/META-INF/persistence.xml @@ -24,6 +24,7 @@ + @@ -35,6 +36,7 @@ + diff --git a/services/pom.xml b/services/pom.xml index af526e7029..6b8ceef248 100755 --- a/services/pom.xml +++ b/services/pom.xml @@ -102,6 +102,7 @@ jboss-servlet-api_3.0_spec provided + org.jboss.logging jboss-logging diff --git a/services/src/main/java/org/keycloak/services/DefaultProviderSession.java b/services/src/main/java/org/keycloak/services/DefaultProviderSession.java index cd6a9c92a4..aee7a41f50 100755 --- a/services/src/main/java/org/keycloak/services/DefaultProviderSession.java +++ b/services/src/main/java/org/keycloak/services/DefaultProviderSession.java @@ -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 Stian Thorgersen */ public class DefaultProviderSession implements ProviderSession { + private DefaultProviderSessionFactory factory; private Map providers = new HashMap(); @@ -21,8 +24,16 @@ public class DefaultProviderSession implements ProviderSession { } public T getProvider(Class 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 providerFactory = factory.getProviderFactory(clazz); + if (providerFactory != null) { + provider = providerFactory.create(this); + providers.put(hash, provider); + } + } + return provider; } public T getProvider(Class clazz, String id) { @@ -39,15 +50,14 @@ public class DefaultProviderSession implements ProviderSession { } public Set listProviderIds(Class clazz) { - return factory.providerIds(clazz); + return factory.getAllProviderIds(clazz); } @Override public Set getAllProviders(Class clazz) { - Set providerIds = listProviderIds(clazz); Set providers = new HashSet(); - for (String providerId : providerIds) { - providers.add(getProvider(clazz, providerId)); + for (String id : listProviderIds(clazz)) { + providers.add(getProvider(clazz, id)); } return providers; } diff --git a/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java b/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java index fbc0993c06..128c27200c 100755 --- a/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java +++ b/services/src/main/java/org/keycloak/services/DefaultProviderSessionFactory.java @@ -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, ProviderFactoryLoader> loaders = new HashMap, ProviderFactoryLoader>(); - private Map, String> defaultFactories = new HashMap, String>(); + private static final Logger log = Logger.getLogger(DefaultProviderSessionFactory.class); + + private Map, String> provider = new HashMap, String>(); + private Map, Map> factoriesMap = new HashMap, Map>(); + + public void init() { + for (Spi spi : ServiceLoader.load(Spi.class)) { + Map factories = new HashMap(); + 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); } + ProviderFactory getProviderFactory(Class clazz) { + return getProviderFactory(clazz, provider.get(clazz)); + } + + ProviderFactory getProviderFactory(Class clazz, String id) { + return factoriesMap.get(clazz).get(id); + } + + Set getAllProviderIds(Class clazz) { + Set ids = new HashSet(); + 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 factories : factoriesMap.values()) { + for (ProviderFactory factory : factories.values()) { + factory.close(); + } } } - public ProviderFactory getProviderFactory(Class clazz) { - String id = defaultFactories.get(clazz); - if (id == null) { - return null; - } - return getProviderFactory(clazz, id); - } - - public ProviderFactory getProviderFactory(Class clazz, String id) { - ProviderFactoryLoader loader = getLoader(clazz); - return loader != null ? loader.find(id) : null; - } - - public Set providerIds(Class clazz) { - ProviderFactoryLoader loader = getLoader(clazz); - return loader != null ? loader.providerIds() : null; - } - - public String getDefaultProvider(Class clazz) { - return defaultFactories.get(clazz); - } - - public void registerLoader(Class clazz, ProviderFactoryLoader loader) { - loaders.put(clazz, loader); - - } - - public void registerLoader(Class clazz, ProviderFactoryLoader loader, String defaultProvider) { - loaders.put(clazz, loader); - defaultFactories.put(clazz, defaultProvider); - - } - - public void init() { - for (ProviderFactoryLoader l : loaders.values()) { - l.init(); - } - } - - private ProviderFactoryLoader getLoader(Class clazz) { - return loaders.get(clazz); - } - } diff --git a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java index 97a5f0f9c0..68106f975b 100755 --- a/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java +++ b/services/src/main/java/org/keycloak/services/filters/KeycloakSessionServletFilter.java @@ -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(); } diff --git a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java index 1ef7d7c693..653cdc6f93 100755 --- a/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java +++ b/services/src/main/java/org/keycloak/services/managers/ApplianceBootstrap.java @@ -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); diff --git a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java index a941f89ad7..f231825ee7 100755 --- a/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java +++ b/services/src/main/java/org/keycloak/services/managers/BruteForceProtector.java @@ -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); diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java index 43e9630e3c..19c441fbfc 100755 --- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java +++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java @@ -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; diff --git a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java index 63396a19b4..dec47523fc 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -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 singletons = new HashSet(); protected Set> classes = new HashSet>(); - 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 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!"); } diff --git a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java index 889990fa5b..ad5cd03b14 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/AdminConsole.java @@ -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); diff --git a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java index 1caa29029b..275b43bdad 100644 --- a/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java +++ b/services/src/main/java/org/keycloak/services/scheduled/ScheduledTaskRunner.java @@ -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) { diff --git a/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java new file mode 100644 index 0000000000..181de112fc --- /dev/null +++ b/services/src/main/java/org/keycloak/services/util/JsonConfigProvider.java @@ -0,0 +1,129 @@ +package org.keycloak.services.util; + +import org.codehaus.jackson.JsonNode; +import org.keycloak.Config; +import org.keycloak.util.StringPropertyReplacer; + +/** + * @author Stian Thorgersen + */ +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(); + } + } + } + +} diff --git a/social/core/src/main/java/org/keycloak/social/SocialLoader.java b/social/core/src/main/java/org/keycloak/social/SocialLoader.java index 6d245355ac..23876e1315 100644 --- a/social/core/src/main/java/org/keycloak/social/SocialLoader.java +++ b/social/core/src/main/java/org/keycloak/social/SocialLoader.java @@ -2,8 +2,6 @@ package org.keycloak.social; import org.keycloak.util.ProviderLoader; -import java.util.ServiceLoader; - /** * @author Stian Thorgersen */ diff --git a/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java b/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java index 6f3103ded2..e901be73e2 100644 --- a/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java +++ b/social/core/src/main/test/java/org/keycloak/social/utils/SimpleHttpTest.java @@ -90,10 +90,9 @@ public class SimpleHttpTest { @Test public void testGetCustomHeader() throws IOException { - JsonNode o = SimpleHttp.doGet("http://localhost:8081/tojson").header("Accept", "application/json").header("Authorization", "bearer dsfsadfsdf").asJson(); + JsonNode o = SimpleHttp.doGet("http://localhost:8081/tojson").header("Authorization", "bearer dsfsadfsdf").asJson(); JsonNode h = o.get("headers"); - assertEquals("application/json", h.get("Accept")); assertEquals("bearer dsfsadfsdf", h.get("Authorization").getTextValue()); } diff --git a/testsuite/integration/README.md b/testsuite/integration/README.md index 1b3ed3e802..003c2151bf 100644 --- a/testsuite/integration/README.md +++ b/testsuite/integration/README.md @@ -11,7 +11,7 @@ To run the tests with Firefox add `-Dbrowser=firefox` or for Chrome add `-Dbrows Mongo ----- -The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model=mongo` when executing it. +The testsuite is executed with JPA model implementation with data saved in H2 database by default. To run testsuite with Mongo model, just add property `-Dkeycloak.model.provider=mongo` when executing it. Note that this will automatically run embedded Mongo database on localhost/27018 and it will stop it after whole testsuite is finished. So you don't need to have Mongo installed on your laptop to run mongo execution tests. @@ -52,11 +52,11 @@ For example to use the example themes run the server with: To start a Keycloak server with identity model data persisted in Mongo database instead of default JPA/H2 you can run: - mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo + mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo By default it's using database `keycloak` on localhost/27017 and it uses already existing data from this DB (no cleanup of existing data during bootstrap). Assumption is that you already have DB running on localhost/27017 . Use system properties to configure things differently: - mvn exec:java -Pkeycloak-server -Dkeycloak.model=mongo -Dkeycloak.mongo.host=localhost -Dkeycloak.mongo.port=27017 -Dkeycloak.mongo.db=keycloak -Dkeycloak.mongo.clearCollectionsOnStartup=false + mvn exec:java -Pkeycloak-server -Dkeycloak.model.provider=mongo -Dkeycloak.model.mongo.host=localhost -Dkeycloak.model.mongo.port=27017 -Dkeycloak.model.mongo.db=keycloak -Dkeycloak.model.mongo.clearOnStartup=false TOTP codes ---------- diff --git a/testsuite/integration/pom.xml b/testsuite/integration/pom.xml index 74c30bbe3f..f54b269296 100755 --- a/testsuite/integration/pom.xml +++ b/testsuite/integration/pom.xml @@ -430,10 +430,10 @@ - localhost - 27018 - keycloak - true + localhost + 27018 + keycloak + true @@ -452,16 +452,17 @@ - ${keycloak.mongo.host} - ${keycloak.mongo.port} - ${keycloak.mongo.db} + mongo + ${keycloak.model.mongo.host} + ${keycloak.model.mongo.port} + ${keycloak.model.mongo.db} - mongo - ${keycloak.mongo.host} - ${keycloak.mongo.port} - ${keycloak.mongo.db} + mongo + ${keycloak.model.mongo.host} + ${keycloak.model.mongo.port} + ${keycloak.model.mongo.db} - ${keycloak.mongo.clearOnStartup} + ${keycloak.model.mongo.clearOnStartup} @@ -486,7 +487,7 @@ start - ${keycloak.mongo.port} + ${keycloak.model.mongo.port} file ${project.build.directory}/mongodb.log diff --git a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java index f3eb078f9d..da3dc68683 100755 --- a/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java +++ b/testsuite/integration/src/main/java/org/keycloak/testutils/KeycloakServer.java @@ -30,18 +30,16 @@ import io.undertow.servlet.api.FilterInfo; import org.jboss.logging.Logger; import org.jboss.resteasy.plugins.server.undertow.UndertowJaxrsServer; import org.jboss.resteasy.spi.ResteasyDeployment; -import org.keycloak.models.Config; import org.keycloak.models.KeycloakSession; -import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderSession; import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.services.filters.ClientConnectionFilter; import org.keycloak.services.filters.KeycloakSessionServletFilter; import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resources.KeycloakApplication; -import org.keycloak.theme.DefaultKeycloakThemeProvider; import org.keycloak.util.JsonSerialization; import javax.servlet.DispatcherType; @@ -129,9 +127,8 @@ public class KeycloakServer { throw new RuntimeException("Invalid resources directory"); } - if (Config.getThemeDir() == null) { - System.setProperty(DefaultKeycloakThemeProvider.class.getName() + ".disabled", ""); - Config.setThemeDir(file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath()); + if (!System.getProperties().containsKey("keycloak.theme.dir")) { + System.setProperty("keycloak.theme.dir", file(dir.getAbsolutePath(), "forms", "common-themes", "src", "main", "resources", "theme").getAbsolutePath()); } config.setResourcesHome(dir.getAbsolutePath()); @@ -162,8 +159,6 @@ public class KeycloakServer { private KeycloakServerConfig config; - private KeycloakSessionFactory factory; - private ProviderSessionFactory providerSessionFactory; private UndertowJaxrsServer server; @@ -176,10 +171,6 @@ public class KeycloakServer { this.config = config; } - public KeycloakSessionFactory getKeycloakSessionFactory() { - return factory; - } - public ProviderSessionFactory getProviderSessionFactory() { return providerSessionFactory; } @@ -194,7 +185,8 @@ public class KeycloakServer { } public void importRealm(RealmRepresentation rep) { - KeycloakSession session = factory.createSession(); + ProviderSession providerSession = providerSessionFactory.createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); try { @@ -217,12 +209,13 @@ public class KeycloakServer { session.getTransaction().commit(); } finally { - session.close(); + providerSession.close(); } } protected void setupDevConfig() { - KeycloakSession session = factory.createSession(); + ProviderSession providerSession = providerSessionFactory.createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); try { @@ -234,7 +227,7 @@ public class KeycloakServer { session.getTransaction().commit(); } finally { - session.close(); + providerSession.close(); } } @@ -266,7 +259,6 @@ public class KeycloakServer { server.deploy(di); - factory = ((KeycloakApplication) deployment.getApplication()).getFactory(); providerSessionFactory = ((KeycloakApplication) deployment.getApplication()).getProviderSessionFactory(); setupDevConfig(); @@ -289,7 +281,6 @@ public class KeycloakServer { public void stop() { providerSessionFactory.close(); - factory.close(); server.stop(); info("Stopped Keycloak"); diff --git a/testsuite/integration/src/main/resources/META-INF/keycloak-server.json b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json new file mode 100644 index 0000000000..ee6c8fcae5 --- /dev/null +++ b/testsuite/integration/src/main/resources/META-INF/keycloak-server.json @@ -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 + } +} \ No newline at end of file diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java index a57aff1a9b..4a8543e672 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/AssertEvents.java @@ -4,10 +4,10 @@ import org.hamcrest.CoreMatchers; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.hamcrest.TypeSafeMatcher; -import org.jboss.logging.Logger; import org.junit.Assert; import org.junit.rules.TestRule; import org.junit.runners.model.Statement; +import org.keycloak.Config; import org.keycloak.audit.AuditListener; import org.keycloak.audit.AuditListenerFactory; import org.keycloak.audit.Details; @@ -58,11 +58,6 @@ public class AssertEvents implements TestRule, AuditListenerFactory { return "assert-events"; } - @Override - public boolean lazyLoad() { - return false; - } - @Override public Statement apply(final Statement base, org.junit.runner.Description description) { return new Statement() { @@ -194,7 +189,7 @@ public class AssertEvents implements TestRule, AuditListenerFactory { } @Override - public void init() { + public void init(Config.Scope config) { } @Override diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java index 8290d3f7ce..393c3f72c1 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/AccountTest.java @@ -22,18 +22,14 @@ package org.keycloak.testsuite.account; import org.junit.After; -import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; import org.keycloak.audit.Details; import org.keycloak.audit.Event; -import org.keycloak.audit.jpa.JpaAuditProviderFactory; import org.keycloak.models.ApplicationModel; -import org.keycloak.models.Config; import org.keycloak.models.PasswordPolicy; import org.keycloak.models.RealmModel; import org.keycloak.models.UserCredentialModel; diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java index ba5aaab132..4051a6e8b0 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java @@ -32,6 +32,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.provider.ProviderSession; import org.keycloak.representations.AccessToken; import org.keycloak.representations.adapters.action.SessionStats; import org.keycloak.representations.idm.RealmRepresentation; @@ -171,11 +172,11 @@ public class AdapterTest { System.out.println(pageSource); Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.getRealmByName("demo"); + ProviderSession providerSession = keycloakRule.startSession(); + RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo"); int originalIdle = realm.getSsoSessionIdleTimeout(); realm.setSsoSessionIdleTimeout(1); - keycloakRule.stopSession(session, true); + keycloakRule.stopSession(providerSession, true); Thread.sleep(2000); @@ -184,10 +185,10 @@ public class AdapterTest { driver.navigate().to("http://localhost:8081/product-portal"); Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - session = keycloakRule.startSession(); - realm = session.getRealmByName("demo"); + providerSession = keycloakRule.startSession(); + realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo"); realm.setSsoSessionIdleTimeout(originalIdle); - keycloakRule.stopSession(session, true); + keycloakRule.stopSession(providerSession, true); } @Test public void testLoginSSOMax() throws Exception { @@ -202,11 +203,11 @@ public class AdapterTest { System.out.println(pageSource); Assert.assertTrue(pageSource.contains("Bill Burke") && pageSource.contains("Stian Thorgersen")); - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.getRealmByName("demo"); + ProviderSession providerSession = keycloakRule.startSession(); + RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo"); int original = realm.getSsoSessionMaxLifespan(); realm.setSsoSessionMaxLifespan(1); - keycloakRule.stopSession(session, true); + keycloakRule.stopSession(providerSession, true); Thread.sleep(2000); @@ -215,9 +216,9 @@ public class AdapterTest { driver.navigate().to("http://localhost:8081/product-portal"); Assert.assertTrue(driver.getCurrentUrl().startsWith(LOGIN_URL)); - session = keycloakRule.startSession(); - realm = session.getRealmByName("demo"); + providerSession = keycloakRule.startSession(); + realm = providerSession.getProvider(KeycloakSession.class).getRealmByName("demo"); realm.setSsoSessionMaxLifespan(original); - keycloakRule.stopSession(session, true); + keycloakRule.stopSession(providerSession, true); } } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java index cead905f48..4b1645ea7e 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/RefreshTokenTest.java @@ -29,14 +29,12 @@ import org.keycloak.OAuth2Constants; import org.keycloak.audit.Details; import org.keycloak.audit.Errors; import org.keycloak.audit.Event; -import org.keycloak.models.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; import org.keycloak.provider.ProviderSession; import org.keycloak.representations.AccessToken; import org.keycloak.representations.RefreshToken; -import org.keycloak.services.managers.RealmManager; import org.keycloak.testsuite.AssertEvents; import org.keycloak.testsuite.OAuthClient; import org.keycloak.testsuite.OAuthClient.AccessTokenResponse; @@ -47,7 +45,11 @@ import org.keycloak.testsuite.rule.WebRule; import org.keycloak.util.Time; import org.openqa.selenium.WebDriver; -import static org.hamcrest.Matchers.*; +import static org.hamcrest.Matchers.allOf; +import static org.hamcrest.Matchers.greaterThan; +import static org.hamcrest.Matchers.greaterThanOrEqualTo; +import static org.hamcrest.Matchers.lessThan; +import static org.hamcrest.Matchers.lessThanOrEqualTo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; @@ -181,8 +183,8 @@ public class RefreshTokenTest { String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId(); - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.getRealmByName("test"); + ProviderSession session = keycloakRule.startSession(); + RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); UserSessionModel userSession = realm.getUserSession(sessionId); int last = userSession.getLastSessionRefresh(); keycloakRule.stopSession(session, false); @@ -197,7 +199,7 @@ public class RefreshTokenTest { Assert.assertEquals(200, tokenResponse.getStatusCode()); session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); userSession = realm.getUserSession(sessionId); int next = userSession.getLastSessionRefresh(); keycloakRule.stopSession(session, false); @@ -208,7 +210,7 @@ public class RefreshTokenTest { session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); int lastAccessTokenLifespan = realm.getAccessTokenLifespan(); realm.setAccessTokenLifespan(100000); keycloakRule.stopSession(session, true); @@ -217,7 +219,7 @@ public class RefreshTokenTest { tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "password"); session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); userSession = realm.getUserSession(sessionId); next = userSession.getLastSessionRefresh(); keycloakRule.stopSession(session, false); @@ -226,7 +228,7 @@ public class RefreshTokenTest { Assert.assertThat(next, allOf(greaterThan(last), lessThan(last + 6))); session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); int originalIdle = realm.getSsoSessionIdleTimeout(); realm.setSsoSessionIdleTimeout(1); keycloakRule.stopSession(session, true); @@ -243,7 +245,7 @@ public class RefreshTokenTest { events.expectRefresh(refreshId, sessionId).error(Errors.INVALID_TOKEN); session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); realm.setSsoSessionIdleTimeout(originalIdle); realm.setAccessTokenLifespan(lastAccessTokenLifespan); keycloakRule.stopSession(session, true); @@ -266,8 +268,8 @@ public class RefreshTokenTest { String refreshId = oauth.verifyRefreshToken(tokenResponse.getRefreshToken()).getId(); - KeycloakSession session = keycloakRule.startSession(); - RealmModel realm = session.getRealmByName("test"); + ProviderSession session = keycloakRule.startSession(); + RealmModel realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); int maxLifespan = realm.getSsoSessionMaxLifespan(); realm.setSsoSessionMaxLifespan(1); keycloakRule.stopSession(session, true); @@ -281,7 +283,7 @@ public class RefreshTokenTest { assertNull(tokenResponse.getRefreshToken()); session = keycloakRule.startSession(); - realm = session.getRealmByName("test"); + realm = session.getProvider(KeycloakSession.class).getRealmByName("test"); realm.setSsoSessionMaxLifespan(maxLifespan); keycloakRule.stopSession(session, true); diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java index 9312eeeb4e..47e91ec25a 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/AbstractKeycloakRule.java @@ -6,10 +6,11 @@ import io.undertow.servlet.api.SecurityConstraint; import io.undertow.servlet.api.ServletInfo; import io.undertow.servlet.api.WebResourceCollection; import org.junit.rules.ExternalResource; -import org.keycloak.models.Config; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserModel; +import org.keycloak.provider.ProviderSession; import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.UserRepresentation; import org.keycloak.services.managers.ModelToRepresentation; @@ -33,31 +34,33 @@ public abstract class AbstractKeycloakRule extends ExternalResource { server = new KeycloakServer(); server.start(); - setupKeycloak(); } public UserRepresentation getUser(String realm, String name) { - KeycloakSession session = server.getKeycloakSessionFactory().createSession(); + ProviderSession providerSession = server.getProviderSessionFactory().createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); try { UserModel user = session.getRealmByName(realm).getUser(name); return user != null ? ModelToRepresentation.toRepresentation(user) : null; } finally { - session.close(); + providerSession.close(); } } public UserRepresentation getUserById(String realm, String id) { - KeycloakSession session = server.getKeycloakSessionFactory().createSession(); + ProviderSession providerSession = server.getProviderSessionFactory().createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); try { return ModelToRepresentation.toRepresentation(session.getRealmByName(realm).getUserById(id)); } finally { - session.close(); + providerSession.close(); } } protected void setupKeycloak() { - KeycloakSession session = server.getKeycloakSessionFactory().createSession(); + ProviderSession providerSession = server.getProviderSessionFactory().createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); try { @@ -69,9 +72,8 @@ public abstract class AbstractKeycloakRule extends ExternalResource { session.getTransaction().commit(); } finally { - session.close(); + providerSession.close(); } - } protected void configure(RealmManager manager, RealmModel adminRealm) { @@ -134,15 +136,16 @@ public abstract class AbstractKeycloakRule extends ExternalResource { return JsonSerialization.readValue(bytes, RealmRepresentation.class); } - public KeycloakSession startSession() { - KeycloakSession session = server.getKeycloakSessionFactory().createSession(); + public ProviderSession startSession() { + ProviderSession providerSession = server.getProviderSessionFactory().createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); - return session; + return providerSession; } - public void stopSession(KeycloakSession session, boolean commit) { + public void stopSession(ProviderSession session, boolean commit) { if (commit) { - session.getTransaction().commit(); + session.getProvider(KeycloakSession.class).getTransaction().commit(); } session.close(); } diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java index 4096658210..f520d19aec 100755 --- a/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java +++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/rule/KeycloakRule.java @@ -21,7 +21,7 @@ */ package org.keycloak.testsuite.rule; -import org.keycloak.models.Config; +import org.keycloak.Config; import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; @@ -61,8 +61,8 @@ public class KeycloakRule extends AbstractKeycloakRule { } public void configure(KeycloakSetup configurer) { - KeycloakSession session = server.getKeycloakSessionFactory().createSession(); ProviderSession providerSession = server.getProviderSessionFactory().createSession(); + KeycloakSession session = providerSession.getProvider(KeycloakSession.class); session.getTransaction().begin(); try { @@ -77,17 +77,16 @@ public class KeycloakRule extends AbstractKeycloakRule { session.getTransaction().commit(); } finally { providerSession.close(); - session.close(); } } public void removeUserSession(String sessionId) { - KeycloakSession keycloakSession = startSession(); - RealmModel realm = keycloakSession.getRealm("test"); + ProviderSession providerSession = startSession(); + RealmModel realm = providerSession.getProvider(KeycloakSession.class).getRealm("test"); UserSessionModel session = realm.getUserSession(sessionId); assertNotNull(session); realm.removeUserSession(session); - stopSession(keycloakSession, true); + stopSession(providerSession, true); } public abstract static class KeycloakSetup { diff --git a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java index eda27612bc..c78cb615ba 100755 --- a/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java +++ b/testsuite/performance/src/test/java/org/keycloak/testsuite/performance/BaseJMeterPerformanceTest.java @@ -6,6 +6,8 @@ import org.apache.jmeter.samplers.SampleResult; import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSessionFactory; import org.keycloak.models.KeycloakTransaction; +import org.keycloak.provider.ProviderSession; +import org.keycloak.provider.ProviderSessionFactory; import org.keycloak.services.resources.KeycloakApplication; import java.util.concurrent.Callable; @@ -18,17 +20,17 @@ import java.util.concurrent.atomic.AtomicInteger; public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { - private static FutureTask factoryProvider = new FutureTask(new Callable() { + private static FutureTask factoryProvider = new FutureTask(new Callable() { @Override - public KeycloakSessionFactory call() throws Exception { - return KeycloakApplication.createSessionFactory(); + public ProviderSessionFactory call() throws Exception { + return KeycloakApplication.createProviderSessionFactory(); } }); private static AtomicInteger counter = new AtomicInteger(); - private KeycloakSessionFactory factory; + private ProviderSessionFactory factory; // private KeycloakSession identitySession; private Worker worker; private boolean setupSuccess = false; @@ -42,7 +44,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { worker = getWorker(); factory = getFactory(); - KeycloakSession identitySession = factory.createSession(); + ProviderSession providerSession = factory.createSession(); + KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class); KeycloakTransaction transaction = identitySession.getTransaction(); transaction.begin(); @@ -56,11 +59,11 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { } else { transaction.rollback(); } - identitySession.close(); + providerSession.close(); } } - private static KeycloakSessionFactory getFactory() { + private static ProviderSessionFactory getFactory() { factoryProvider.run(); try { return factoryProvider.get(); @@ -98,7 +101,8 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { return result; } - KeycloakSession identitySession = factory.createSession(); + ProviderSession providerSession = factory.createSession(); + KeycloakSession identitySession = providerSession.getProvider(KeycloakSession.class); KeycloakTransaction transaction = identitySession.getTransaction(); try { transaction.begin(); @@ -114,7 +118,7 @@ public class BaseJMeterPerformanceTest extends AbstractJavaSamplerClient { } finally { result.sampleEnd(); result.setSuccessful(true); - identitySession.close(); + providerSession.close(); } return result; diff --git a/testsuite/performance/src/test/jmeter/system.properties b/testsuite/performance/src/test/jmeter/system.properties index 26d24aab9b..c1cdda49b4 100644 --- a/testsuite/performance/src/test/jmeter/system.properties +++ b/testsuite/performance/src/test/jmeter/system.properties @@ -8,11 +8,11 @@ keycloak.jpa.hbm2ddl.auto=create ## Configure MongoDB (Useful just when keycloak.sessionFactory=mongo) -keycloak.mongodb.host=localhost -keycloak.mongodb.port=27017 -keycloak.mongodb.databaseName=keycloakPerfTest +keycloak.model.mongo.host=localhost +keycloak.model.mongo.port=27017 +keycloak.model.mongo.databaseName=keycloakPerfTest # Should be DB dropped at startup of the test? -keycloak.mongodb.dropDatabaseOnStartup=true +keycloak.model.mongo.dropDatabaseOnStartup=true ## Specify Keycloak worker class diff --git a/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java b/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java index e701b5fbea..627f00e3f5 100644 --- a/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java +++ b/timer/api/src/main/java/org/keycloak/timer/TimerProvider.java @@ -7,6 +7,6 @@ import org.keycloak.provider.Provider; */ public interface TimerProvider extends Provider { - public void schedule(Runnable runnable, String config); + public void schedule(Runnable runnable, long interval); } diff --git a/timer/api/src/main/java/org/keycloak/timer/TimerSpi.java b/timer/api/src/main/java/org/keycloak/timer/TimerSpi.java new file mode 100644 index 0000000000..d0e57f93f0 --- /dev/null +++ b/timer/api/src/main/java/org/keycloak/timer/TimerSpi.java @@ -0,0 +1,25 @@ +package org.keycloak.timer; + +import org.keycloak.provider.Provider; +import org.keycloak.provider.ProviderFactory; +import org.keycloak.provider.Spi; + +/** + * @author Stian Thorgersen + */ +public class TimerSpi implements Spi { + @Override + public String getName() { + return "timer"; + } + + @Override + public Class getProviderClass() { + return TimerProvider.class; + } + + @Override + public Class getProviderFactoryClass() { + return TimerProviderFactory.class; + } +} diff --git a/timer/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi b/timer/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi new file mode 100644 index 0000000000..e8219ff77d --- /dev/null +++ b/timer/api/src/main/resources/META-INF/services/org.keycloak.provider.Spi @@ -0,0 +1 @@ +org.keycloak.timer.TimerSpi \ No newline at end of file diff --git a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java index bda150b0aa..267f17cae1 100644 --- a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java +++ b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProvider.java @@ -18,9 +18,7 @@ public class BasicTimerProvider implements TimerProvider { } @Override - public void schedule(final Runnable runnable, String config) { - long interval = Long.parseLong(config); - + public void schedule(final Runnable runnable, final long interval) { TimerTask task = new TimerTask() { @Override public void run() { diff --git a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java index 466167df3d..3454d87806 100644 --- a/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java +++ b/timer/basic/src/main/java/org/keycloak/timer/basic/BasicTimerProviderFactory.java @@ -1,5 +1,6 @@ package org.keycloak.timer.basic; +import org.keycloak.Config; import org.keycloak.provider.ProviderSession; import org.keycloak.timer.TimerProvider; import org.keycloak.timer.TimerProviderFactory; @@ -19,7 +20,7 @@ public class BasicTimerProviderFactory implements TimerProviderFactory { } @Override - public void init() { + public void init(Config.Scope config) { timer = new Timer(); } @@ -34,8 +35,4 @@ public class BasicTimerProviderFactory implements TimerProviderFactory { return "basic"; } - @Override - public boolean lazyLoad() { - return true; - } }