KEYCLOAK-8556 Improvements to profile
This commit is contained in:
parent
4483677cdd
commit
11374a2707
15 changed files with 318 additions and 94 deletions
|
@ -50,6 +50,11 @@
|
|||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jboss.logging</groupId>
|
||||
<artifactId>jboss-logging</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
|
|
|
@ -17,12 +17,12 @@
|
|||
|
||||
package org.keycloak.common;
|
||||
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.io.IOException;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
|
@ -32,95 +32,101 @@ import java.util.Set;
|
|||
*/
|
||||
public class Profile {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(Profile.class);
|
||||
|
||||
public enum Type {
|
||||
DEFAULT,
|
||||
DISABLED_BY_DEFAULT,
|
||||
PREVIEW,
|
||||
EXPERIMENTAL
|
||||
}
|
||||
|
||||
public enum Feature {
|
||||
ACCOUNT2,
|
||||
ADMIN_FINE_GRAINED_AUTHZ,
|
||||
DOCKER,
|
||||
IMPERSONATION,
|
||||
OPENSHIFT_INTEGRATION,
|
||||
SCRIPTS,
|
||||
TOKEN_EXCHANGE
|
||||
ACCOUNT2(Type.EXPERIMENTAL),
|
||||
ACCOUNT_API(Type.PREVIEW),
|
||||
ADMIN_FINE_GRAINED_AUTHZ(Type.PREVIEW),
|
||||
DOCKER(Type.DISABLED_BY_DEFAULT),
|
||||
IMPERSONATION(Type.DEFAULT),
|
||||
OPENSHIFT_INTEGRATION(Type.DEFAULT),
|
||||
SCRIPTS(Type.PREVIEW),
|
||||
TOKEN_EXCHANGE(Type.PREVIEW);
|
||||
|
||||
private Type type;
|
||||
|
||||
Feature(Type type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Type getType() {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
private enum ProductValue {
|
||||
KEYCLOAK(),
|
||||
RHSSO(Feature.ACCOUNT2);
|
||||
|
||||
private List<Feature> excluded;
|
||||
|
||||
ProductValue(Feature... excluded) {
|
||||
this.excluded = Arrays.asList(excluded);
|
||||
}
|
||||
KEYCLOAK,
|
||||
RHSSO
|
||||
}
|
||||
|
||||
private enum ProfileValue {
|
||||
PRODUCT(Feature.ADMIN_FINE_GRAINED_AUTHZ, Feature.SCRIPTS, Feature.DOCKER, Feature.ACCOUNT2, Feature.TOKEN_EXCHANGE),
|
||||
PREVIEW(Feature.ACCOUNT2),
|
||||
COMMUNITY(Feature.DOCKER, Feature.ACCOUNT2);
|
||||
|
||||
private List<Feature> disabled;
|
||||
|
||||
ProfileValue(Feature... disabled) {
|
||||
this.disabled = Arrays.asList(disabled);
|
||||
}
|
||||
COMMUNITY,
|
||||
PRODUCT,
|
||||
PREVIEW
|
||||
}
|
||||
|
||||
private static final Profile CURRENT = new Profile();
|
||||
private static Profile CURRENT = new Profile();
|
||||
|
||||
private final ProductValue product;
|
||||
|
||||
private final ProfileValue profile;
|
||||
|
||||
private final Set<Feature> disabledFeatures = new HashSet<>();
|
||||
private final Set<Feature> previewFeatures = new HashSet<>();
|
||||
private final Set<Feature> experimentalFeatures = new HashSet<>();
|
||||
|
||||
private Profile() {
|
||||
Config config = new Config();
|
||||
|
||||
product = "rh-sso".equals(Version.NAME) ? ProductValue.RHSSO : ProductValue.KEYCLOAK;
|
||||
profile = ProfileValue.valueOf(config.getProfile().toUpperCase());
|
||||
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
for (Feature f : Feature.values()) {
|
||||
Boolean enabled = config.getConfig(f);
|
||||
|
||||
String jbossServerConfigDir = System.getProperty("jboss.server.config.dir");
|
||||
if (jbossServerConfigDir != null) {
|
||||
File file = new File(jbossServerConfigDir, "profile.properties");
|
||||
if (file.isFile()) {
|
||||
props.load(new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
|
||||
if (System.getProperties().containsKey("keycloak.profile")) {
|
||||
props.setProperty("profile", System.getProperty("keycloak.profile"));
|
||||
}
|
||||
|
||||
for (String k : System.getProperties().stringPropertyNames()) {
|
||||
if (k.startsWith("keycloak.profile.feature.")) {
|
||||
props.put(k.replace("keycloak.profile.feature.", "feature."), System.getProperty(k));
|
||||
}
|
||||
}
|
||||
|
||||
if (props.containsKey("profile")) {
|
||||
profile = ProfileValue.valueOf(props.getProperty("profile").toUpperCase());
|
||||
} else {
|
||||
profile = ProfileValue.valueOf(Version.DEFAULT_PROFILE.toUpperCase());
|
||||
}
|
||||
|
||||
disabledFeatures.addAll(profile.disabled);
|
||||
disabledFeatures.removeAll(product.excluded);
|
||||
|
||||
for (String k : props.stringPropertyNames()) {
|
||||
if (k.startsWith("feature.")) {
|
||||
Feature f = Feature.valueOf(k.replace("feature.", "").toUpperCase());
|
||||
if (props.get(k).equals("enabled")) {
|
||||
disabledFeatures.remove(f);
|
||||
} else if (props.get(k).equals("disabled")) {
|
||||
switch (f.getType()) {
|
||||
case DEFAULT:
|
||||
if (enabled != null && !enabled) {
|
||||
disabledFeatures.add(f);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DISABLED_BY_DEFAULT:
|
||||
if (enabled == null || !enabled) {
|
||||
disabledFeatures.add(f);
|
||||
}
|
||||
break;
|
||||
case PREVIEW:
|
||||
previewFeatures.add(f);
|
||||
if (enabled == null || !enabled) {
|
||||
disabledFeatures.add(f);
|
||||
} else {
|
||||
logger.info("Preview feature enabled: " + f.name().toLowerCase());
|
||||
}
|
||||
break;
|
||||
case EXPERIMENTAL:
|
||||
experimentalFeatures.add(f);
|
||||
if (enabled == null || !enabled) {
|
||||
disabledFeatures.add(f);
|
||||
} else {
|
||||
logger.warn("Experimental feature enabled: " + f.name().toLowerCase());
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void init() {
|
||||
CURRENT = new Profile();
|
||||
}
|
||||
|
||||
public static String getName() {
|
||||
return CURRENT.profile.name().toLowerCase();
|
||||
}
|
||||
|
@ -129,11 +135,68 @@ public class Profile {
|
|||
return CURRENT.disabledFeatures;
|
||||
}
|
||||
|
||||
public static Set<Feature> getPreviewFeatures() {
|
||||
return CURRENT.previewFeatures;
|
||||
}
|
||||
|
||||
public static Set<Feature> getExperimentalFeatures() {
|
||||
return CURRENT.experimentalFeatures;
|
||||
}
|
||||
|
||||
public static boolean isFeatureEnabled(Feature feature) {
|
||||
if (CURRENT.product.excluded.contains(feature)) {
|
||||
return false;
|
||||
}
|
||||
return !CURRENT.disabledFeatures.contains(feature);
|
||||
}
|
||||
|
||||
private class Config {
|
||||
|
||||
private Properties properties;
|
||||
|
||||
public Config() {
|
||||
properties = new Properties();
|
||||
|
||||
try {
|
||||
String jbossServerConfigDir = System.getProperty("jboss.server.config.dir");
|
||||
if (jbossServerConfigDir != null) {
|
||||
File file = new File(jbossServerConfigDir, "profile.properties");
|
||||
if (file.isFile()) {
|
||||
properties.load(new FileInputStream(file));
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public String getProfile() {
|
||||
String profile = System.getProperty("keycloak.profile");
|
||||
if (profile != null) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
profile = properties.getProperty("profile");
|
||||
if (profile != null) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
return Version.DEFAULT_PROFILE;
|
||||
}
|
||||
|
||||
public Boolean getConfig(Feature feature) {
|
||||
String config = System.getProperty("keycloak.profile.feature." + feature.name().toLowerCase());
|
||||
if (config == null) {
|
||||
config = properties.getProperty("feature." + feature.name().toLowerCase());
|
||||
}
|
||||
|
||||
if (config == null) {
|
||||
return null;
|
||||
} else if (config.equals("enabled")) {
|
||||
return Boolean.TRUE;
|
||||
} else if (config.equals("disabled")) {
|
||||
return Boolean.FALSE;
|
||||
} else {
|
||||
throw new RuntimeException("Invalid value for feature " + config);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
97
common/src/test/java/org/keycloak/common/ProfileTest.java
Normal file
97
common/src/test/java/org/keycloak/common/ProfileTest.java
Normal file
|
@ -0,0 +1,97 @@
|
|||
package org.keycloak.common;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
public class ProfileTest {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void checkDefaults() {
|
||||
Assert.assertEquals("community", Profile.getName());
|
||||
assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ACCOUNT2, Profile.Feature.ACCOUNT_API, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DOCKER, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE);
|
||||
assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ACCOUNT_API, Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE);
|
||||
assertEquals(Profile.getExperimentalFeatures(), Profile.Feature.ACCOUNT2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithSystemProperties() {
|
||||
Assert.assertEquals("community", Profile.getName());
|
||||
Assert.assertFalse(Profile.isFeatureEnabled(Profile.Feature.DOCKER));
|
||||
Assert.assertTrue(Profile.isFeatureEnabled(Profile.Feature.IMPERSONATION));
|
||||
|
||||
System.setProperty("keycloak.profile", "preview");
|
||||
System.setProperty("keycloak.profile.feature.docker", "enabled");
|
||||
System.setProperty("keycloak.profile.feature.impersonation", "disabled");
|
||||
|
||||
Profile.init();
|
||||
|
||||
Assert.assertEquals("preview", Profile.getName());
|
||||
Assert.assertTrue(Profile.isFeatureEnabled(Profile.Feature.DOCKER));
|
||||
Assert.assertFalse(Profile.isFeatureEnabled(Profile.Feature.IMPERSONATION));
|
||||
|
||||
System.getProperties().remove("keycloak.profile");
|
||||
System.getProperties().remove("keycloak.profile.feature.docker");
|
||||
System.getProperties().remove("keycloak.profile.feature.impersonation");
|
||||
|
||||
Profile.init();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configWithPropertiesFile() throws IOException {
|
||||
Assert.assertEquals("community", Profile.getName());
|
||||
Assert.assertFalse(Profile.isFeatureEnabled(Profile.Feature.DOCKER));
|
||||
Assert.assertTrue(Profile.isFeatureEnabled(Profile.Feature.IMPERSONATION));
|
||||
|
||||
File d = temporaryFolder.newFolder();
|
||||
File f = new File(d, "profile.properties");
|
||||
|
||||
Properties p = new Properties();
|
||||
p.setProperty("profile", "preview");
|
||||
p.setProperty("feature.docker", "enabled");
|
||||
p.setProperty("feature.impersonation", "disabled");
|
||||
PrintWriter pw = new PrintWriter(f);
|
||||
p.list(pw);
|
||||
pw.close();
|
||||
|
||||
System.setProperty("jboss.server.config.dir", d.getAbsolutePath());
|
||||
|
||||
Profile.init();
|
||||
|
||||
Assert.assertEquals("preview", Profile.getName());
|
||||
Assert.assertTrue(Profile.isFeatureEnabled(Profile.Feature.DOCKER));
|
||||
Assert.assertFalse(Profile.isFeatureEnabled(Profile.Feature.IMPERSONATION));
|
||||
|
||||
System.getProperties().remove("jboss.server.config.dir");
|
||||
|
||||
Profile.init();
|
||||
}
|
||||
|
||||
public static void assertEquals(Set<Profile.Feature> actual, Profile.Feature... expected) {
|
||||
Profile.Feature[] a = actual.toArray(new Profile.Feature[actual.size()]);
|
||||
Arrays.sort(a, new FeatureComparator());
|
||||
Arrays.sort(expected, new FeatureComparator());
|
||||
Assert.assertArrayEquals(a, expected);
|
||||
}
|
||||
|
||||
private static class FeatureComparator implements Comparator<Profile.Feature> {
|
||||
@Override
|
||||
public int compare(Profile.Feature o1, Profile.Feature o2) {
|
||||
return o1.name().compareTo(o2.name());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -21,6 +21,7 @@ import org.keycloak.common.Profile;
|
|||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -29,15 +30,16 @@ public class ProfileInfoRepresentation {
|
|||
|
||||
private String name;
|
||||
private List<String> disabledFeatures;
|
||||
private List<String> previewFeatures;
|
||||
private List<String> experimentalFeatures;
|
||||
|
||||
public static ProfileInfoRepresentation create() {
|
||||
ProfileInfoRepresentation info = new ProfileInfoRepresentation();
|
||||
|
||||
info.name = Profile.getName();
|
||||
info.disabledFeatures = new LinkedList<>();
|
||||
for (Profile.Feature f : Profile.getDisabledFeatures()) {
|
||||
info.disabledFeatures.add(f.name());
|
||||
}
|
||||
info.disabledFeatures = names(Profile.getDisabledFeatures());
|
||||
info.previewFeatures = names(Profile.getPreviewFeatures());
|
||||
info.experimentalFeatures = names(Profile.getExperimentalFeatures());
|
||||
|
||||
return info;
|
||||
}
|
||||
|
@ -50,4 +52,20 @@ public class ProfileInfoRepresentation {
|
|||
return disabledFeatures;
|
||||
}
|
||||
|
||||
public List<String> getPreviewFeatures() {
|
||||
return previewFeatures;
|
||||
}
|
||||
|
||||
public List<String> getExperimentalFeatures() {
|
||||
return experimentalFeatures;
|
||||
}
|
||||
|
||||
private static List<String> names(Set<Profile.Feature> featureSet) {
|
||||
List<String> l = new LinkedList();
|
||||
for (Profile.Feature f : featureSet) {
|
||||
l.add(f.name());
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
</module>
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<module name="org.bouncycastle" />
|
||||
<module name="javax.api"/>
|
||||
<module name="javax.activation.api"/>
|
||||
<module name="org.jboss.logging"/>
|
||||
<module name="sun.jdk" optional="true" />
|
||||
</dependencies>
|
||||
|
||||
|
|
|
@ -206,7 +206,7 @@ public class AccountRestService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response sessions() {
|
||||
checkAccount2Enabled();
|
||||
checkAccountApiEnabled();
|
||||
List<SessionRepresentation> reps = new LinkedList<>();
|
||||
|
||||
List<UserSessionModel> sessions = session.sessions().getUserSessions(realm, user);
|
||||
|
@ -244,7 +244,7 @@ public class AccountRestService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response sessionsLogout(@QueryParam("current") boolean removeCurrent) {
|
||||
checkAccount2Enabled();
|
||||
checkAccountApiEnabled();
|
||||
UserSessionModel userSession = auth.getSession();
|
||||
|
||||
List<UserSessionModel> userSessions = session.sessions().getUserSessions(realm, user);
|
||||
|
@ -268,7 +268,7 @@ public class AccountRestService {
|
|||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@NoCache
|
||||
public Response sessionLogout(@QueryParam("id") String id) {
|
||||
checkAccount2Enabled();
|
||||
checkAccountApiEnabled();
|
||||
UserSessionModel userSession = session.sessions().getUserSession(realm, id);
|
||||
if (userSession != null && userSession.getUser().equals(user)) {
|
||||
AuthenticationManager.backchannelLogout(session, userSession, true);
|
||||
|
@ -278,7 +278,7 @@ public class AccountRestService {
|
|||
|
||||
@Path("/credentials")
|
||||
public AccountCredentialResource credentials() {
|
||||
checkAccount2Enabled();
|
||||
checkAccountApiEnabled();
|
||||
return new AccountCredentialResource(session, event, user);
|
||||
}
|
||||
|
||||
|
@ -286,8 +286,8 @@ public class AccountRestService {
|
|||
// TODO Applications
|
||||
// TODO Logs
|
||||
|
||||
private static void checkAccount2Enabled() {
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.ACCOUNT2)) {
|
||||
private static void checkAccountApiEnabled() {
|
||||
if (!Profile.isFeatureEnabled(Profile.Feature.ACCOUNT_API)) {
|
||||
throw new NotFoundException();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,7 +44,7 @@ import static org.hamcrest.Matchers.containsInAnyOrder;
|
|||
import static org.junit.Assert.*;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
|
||||
import static org.keycloak.common.Profile.Feature.ACCOUNT2;
|
||||
import static org.keycloak.common.Profile.Feature.ACCOUNT_API;
|
||||
import static org.keycloak.testsuite.ProfileAssume.assumeFeatureEnabled;
|
||||
|
||||
/**
|
||||
|
@ -234,7 +234,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testGetSessions() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT2);
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
List<SessionRepresentation> sessions = SimpleHttp.doGet(getAccountUrl("sessions"), client).auth(tokenUtil.getToken()).asJson(new TypeReference<List<SessionRepresentation>>() {});
|
||||
|
||||
|
@ -243,14 +243,14 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testGetPasswordDetails() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT2);
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
getPasswordDetails();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPostPasswordUpdate() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT2);
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
//Get the time of lastUpdate
|
||||
AccountCredentialResource.PasswordDetails initialDetails = getPasswordDetails();
|
||||
|
@ -275,7 +275,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testPasswordConfirmation() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT2);
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
updatePassword("password", "Str0ng3rP4ssw0rd", "confirmationDoesNotMatch", 400);
|
||||
|
||||
|
@ -318,7 +318,7 @@ public class AccountRestServiceTest extends AbstractTestRealmKeycloakTest {
|
|||
|
||||
@Test
|
||||
public void testDeleteSession() throws IOException {
|
||||
assumeFeatureEnabled(ACCOUNT2);
|
||||
assumeFeatureEnabled(ACCOUNT_API);
|
||||
|
||||
TokenUtil viewToken = new TokenUtil("view-account-access", "password");
|
||||
String sessionId = oauth.doLogin("view-account-access", "password").getSessionState();
|
||||
|
|
|
@ -26,6 +26,7 @@ import org.junit.Rule;
|
|||
import org.junit.Test;
|
||||
import org.keycloak.authentication.authenticators.browser.ScriptBasedAuthenticatorFactory;
|
||||
import org.keycloak.authentication.authenticators.browser.UsernamePasswordFormFactory;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.events.Details;
|
||||
import org.keycloak.events.Errors;
|
||||
import org.keycloak.events.EventType;
|
||||
|
@ -64,6 +65,11 @@ public class ScriptAuthenticatorTest extends AbstractFlowTest {
|
|||
|
||||
public static final String EXECUTION_ID = "scriptAuth";
|
||||
|
||||
@BeforeClass
|
||||
public static void verifyEnvironment() {
|
||||
ProfileAssume.assumeFeatureEnabled(Profile.Feature.SCRIPTS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
||||
|
|
|
@ -1121,11 +1121,17 @@ include-representation.tooltip=Include JSON representation for create and update
|
|||
clear-admin-events.tooltip=Deletes all admin events in the database.
|
||||
server-version=Server Version
|
||||
server-profile=Server Profile
|
||||
server-disabled=Server Disabled Features
|
||||
server-disabled=Disabled Features
|
||||
server-disabled.tooltip=Features that are not currently enabled. Some features are not enabled by default. This applies to all preview and experimental features.
|
||||
server-preview=Preview Features
|
||||
server-preview.tooltip=Preview features are not supported in production use and may be significantly changed or removed in the future.
|
||||
server-experimental=Experimental Features
|
||||
server-experimental.tooltip=Experimental features are experimental features that may not be fully function. Never use experimental features in production.
|
||||
info=Info
|
||||
providers=Providers
|
||||
server-time=Server Time
|
||||
server-uptime=Server Uptime
|
||||
profile=Profile
|
||||
memory=Memory
|
||||
total-memory=Total Memory
|
||||
free-memory=Free Memory
|
||||
|
|
|
@ -14,14 +14,6 @@
|
|||
<td width="20%">{{:: 'server-version' | translate}}</td>
|
||||
<td>{{serverInfo.systemInfo.version}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="20%">{{:: 'server-profile' | translate}}</td>
|
||||
<td>{{serverInfo.profileInfo.name | capitalize}}</td>
|
||||
</tr>
|
||||
<tr data-ng-if="serverInfo.profileInfo.disabledFeatures.length > 0">
|
||||
<td width="20%">{{:: 'server-disabled' | translate}}</td>
|
||||
<td>{{serverInfo.profileInfo.disabledFeatures.join(', ').toLowerCase() | capitalize}}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>{{:: 'server-time' | translate}}</td>
|
||||
<td>{{serverInfo.systemInfo.serverTime}}</td>
|
||||
|
@ -32,6 +24,37 @@
|
|||
</tr>
|
||||
</table>
|
||||
|
||||
<table class="table table-striped table-bordered">
|
||||
<legend>{{:: 'profile' | translate}}</legend>
|
||||
|
||||
<tr>
|
||||
<td width="20%">{{:: 'server-profile' | translate}}</td>
|
||||
<td>{{serverInfo.profileInfo.name | capitalize}}</td>
|
||||
</tr>
|
||||
<tr data-ng-if="serverInfo.profileInfo.disabledFeatures.length > 0">
|
||||
<td width="20%">
|
||||
<span>{{:: 'server-disabled' | translate}}</span>
|
||||
<kc-tooltip>{{:: 'server-disabled.tooltip' | translate}}</kc-tooltip>
|
||||
</td>
|
||||
<td>{{serverInfo.profileInfo.disabledFeatures.sort().join(', ')}}</td>
|
||||
|
||||
</tr>
|
||||
<tr data-ng-if="serverInfo.profileInfo.previewFeatures.length > 0">
|
||||
<td width="20%">
|
||||
<span>{{:: 'server-preview' | translate}}</span>
|
||||
<kc-tooltip>{{:: 'server-preview.tooltip' | translate}}</kc-tooltip>
|
||||
</td>
|
||||
<td>{{serverInfo.profileInfo.previewFeatures.sort().join(', ')}}</td>
|
||||
</tr>
|
||||
<tr data-ng-if="serverInfo.profileInfo.experimentalFeatures.length > 0">
|
||||
<td width="20%">
|
||||
<span>{{:: 'server-experimental' | translate}}</span>
|
||||
<kc-tooltip>{{:: 'server-experimental.tooltip' | translate}}</kc-tooltip>
|
||||
</td>
|
||||
<td>{{serverInfo.profileInfo.experimentalFeatures.sort().join(', ')}}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<fieldset>
|
||||
<legend>{{:: 'memory' | translate}}</legend>
|
||||
<table class="table table-striped table-bordered" style="margin-top: 0;">
|
||||
|
|
Loading…
Reference in a new issue