diff --git a/common/src/main/java/org/keycloak/common/Profile.java b/common/src/main/java/org/keycloak/common/Profile.java new file mode 100755 index 0000000000..ac16874f22 --- /dev/null +++ b/common/src/main/java/org/keycloak/common/Profile.java @@ -0,0 +1,69 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.common; + +import java.io.File; +import java.io.FileInputStream; +import java.util.Properties; + +/** + * @author Bill Burke + * @version $Revision: 1 $ + */ +public class Profile { + + private enum ProfileValue { + PRODUCT, PREVIEW, COMMUNITY + } + + private static ProfileValue value = load(); + + static ProfileValue load() { + String profile = null; + try { + profile = System.getProperty("keycloak.profile"); + if (profile == null) { + String jbossServerConfigDir = System.getProperty("jboss.server.config.dir"); + if (jbossServerConfigDir != null) { + File file = new File(jbossServerConfigDir, "profile.properties"); + if (file.isFile()) { + Properties props = new Properties(); + props.load(new FileInputStream(file)); + profile = props.getProperty("profile"); + } + } + } + } catch (Exception e) { + } + + if (profile == null) { + return ProfileValue.valueOf(Version.DEFAULT_PROFILE.toUpperCase()); + } else { + return ProfileValue.valueOf(profile.toUpperCase()); + } + } + + public static String getName() { + return value.name().toLowerCase(); + } + + public static boolean isPreviewEnabled() { + return value.ordinal() >= ProfileValue.PREVIEW.ordinal(); + } + +} diff --git a/common/src/main/java/org/keycloak/common/Version.java b/common/src/main/java/org/keycloak/common/Version.java index 42ba52a816..862ccd2def 100755 --- a/common/src/main/java/org/keycloak/common/Version.java +++ b/common/src/main/java/org/keycloak/common/Version.java @@ -32,6 +32,7 @@ public class Version { public static String VERSION; public static String RESOURCES_VERSION; public static String BUILD_TIME; + public static String DEFAULT_PROFILE; static { Properties props = new Properties(); @@ -40,6 +41,7 @@ public class Version { props.load(is); Version.NAME = props.getProperty("name"); Version.NAME_HTML = props.getProperty("name-html"); + Version.DEFAULT_PROFILE = props.getProperty("default-profile"); Version.VERSION = props.getProperty("version"); Version.BUILD_TIME = props.getProperty("build-time"); Version.RESOURCES_VERSION = Version.VERSION.toLowerCase(); diff --git a/common/src/main/resources/keycloak-version.properties b/common/src/main/resources/keycloak-version.properties index 643b6de4af..f66e436852 100755 --- a/common/src/main/resources/keycloak-version.properties +++ b/common/src/main/resources/keycloak-version.properties @@ -18,4 +18,5 @@ name=${product.name} name-html=${product.name-html} version=${product.version} -build-time=${product.build-time} \ No newline at end of file +build-time=${product.build-time} +default-profile=${product.default-profile} \ No newline at end of file diff --git a/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java new file mode 100644 index 0000000000..3c474d03ba --- /dev/null +++ b/core/src/main/java/org/keycloak/representations/info/ProfileInfoRepresentation.java @@ -0,0 +1,53 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.representations.info; + +import org.keycloak.common.Profile; + +/** + * @author Stian Thorgersen + */ +public class ProfileInfoRepresentation { + + private String name; + private boolean previewEnabled; + + public static ProfileInfoRepresentation create() { + ProfileInfoRepresentation info = new ProfileInfoRepresentation(); + info.setName(Profile.getName()); + info.setPreviewEnabled(Profile.isPreviewEnabled()); + return info; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public boolean isPreviewEnabled() { + return previewEnabled; + } + + public void setPreviewEnabled(boolean previewEnabled) { + this.previewEnabled = previewEnabled; + } + +} diff --git a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java index 59d400e485..8c98ce3468 100755 --- a/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java +++ b/core/src/main/java/org/keycloak/representations/info/ServerInfoRepresentation.java @@ -32,6 +32,7 @@ public class ServerInfoRepresentation { private SystemInfoRepresentation systemInfo; private MemoryInfoRepresentation memoryInfo; + private ProfileInfoRepresentation profileInfo; private Map> themes; @@ -66,6 +67,14 @@ public class ServerInfoRepresentation { this.memoryInfo = memoryInfo; } + public ProfileInfoRepresentation getProfileInfo() { + return profileInfo; + } + + public void setProfileInfo(ProfileInfoRepresentation profileInfo) { + this.profileInfo = profileInfo; + } + public Map> getThemes() { return themes; } diff --git a/pom.xml b/pom.xml index e1a47ce26b..da7a2ea4bd 100755 --- a/pom.xml +++ b/pom.xml @@ -40,6 +40,7 @@ \u003Cdiv class="kc-logo-text"\u003E\u003Cspan\u003EKeycloak\u003C\u002Fspan\u003E\u003C\u002Fdiv\u003E ${project.version} ${timestamp} + community 7.0.0.Beta diff --git a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java index 1e42e7b740..d0ca449de0 100755 --- a/services/src/main/java/org/keycloak/services/resources/RealmsResource.java +++ b/services/src/main/java/org/keycloak/services/resources/RealmsResource.java @@ -35,6 +35,7 @@ import org.keycloak.services.managers.RealmManager; import org.keycloak.services.resource.RealmResourceProvider; import org.keycloak.services.util.CacheControlUtil; import org.keycloak.services.util.ResolveRelative; +import org.keycloak.utils.ProfileHelper; import org.keycloak.wellknown.WellKnownProvider; import javax.ws.rs.GET; @@ -254,6 +255,8 @@ public class RealmsResource { @Path("{realm}/authz") public Object getAuthorizationService(@PathParam("realm") String name) { + ProfileHelper.requirePreview(); + init(name); AuthorizationProvider authorization = this.session.getProvider(AuthorizationProvider.class); AuthorizationService service = new AuthorizationService(authorization); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java index d208b50711..955dff0144 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/ClientResource.java @@ -18,9 +18,9 @@ package org.keycloak.services.resources.admin; import org.jboss.resteasy.annotations.cache.NoCache; import org.jboss.resteasy.spi.BadRequestException; -import org.jboss.resteasy.spi.NotFoundException; import org.jboss.resteasy.spi.ResteasyProviderFactory; import org.keycloak.authorization.admin.AuthorizationService; +import org.keycloak.common.Profile; import org.keycloak.events.admin.OperationType; import org.keycloak.events.admin.ResourceType; import org.keycloak.models.ClientModel; @@ -52,10 +52,12 @@ import org.keycloak.common.util.Time; import org.keycloak.services.validation.ClientValidator; import org.keycloak.services.validation.PairwiseClientValidator; import org.keycloak.services.validation.ValidationMessages; +import org.keycloak.utils.ProfileHelper; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.GET; +import javax.ws.rs.NotFoundException; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; @@ -153,10 +155,12 @@ public class ClientResource { RepresentationToModel.updateClient(rep, client); - if (TRUE.equals(rep.getAuthorizationServicesEnabled())) { - authorization().enable(); - } else { - authorization().disable(); + if (Profile.isPreviewEnabled()) { + if (TRUE.equals(rep.getAuthorizationServicesEnabled())) { + authorization().enable(); + } else { + authorization().disable(); + } } } @@ -177,7 +181,9 @@ public class ClientResource { ClientRepresentation representation = ModelToRepresentation.toRepresentation(client); - representation.setAuthorizationServicesEnabled(authorization().isEnabled()); + if (Profile.isPreviewEnabled()) { + representation.setAuthorizationServicesEnabled(authorization().isEnabled()); + } return representation; } @@ -562,6 +568,8 @@ public class ClientResource { @Path("/authz") public AuthorizationService authorization() { + ProfileHelper.requirePreview(); + AuthorizationService resource = new AuthorizationService(this.session, this.client, this.auth); ResteasyProviderFactory.getInstance().injectProperties(resource); diff --git a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java index a7195e12c7..638e6570d4 100755 --- a/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java +++ b/services/src/main/java/org/keycloak/services/resources/admin/info/ServerInfoAdminResource.java @@ -43,6 +43,7 @@ import org.keycloak.policy.PasswordPolicyProviderFactory; import org.keycloak.provider.*; import org.keycloak.representations.idm.ComponentTypeRepresentation; import org.keycloak.representations.idm.PasswordPolicyTypeRepresentation; +import org.keycloak.representations.info.ProfileInfoRepresentation; import org.keycloak.theme.Theme; import org.keycloak.theme.ThemeProvider; import org.keycloak.models.KeycloakSession; @@ -84,6 +85,7 @@ public class ServerInfoAdminResource { ServerInfoRepresentation info = new ServerInfoRepresentation(); info.setSystemInfo(SystemInfoRepresentation.create(session.getKeycloakSessionFactory().getServerStartupTimestamp())); info.setMemoryInfo(MemoryInfoRepresentation.create()); + info.setProfileInfo(ProfileInfoRepresentation.create()); setSocialProviders(info); setIdentityProviders(info); diff --git a/services/src/main/java/org/keycloak/utils/ProfileHelper.java b/services/src/main/java/org/keycloak/utils/ProfileHelper.java new file mode 100644 index 0000000000..719bd24e19 --- /dev/null +++ b/services/src/main/java/org/keycloak/utils/ProfileHelper.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.utils; + +import org.keycloak.common.Profile; + +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.Response; + +/** + * @author Stian Thorgersen + */ +public class ProfileHelper { + + public static void requirePreview() { + if (!Profile.isPreviewEnabled()) { + throw new WebApplicationException("Feature not available in current profile", Response.Status.NOT_IMPLEMENTED); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java new file mode 100644 index 0000000000..7fbf59b068 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/main/java/org/keycloak/testsuite/ProfileAssume.java @@ -0,0 +1,36 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite; + +import org.junit.Assume; +import org.keycloak.common.Profile; + +/** + * @author Stian Thorgersen + */ +public class ProfileAssume { + + public static void assumePreview() { + Assume.assumeTrue("Ignoring test as community/preview profile is not enabled", Profile.isPreviewEnabled()); + } + + public static void assumePreviewDisabled() { + Assume.assumeFalse("Ignoring test as community/preview profile is enabled", Profile.isPreviewEnabled()); + } + +} diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java index c4979e0f67..2c3aac71e0 100644 --- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AbstractAuthorizationTest.java @@ -19,11 +19,13 @@ package org.keycloak.testsuite.admin.client.authorization; import org.junit.After; import org.junit.Before; +import org.junit.BeforeClass; import org.keycloak.admin.client.resource.ClientResource; import org.keycloak.admin.client.resource.ResourceScopeResource; import org.keycloak.admin.client.resource.ResourceScopesResource; import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.authorization.ScopeRepresentation; +import org.keycloak.testsuite.ProfileAssume; import org.keycloak.testsuite.admin.client.AbstractClientTest; import javax.ws.rs.core.Response; @@ -38,6 +40,11 @@ public abstract class AbstractAuthorizationTest extends AbstractClientTest { protected static final String RESOURCE_SERVER_CLIENT_ID = "test-resource-server"; + @BeforeClass + public static void enabled() { + ProfileAssume.assumePreview(); + } + @Before public void onBeforeAuthzTests() { createOidcClient(RESOURCE_SERVER_CLIENT_ID); diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java new file mode 100644 index 0000000000..072aa5f630 --- /dev/null +++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/admin/client/authorization/AuthorizationDisabledInPreviewTest.java @@ -0,0 +1,50 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.keycloak.testsuite.admin.client.authorization; + +import org.junit.BeforeClass; +import org.junit.Test; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.admin.client.AbstractClientTest; + +import javax.ws.rs.ServerErrorException; +import javax.ws.rs.core.Response; + +import static org.junit.Assert.assertEquals; + +/** + * @author Stian Thorgersen + */ +public class AuthorizationDisabledInPreviewTest extends AbstractClientTest { + + @BeforeClass + public static void enabled() { + ProfileAssume.assumePreviewDisabled(); + } + + @Test + public void testAuthzServicesRemoved() { + String id = testRealmResource().clients().findAll().get(0).getId(); + try { + testRealmResource().clients().get(id).authorization().getSettings(); + } catch (ServerErrorException e) { + assertEquals(Response.Status.NOT_IMPLEMENTED.getStatusCode(), e.getResponse().getStatus()); + } + } + +} diff --git a/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java new file mode 100644 index 0000000000..0dcf08bf9d --- /dev/null +++ b/testsuite/integration-arquillian/tests/other/console/src/test/java/org/keycloak/testsuite/console/clients/ClientAuthorizationServicesAvailableTest.java @@ -0,0 +1,65 @@ +/* + * Copyright 2016 Red Hat, Inc. and/or its affiliates + * and other contributors as indicated by the @author tags. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.keycloak.testsuite.console.clients; + +import org.jboss.arquillian.graphene.page.Page; +import org.junit.Test; +import org.keycloak.common.Profile; +import org.keycloak.representations.idm.ClientRepresentation; +import org.keycloak.testsuite.ProfileAssume; +import org.keycloak.testsuite.console.page.clients.settings.ClientSettings; +import org.openqa.selenium.By; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.keycloak.testsuite.auth.page.login.Login.OIDC; + +/** + * + * @author Vlastislav Ramik + */ +public class ClientAuthorizationServicesAvailableTest extends AbstractClientTest { + + private ClientRepresentation newClient; + + @Page + private ClientSettings clientSettingsPage; + + @Test + public void authzServicesAvailable() { + ProfileAssume.assumePreview(); + + newClient = createClientRep("oidc-public", OIDC); + createClient(newClient); + assertEquals("oidc-public", clientSettingsPage.form().getClientId()); + + assertTrue(driver.findElement(By.xpath("//*[@for='authorizationServicesEnabled']")).isDisplayed()); + } + + @Test + public void authzServicesUnavailable() throws InterruptedException { + ProfileAssume.assumePreviewDisabled(); + + newClient = createClientRep("oidc-public", OIDC); + createClient(newClient); + assertEquals("oidc-public", clientSettingsPage.form().getClientId()); + + assertFalse(driver.findElement(By.xpath("//*[@for='authorizationServicesEnabled']")).isDisplayed()); + + } +} \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index ea3426890f..d3cdd9ee82 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -851,6 +851,7 @@ include-representation=Include Representation include-representation.tooltip=Include JSON representation for create and update requests. clear-admin-events.tooltip=Deletes all admin events in the database. server-version=Server Version +server-profile=Server Profile info=Info providers=Providers server-time=Server Time diff --git a/themes/src/main/resources/theme/base/admin/resources/js/services.js b/themes/src/main/resources/theme/base/admin/resources/js/services.js index 6d8fdcdcf1..a063143fff 100755 --- a/themes/src/main/resources/theme/base/admin/resources/js/services.js +++ b/themes/src/main/resources/theme/base/admin/resources/js/services.js @@ -274,7 +274,7 @@ module.service('ServerInfo', function($resource, $q, $http) { var delay = $q.defer(); $http.get(authUrl + '/admin/serverinfo').success(function(data) { - info = data; + angular.copy(data, info); delay.resolve(info); }); diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html index d8f0d246a1..9fb3a2d5be 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/client-detail.html @@ -110,7 +110,7 @@ -
+
{{:: 'authz-authorization-services-enabled.tooltip' | translate}}
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html b/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html index 0607250031..299a93b3c9 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/server-info.html @@ -14,6 +14,10 @@ {{:: 'server-version' | translate}} {{serverInfo.systemInfo.version}} + + {{:: 'server-profile' | translate}} + {{serverInfo.profileInfo.name}} + {{:: 'server-time' | translate}} {{serverInfo.systemInfo.serverTime}} diff --git a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html index f39bc2c6cb..e2d5db2665 100755 --- a/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html +++ b/themes/src/main/resources/theme/base/admin/resources/templates/kc-tabs-client.html @@ -19,7 +19,7 @@ {{:: 'scope' | translate}} {{:: 'scope.tooltip' | translate}} -
  • {{:: 'authz-authorization' | translate}}
  • +
  • {{:: 'authz-authorization' | translate}}
  • {{:: 'revocation' | translate}}