diff --git a/core/pom.xml b/core/pom.xml index b567a3269a..b8b427410b 100755 --- a/core/pom.xml +++ b/core/pom.xml @@ -13,6 +13,10 @@ Keycloak Core + + ${maven.build.timestamp} + yyyy-MM-dd HH:mm + org.bouncycastle @@ -41,6 +45,12 @@ + + + src/main/resources + true + + org.apache.maven.plugins diff --git a/core/src/main/java/org/keycloak/Version.java b/core/src/main/java/org/keycloak/Version.java new file mode 100755 index 0000000000..005a4c20c4 --- /dev/null +++ b/core/src/main/java/org/keycloak/Version.java @@ -0,0 +1,44 @@ +package org.keycloak; + +import org.codehaus.jackson.annotate.JsonProperty; + +import java.io.IOException; +import java.io.InputStream; +import java.util.Properties; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class Version { + public static String VERSION; + public static String BUILD_TIME; + public static final String UNKNOWN = "UNKNOWN"; + public static final Version SINGLETON = new Version(); + + private final String version = VERSION; + private final String buildTime = BUILD_TIME; + + static { + Properties props = new Properties(); + InputStream is = Version.class.getResourceAsStream("/keycloak-version.properties"); + try { + props.load(is); + VERSION = props.getProperty("version"); + BUILD_TIME = props.getProperty("build-time"); + } catch (IOException e) { + VERSION=UNKNOWN; + BUILD_TIME=UNKNOWN; + } + } + + @JsonProperty("version") + public String getVersion() { + return version; + } + + @JsonProperty("build-time") + public String getBuildTime() { + return buildTime; + } +} diff --git a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java index b03f09eda7..6ad29c7ebd 100755 --- a/core/src/main/java/org/keycloak/adapters/AdapterConstants.java +++ b/core/src/main/java/org/keycloak/adapters/AdapterConstants.java @@ -8,6 +8,7 @@ public interface AdapterConstants { // URL endpoints public static final String K_LOGOUT = "k_logout"; + public static final String K_VERSION = "k_version"; public static final String K_PUSH_NOT_BEFORE = "k_push_not_before"; public static final String K_GET_USER_STATS = "k_get_user_stats"; public static final String K_GET_SESSION_STATS = "k_get_session_stats"; diff --git a/core/src/main/resources/keycloak-version.properties b/core/src/main/resources/keycloak-version.properties new file mode 100755 index 0000000000..9d6f352189 --- /dev/null +++ b/core/src/main/resources/keycloak-version.properties @@ -0,0 +1,2 @@ +version=${project.version} +build-time=${timestamp} \ No newline at end of file diff --git a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java index 095be57ba2..2834ccc367 100755 --- a/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java +++ b/integration/adapter-core/src/main/java/org/keycloak/adapters/PreAuthActionsHandler.java @@ -1,6 +1,7 @@ package org.keycloak.adapters; import org.jboss.logging.Logger; +import org.keycloak.Version; import org.keycloak.jose.jws.JWSInput; import org.keycloak.jose.jws.crypto.RSAProvider; import org.keycloak.representations.adapters.action.AdminAction; @@ -63,10 +64,13 @@ public class PreAuthActionsHandler { if (!resolveDeployment()) return true; handleGetSessionStats(); return true; - }else if (requestUri.endsWith(AdapterConstants.K_GET_USER_STATS)) { + } else if (requestUri.endsWith(AdapterConstants.K_GET_USER_STATS)) { if (!resolveDeployment()) return true; handleGetUserStats(); return true; + } else if (requestUri.endsWith(AdapterConstants.K_VERSION)) { + handleVersion(); + return true; } return false; } @@ -240,6 +244,15 @@ public class PreAuthActionsHandler { throw new RuntimeException(e); } } + protected void handleVersion() { + try { + facade.getResponse().setStatus(200); + facade.getResponse().setHeader("Content-Type", "application/json"); + JsonSerialization.writeValueToStream(facade.getResponse().getOutputStream(), Version.SINGLETON); + } catch (Exception e) { + throw new RuntimeException(e); + } + } protected UserStats getUserStats(String user) { UserStats stats = new UserStats(); 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 f4094af6c3..77da618070 100755 --- a/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java +++ b/services/src/main/java/org/keycloak/services/resources/KeycloakApplication.java @@ -70,6 +70,7 @@ public class KeycloakApplication extends Application { TokenManager tokenManager = new TokenManager(); + singletons.add(new ServerVersionResource()); singletons.add(new RealmsResource(tokenManager)); singletons.add(new SocialResource(tokenManager)); singletons.add(new AdminRoot(tokenManager)); diff --git a/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java b/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java new file mode 100755 index 0000000000..af60ac4c1b --- /dev/null +++ b/services/src/main/java/org/keycloak/services/resources/ServerVersionResource.java @@ -0,0 +1,22 @@ +package org.keycloak.services.resources; + +import org.keycloak.Version; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +@Path("/version") +public class ServerVersionResource { + + @GET + @Produces(MediaType.APPLICATION_JSON) + public Version getVersion() { + return Version.SINGLETON; + } +} 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 b9e4b61a72..9bb0f49faa 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 @@ -21,6 +21,8 @@ */ package org.keycloak.testsuite.adapter; +import org.keycloak.Version; +import org.keycloak.adapters.AdapterConstants; import org.keycloak.util.BasicAuthHelper; import org.junit.Assert; import org.junit.ClassRule; @@ -367,6 +369,29 @@ public class AdapterTest { } + @Test + public void testVersion() throws Exception { + Client client = ClientBuilder.newClient(); + WebTarget target = client.target(org.keycloak.testsuite.Constants.AUTH_SERVER_ROOT).path("version"); + Version version = target.request().get(Version.class); + Assert.assertNotNull(version); + Assert.assertNotNull(version.getVersion()); + Assert.assertNotNull(version.getBuildTime()); + Assert.assertNotEquals(version.getVersion(), Version.UNKNOWN); + Assert.assertNotEquals(version.getBuildTime(), Version.UNKNOWN); + + Version version2 = client.target("http://localhost:8081/secure-portal").path(AdapterConstants.K_VERSION).request().get(Version.class); + Assert.assertNotNull(version2); + Assert.assertNotNull(version2.getVersion()); + Assert.assertNotNull(version2.getBuildTime()); + Assert.assertEquals(version.getVersion(), version2.getVersion()); + Assert.assertEquals(version.getBuildTime(), version2.getBuildTime()); + client.close(); + + } + + + @Test public void testAuthenticated() throws Exception { // test login to customer-portal which does a bearer request to customer-db