[KEYCLOAK-18427] - Allowing switching to declarative provider
This commit is contained in:
parent
512bcd14f7
commit
948f453e2d
27 changed files with 247 additions and 252 deletions
|
@ -61,8 +61,7 @@ public class Profile {
|
|||
WEB_AUTHN(Type.DEFAULT, Type.PREVIEW),
|
||||
CLIENT_POLICIES(Type.DEFAULT),
|
||||
CIBA(Type.PREVIEW),
|
||||
MAP_STORAGE(Type.EXPERIMENTAL),
|
||||
DECLARATIVE_USER_PROFILE(Type.PREVIEW);
|
||||
MAP_STORAGE(Type.EXPERIMENTAL);
|
||||
|
||||
private final Type typeProject;
|
||||
private final Type typeProduct;
|
||||
|
|
|
@ -21,8 +21,8 @@ public class ProfileTest {
|
|||
@Test
|
||||
public void checkDefaultsKeycloak() {
|
||||
Assert.assertEquals("community", Profile.getName());
|
||||
assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DOCKER, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.UPLOAD_SCRIPTS, Profile.Feature.CIBA, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.CIBA, Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DOCKER, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.UPLOAD_SCRIPTS, Profile.Feature.CIBA, Profile.Feature.MAP_STORAGE);
|
||||
assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.CIBA);
|
||||
assertEquals(Profile.getDeprecatedFeatures(), Profile.Feature.UPLOAD_SCRIPTS);
|
||||
|
||||
Assert.assertTrue(Profile.Feature.WEB_AUTHN.hasDifferentProductType());
|
||||
|
@ -37,8 +37,8 @@ public class ProfileTest {
|
|||
Profile.init();
|
||||
|
||||
Assert.assertEquals("product", Profile.getName());
|
||||
assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DOCKER, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.UPLOAD_SCRIPTS, Profile.Feature.WEB_AUTHN, Profile.Feature.CIBA, Profile.Feature.MAP_STORAGE, Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.WEB_AUTHN, Profile.Feature.CIBA, Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
assertEquals(Profile.getDisabledFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.DOCKER, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.UPLOAD_SCRIPTS, Profile.Feature.WEB_AUTHN, Profile.Feature.CIBA, Profile.Feature.MAP_STORAGE);
|
||||
assertEquals(Profile.getPreviewFeatures(), Profile.Feature.ADMIN_FINE_GRAINED_AUTHZ, Profile.Feature.SCRIPTS, Profile.Feature.TOKEN_EXCHANGE, Profile.Feature.OPENSHIFT_INTEGRATION, Profile.Feature.WEB_AUTHN, Profile.Feature.CIBA);
|
||||
assertEquals(Profile.getDeprecatedFeatures(), Profile.Feature.UPLOAD_SCRIPTS);
|
||||
|
||||
Assert.assertTrue(Profile.Feature.WEB_AUTHN.hasDifferentProductType());
|
||||
|
|
|
@ -138,6 +138,7 @@ import org.keycloak.representations.idm.authorization.ScopeRepresentation;
|
|||
import org.keycloak.storage.UserStorageProvider;
|
||||
import org.keycloak.storage.UserStorageProviderModel;
|
||||
import org.keycloak.storage.federated.UserFederatedStorageProvider;
|
||||
import org.keycloak.userprofile.UserProfileProvider;
|
||||
import org.keycloak.util.JsonSerialization;
|
||||
import org.keycloak.validation.ValidationUtil;
|
||||
|
||||
|
@ -1077,6 +1078,11 @@ public class RepresentationToModel {
|
|||
renameRealm(realm, rep.getRealm());
|
||||
}
|
||||
|
||||
if (!Boolean.parseBoolean(rep.getAttributesOrEmpty().get("userProfileEnabled"))) {
|
||||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
provider.setConfiguration(null);
|
||||
}
|
||||
|
||||
// Import attributes first, so the stuff saved directly on representation (displayName, bruteForce etc) has bigger priority
|
||||
if (rep.getAttributes() != null) {
|
||||
Set<String> attrsToRemove = new HashSet<>(realm.getAttributes().keySet());
|
||||
|
|
|
@ -275,11 +275,8 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
|
|||
List<String> values = EMPTY_VALUE;
|
||||
AttributeMetadata metadata = metadataByAttribute.get(attributeName);
|
||||
|
||||
// if the attribute is not provided and does not have view permission, use the current values
|
||||
// this check makes possible to decide whether or not validation should happen for read-only attributes
|
||||
// when the context does not have access to such attributes
|
||||
if (user != null && !metadata.canView(createAttributeContext(metadata))) {
|
||||
values = user.getAttributes().get(attributeName);
|
||||
if (user != null && isIncludeAttributeIfNotProvided(metadata)) {
|
||||
values = user.getAttributes().getOrDefault(attributeName, EMPTY_VALUE);
|
||||
}
|
||||
|
||||
newAttributes.put(attributeName, values);
|
||||
|
@ -302,6 +299,11 @@ public class DefaultAttributes extends HashMap<String, List<String>> implements
|
|||
return newAttributes;
|
||||
}
|
||||
|
||||
protected boolean isIncludeAttributeIfNotProvided(AttributeMetadata metadata) {
|
||||
// user api expects that attributes are not updated if not provided when in legacy mode
|
||||
return UserProfileContext.USER_API.equals(context);
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Checks whether an attribute is support by the profile configuration and the current context.
|
||||
*
|
||||
|
|
|
@ -286,10 +286,14 @@ public class UserResource {
|
|||
UserProfileProvider provider = session.getProvider(UserProfileProvider.class);
|
||||
UserProfile profile = provider.create(USER_API, user);
|
||||
|
||||
Map<String, List<String>> attributes = profile.getAttributes().getReadable(false);
|
||||
if (rep.getAttributes() != null) {
|
||||
Map<String, List<String>> allowedAttributes = profile.getAttributes().getReadable(false);
|
||||
|
||||
if (!attributes.isEmpty()) {
|
||||
rep.setAttributes(attributes);
|
||||
for (String attributeName : rep.getAttributes().keySet()) {
|
||||
if (!allowedAttributes.containsKey(attributeName)) {
|
||||
rep.getAttributes().remove(attributeName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return rep;
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.userprofile.legacy;
|
||||
package org.keycloak.userprofile;
|
||||
|
||||
import static org.keycloak.userprofile.DefaultAttributes.READ_ONLY_ATTRIBUTE_KEY;
|
||||
import static org.keycloak.userprofile.UserProfileContext.ACCOUNT;
|
||||
|
@ -44,16 +44,6 @@ import org.keycloak.models.KeycloakSessionFactory;
|
|||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.userprofile.AttributeContext;
|
||||
import org.keycloak.userprofile.AttributeValidatorMetadata;
|
||||
import org.keycloak.userprofile.Attributes;
|
||||
import org.keycloak.userprofile.DefaultAttributes;
|
||||
import org.keycloak.userprofile.DefaultUserProfile;
|
||||
import org.keycloak.userprofile.UserProfile;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.userprofile.UserProfileMetadata;
|
||||
import org.keycloak.userprofile.UserProfileProvider;
|
||||
import org.keycloak.userprofile.UserProfileProviderFactory;
|
||||
import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
||||
import org.keycloak.userprofile.validator.BrokeringFederatedUsernameHasValueValidator;
|
||||
import org.keycloak.userprofile.validator.DuplicateEmailValidator;
|
||||
|
@ -79,7 +69,20 @@ public abstract class AbstractUserProfileProvider<U extends UserProfileProvider>
|
|||
KeycloakSession session = c.getSession();
|
||||
KeycloakContext context = session.getContext();
|
||||
RealmModel realm = context.getRealm();
|
||||
return ((c.getContext() == REGISTRATION_PROFILE || c.getContext() == IDP_REVIEW) && !realm.isRegistrationEmailAsUsername()) || realm.isEditUsernameAllowed();
|
||||
|
||||
switch (c.getContext()) {
|
||||
case REGISTRATION_PROFILE:
|
||||
case IDP_REVIEW:
|
||||
return !realm.isRegistrationEmailAsUsername();
|
||||
case ACCOUNT_OLD:
|
||||
case ACCOUNT:
|
||||
case UPDATE_PROFILE:
|
||||
return realm.isEditUsernameAllowed();
|
||||
case USER_API:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public static Pattern getRegexPatternString(String[] builtinReadOnlyAttributes) {
|
|
@ -1,4 +1,4 @@
|
|||
package org.keycloak.userprofile.config;
|
||||
package org.keycloak.userprofile;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
@ -7,6 +7,7 @@ import java.util.Map;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.userprofile.AttributeMetadata;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.DefaultAttributes;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.userprofile.UserProfileMetadata;
|
||||
|
@ -39,4 +40,9 @@ public class DeclarativeAttributes extends DefaultAttributes {
|
|||
|
||||
return attributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isIncludeAttributeIfNotProvided(AttributeMetadata metadata) {
|
||||
return !metadata.canView(createAttributeContext(metadata));
|
||||
}
|
||||
}
|
|
@ -17,7 +17,7 @@
|
|||
*
|
||||
*/
|
||||
|
||||
package org.keycloak.userprofile.config;
|
||||
package org.keycloak.userprofile;
|
||||
|
||||
import static org.keycloak.common.util.ObjectUtil.isBlank;
|
||||
import static org.keycloak.protocol.oidc.TokenManager.getRequestedClientScopes;
|
||||
|
@ -25,8 +25,6 @@ import static org.keycloak.userprofile.config.UPConfigUtils.readConfig;
|
|||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -35,33 +33,28 @@ import java.util.Map;
|
|||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.MultivaluedHashMap;
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.component.AmphibianProviderFactory;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.ClientScopeModel.ClientScopeRemovedEvent;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||
import org.keycloak.provider.ProviderConfigProperty;
|
||||
import org.keycloak.provider.ProviderEvent;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.userprofile.AttributeContext;
|
||||
import org.keycloak.userprofile.AttributeMetadata;
|
||||
import org.keycloak.userprofile.AttributeValidatorMetadata;
|
||||
import org.keycloak.userprofile.Attributes;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.userprofile.UserProfileMetadata;
|
||||
import org.keycloak.userprofile.UserProfileProvider;
|
||||
import org.keycloak.userprofile.legacy.AbstractUserProfileProvider;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileModel;
|
||||
import org.keycloak.userprofile.config.UPAttribute;
|
||||
import org.keycloak.userprofile.config.UPAttributePermissions;
|
||||
import org.keycloak.userprofile.config.UPAttributeRequired;
|
||||
import org.keycloak.userprofile.config.UPAttributeSelector;
|
||||
import org.keycloak.userprofile.config.UPConfig;
|
||||
import org.keycloak.userprofile.config.UPConfigUtils;
|
||||
import org.keycloak.userprofile.validator.AttributeRequiredByMetadataValidator;
|
||||
import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
||||
import org.keycloak.userprofile.validator.ImmutableAttributeValidator;
|
||||
import org.keycloak.validate.AbstractSimpleValidator;
|
||||
import org.keycloak.validate.ValidatorConfig;
|
||||
|
@ -73,12 +66,12 @@ import org.keycloak.validate.ValidatorConfig;
|
|||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<DeclarativeUserProfileProvider>
|
||||
implements AmphibianProviderFactory<DeclarativeUserProfileProvider>, EnvironmentDependentProviderFactory {
|
||||
public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<UserProfileProvider>
|
||||
implements AmphibianProviderFactory<UserProfileProvider> {
|
||||
|
||||
public static final String SYSTEM_DEFAULT_CONFIG_RESOURCE = "keycloak-default-user-profile.json";
|
||||
public static final String ID = "declarative-user-profile";
|
||||
public static final String UP_PIECES_COUNT_COMPONENT_CONFIG_KEY = "config-pieces-count";
|
||||
public static final String REALM_USER_PROFILE_ENABLED = "userProfileEnabled";
|
||||
private static final String PARSED_CONFIG_COMPONENT_KEY = "kc.user.profile.metadata";
|
||||
private static final String UP_PIECE_COMPONENT_CONFIG_KEY_BASE = "config-piece-";
|
||||
|
||||
|
@ -106,7 +99,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
private String defaultRawConfig;
|
||||
|
||||
public DeclarativeUserProfileProvider() {
|
||||
// for reflection
|
||||
defaultRawConfig = UPConfigUtils.readDefaultConfig();
|
||||
}
|
||||
|
||||
public DeclarativeUserProfileProvider(KeycloakSession session, Map<UserProfileContext, UserProfileMetadata> metadataRegistry, String defaultRawConfig) {
|
||||
|
@ -120,18 +113,33 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
}
|
||||
|
||||
@Override
|
||||
protected DeclarativeUserProfileProvider create(KeycloakSession session, Map<UserProfileContext, UserProfileMetadata> metadataRegistry) {
|
||||
protected UserProfileProvider create(KeycloakSession session, Map<UserProfileContext, UserProfileMetadata> metadataRegistry) {
|
||||
return new DeclarativeUserProfileProvider(session, metadataRegistry, defaultRawConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Attributes createAttributes(UserProfileContext context, Map<String, ?> attributes,
|
||||
UserModel user, UserProfileMetadata metadata) {
|
||||
if (!isEnabled(session)) {
|
||||
return new DefaultAttributes(context, attributes, user, metadata, session);
|
||||
}
|
||||
return new DeclarativeAttributes(context, attributes, user, metadata, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata, KeycloakSession session) {
|
||||
UserProfileContext context = metadata.getContext();
|
||||
UserProfileMetadata decoratedMetadata = metadata.clone();
|
||||
|
||||
if (!isEnabled(session)) {
|
||||
if(!context.equals(UserProfileContext.USER_API) && !context.equals(UserProfileContext.REGISTRATION_USER_CREATION)) {
|
||||
decoratedMetadata.addAttribute(UserModel.FIRST_NAME, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(
|
||||
Messages.MISSING_FIRST_NAME))).setAttributeDisplayName("${firstName}");
|
||||
decoratedMetadata.addAttribute(UserModel.LAST_NAME, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_LAST_NAME))).setAttributeDisplayName("${lastName}");
|
||||
return decoratedMetadata;
|
||||
}
|
||||
}
|
||||
|
||||
ComponentModel model = getComponentModelOrCreate(session);
|
||||
Map<UserProfileContext, UserProfileMetadata> metadataMap = model.getNote(PARSED_CONFIG_COMPONENT_KEY);
|
||||
|
||||
|
@ -141,7 +149,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
model.setNote(PARSED_CONFIG_COMPONENT_KEY, metadataMap);
|
||||
}
|
||||
|
||||
return metadataMap.computeIfAbsent(metadata.getContext(), (context) -> decorateUserProfileForCache(metadata, model));
|
||||
return metadataMap.computeIfAbsent(context, (c) -> decorateUserProfileForCache(decoratedMetadata, model));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -175,6 +183,10 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
|
||||
@Override
|
||||
public String getConfiguration() {
|
||||
if (!isEnabled(session)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String cfg = getConfigJsonFromComponentModel(getComponentModel());
|
||||
|
||||
if (isBlank(cfg)) {
|
||||
|
@ -190,6 +202,8 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
|
||||
removeConfigJsonFromComponentModel(component);
|
||||
|
||||
RealmModel realm = session.getContext().getRealm();
|
||||
|
||||
if (!isBlank(configuration)) {
|
||||
// store new parts
|
||||
List<String> parts = UPConfigUtils.getChunks(configuration, 3800);
|
||||
|
@ -202,19 +216,15 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
for (String part : parts) {
|
||||
config.putSingle(UP_PIECE_COMPONENT_CONFIG_KEY_BASE + (i++), part);
|
||||
}
|
||||
}
|
||||
|
||||
session.getContext().getRealm().updateComponent(component);
|
||||
realm.updateComponent(component);
|
||||
} else {
|
||||
realm.removeComponent(component);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
// TODO: We should avoid blocking operations during startup. Need to review this.
|
||||
try (InputStream is = getClass().getResourceAsStream(SYSTEM_DEFAULT_CONFIG_RESOURCE)) {
|
||||
defaultRawConfig = StreamUtil.readString(is, Charset.defaultCharset());
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to load default user profile config file", cause);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -231,23 +241,19 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
* This method is called for each {@link UserProfileContext} in each realm, and metadata are cached then and this
|
||||
* method is called again only if configuration changes.
|
||||
*
|
||||
* @param metadata base to be decorated based on configuration loaded from component model
|
||||
* @param decoratedMetadata base to be decorated based on configuration loaded from component model
|
||||
* @param model component model to get "per realm" configuration from
|
||||
* @return decorated metadata
|
||||
*/
|
||||
protected UserProfileMetadata decorateUserProfileForCache(UserProfileMetadata metadata, ComponentModel model) {
|
||||
UserProfileContext context = metadata.getContext();
|
||||
protected UserProfileMetadata decorateUserProfileForCache(UserProfileMetadata decoratedMetadata, ComponentModel model) {
|
||||
UserProfileContext context = decoratedMetadata.getContext();
|
||||
UPConfig parsedConfig = getParsedConfig(model);
|
||||
|
||||
// do not change config for REGISTRATION_USER_CREATION context, everything important is covered thanks to REGISTRATION_PROFILE
|
||||
if (parsedConfig == null || context == UserProfileContext.REGISTRATION_USER_CREATION) {
|
||||
return metadata;
|
||||
return decoratedMetadata;
|
||||
}
|
||||
|
||||
// need to clone otherwise changes to profile config are going to be reflected
|
||||
// in the default config
|
||||
UserProfileMetadata decoratedMetadata = metadata.clone();
|
||||
|
||||
for (UPAttribute attrConfig : parsedConfig.getAttributes()) {
|
||||
String attributeName = attrConfig.getName();
|
||||
List<AttributeValidatorMetadata> validators = new ArrayList<>();
|
||||
|
@ -425,8 +431,14 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
|||
model.getConfig().remove(UP_PIECES_COUNT_COMPONENT_CONFIG_KEY);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSupported() {
|
||||
return Profile.isFeatureEnabled(Profile.Feature.DECLARATIVE_USER_PROFILE);
|
||||
/**
|
||||
* Returns whether the declarative provider is enabled to a realm
|
||||
*
|
||||
* @deprecated should be removed once {@link DeclarativeUserProfileProvider} becomes the default.
|
||||
* @param session the session
|
||||
* @return {@code true} if the declarative provider is enabled. Otherwise, {@code false}.
|
||||
*/
|
||||
private Boolean isEnabled(KeycloakSession session) {
|
||||
return session.getContext().getRealm().getAttribute(REALM_USER_PROFILE_ENABLED, false);
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@
|
|||
package org.keycloak.userprofile.config;
|
||||
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.UserProfileProvider;
|
||||
|
||||
/**
|
||||
|
|
|
@ -20,6 +20,7 @@ import static org.keycloak.common.util.ObjectUtil.isBlank;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
|
@ -28,6 +29,7 @@ import java.util.Set;
|
|||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
|
@ -45,6 +47,7 @@ import org.keycloak.validate.Validators;
|
|||
*/
|
||||
public class UPConfigUtils {
|
||||
|
||||
private static final String SYSTEM_DEFAULT_CONFIG_RESOURCE = "keycloak-default-user-profile.json";
|
||||
public static final String ROLE_USER = "user";
|
||||
public static final String ROLE_ADMIN = "admin";
|
||||
|
||||
|
@ -260,4 +263,11 @@ public class UPConfigUtils {
|
|||
return str.substring(0, 1).toUpperCase() + str.substring(1);
|
||||
}
|
||||
|
||||
public static String readDefaultConfig() {
|
||||
try (InputStream is = UPConfigUtils.class.getResourceAsStream(SYSTEM_DEFAULT_CONFIG_RESOURCE)) {
|
||||
return StreamUtil.readString(is, Charset.defaultCharset());
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to load default user profile config file", cause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,70 +0,0 @@
|
|||
/*
|
||||
*
|
||||
* * Copyright 2021 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.userprofile.legacy;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.userprofile.AttributeValidatorMetadata;
|
||||
import org.keycloak.userprofile.UserProfileContext;
|
||||
import org.keycloak.userprofile.UserProfileMetadata;
|
||||
import org.keycloak.userprofile.validator.BlankAttributeValidator;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:markus.till@bosch.io">Markus Till</a>
|
||||
*/
|
||||
public class DefaultUserProfileProvider extends AbstractUserProfileProvider<DefaultUserProfileProvider> {
|
||||
|
||||
private static final String PROVIDER_ID = "legacy-user-profile";
|
||||
|
||||
public DefaultUserProfileProvider() {
|
||||
// for reflection
|
||||
}
|
||||
|
||||
public DefaultUserProfileProvider(KeycloakSession session, Map<UserProfileContext, UserProfileMetadata> validators) {
|
||||
super(session, validators);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected DefaultUserProfileProvider create(KeycloakSession session, Map<UserProfileContext, UserProfileMetadata> metadataRegistry) {
|
||||
return new DefaultUserProfileProvider(session, metadataRegistry);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return PROVIDER_ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
protected UserProfileMetadata configureUserProfile(UserProfileMetadata metadata) {
|
||||
UserProfileContext ctx = metadata.getContext();
|
||||
if(ctx != UserProfileContext.USER_API && ctx != UserProfileContext.REGISTRATION_USER_CREATION) {
|
||||
metadata.addAttribute(UserModel.FIRST_NAME, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_FIRST_NAME))).setAttributeDisplayName("${firstName}");
|
||||
metadata.addAttribute(UserModel.LAST_NAME, new AttributeValidatorMetadata(BlankAttributeValidator.ID, BlankAttributeValidator.createConfig(Messages.MISSING_LAST_NAME))).setAttributeDisplayName("${lastName}");
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
}
|
|
@ -16,5 +16,4 @@
|
|||
# * limitations under the License.
|
||||
# */
|
||||
#
|
||||
org.keycloak.userprofile.legacy.DefaultUserProfileProvider
|
||||
org.keycloak.userprofile.config.DeclarativeUserProfileProvider
|
||||
org.keycloak.userprofile.DeclarativeUserProfileProvider
|
|
@ -19,9 +19,6 @@ echo ** Adding max-detail-length to eventsStore spi **
|
|||
|
||||
echo ** Adding spi=userProfile with legacy-user-profile configuration of read-only attributes **
|
||||
/subsystem=keycloak-server/spi=userProfile/:add
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=legacy-user-profile/:add(properties={},enabled=true)
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=legacy-user-profile/:map-put(name=properties,key=read-only-attributes,value=[deniedFoo,deniedBar*,deniedSome/thing,deniedsome*thing])
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=legacy-user-profile/:map-put(name=properties,key=admin-read-only-attributes,value=[deniedSomeAdmin])
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=declarative-user-profile/:add(properties={},enabled=true)
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=declarative-user-profile/:map-put(name=properties,key=read-only-attributes,value=[deniedFoo,deniedBar*,deniedSome/thing,deniedsome*thing])
|
||||
/subsystem=keycloak-server/spi=userProfile/provider=declarative-user-profile/:map-put(name=properties,key=admin-read-only-attributes,value=[deniedSomeAdmin])
|
||||
|
|
|
@ -25,6 +25,3 @@ spi.truststore.file.password=secret
|
|||
|
||||
# http client connection reuse settings
|
||||
spi.connections-http-client.default.reuse-connections=false
|
||||
|
||||
# user profile provider settings
|
||||
spi.user-profile.provider=${keycloak.userProfile.provider:legacy-user-profile}
|
||||
|
|
|
@ -20,50 +20,40 @@
|
|||
package org.keycloak.testsuite.admin.userprofile;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
||||
import static org.keycloak.userprofile.config.UPConfigUtils.readDefaultConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.keycloak.admin.client.resource.UserProfileResource;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.util.StreamUtil;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.testsuite.admin.AbstractAdminTest;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.SetDefaultProvider;
|
||||
import org.keycloak.userprofile.UserProfileSpi;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.config.UPConfigUtils;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE, skipRestart = false)
|
||||
@SetDefaultProvider(spi = UserProfileSpi.ID, providerId = DeclarativeUserProfileProvider.ID,
|
||||
beforeEnableFeature = false,
|
||||
onlyUpdateDefault = true
|
||||
)
|
||||
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
|
||||
public class UserProfileAdminTest extends AbstractAdminTest {
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultConfigIfNoneSet() {
|
||||
String defaultRawConfig;
|
||||
|
||||
try (InputStream is = DeclarativeUserProfileProvider.class.getResourceAsStream(DeclarativeUserProfileProvider.SYSTEM_DEFAULT_CONFIG_RESOURCE)) {
|
||||
defaultRawConfig = StreamUtil.readString(is, Charset.defaultCharset());
|
||||
} catch (IOException cause) {
|
||||
throw new RuntimeException("Failed to load default user profile config file", cause);
|
||||
}
|
||||
|
||||
assertEquals(defaultRawConfig, testRealm().users().userProfile().getConfiguration());
|
||||
assertEquals(readDefaultConfig(), testRealm().users().userProfile().getConfiguration());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -39,6 +39,10 @@ import org.keycloak.testsuite.pages.AppPage.RequestType;
|
|||
import org.keycloak.testsuite.util.*;
|
||||
import javax.mail.internet.MimeMessage;
|
||||
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.hamcrest.Matchers.anyOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.jgroups.util.Util.assertTrue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude.AuthServer;
|
||||
|
@ -261,10 +265,20 @@ public class RegisterTest extends AbstractTestRealmKeycloakTest {
|
|||
registerPage.assertCurrent();
|
||||
|
||||
assertEquals("Please specify username.", registerPage.getInputAccountErrors().getUsernameError());
|
||||
assertEquals("Please specify first name.", registerPage.getInputAccountErrors().getFirstNameError());
|
||||
assertEquals("Please specify last name.", registerPage.getInputAccountErrors().getLastNameError());
|
||||
assertEquals("Please specify email.", registerPage.getInputAccountErrors().getEmailError());
|
||||
assertEquals("Please specify password.", registerPage.getInputPasswordErrors().getPasswordError());
|
||||
assertThat(registerPage.getInputAccountErrors().getFirstNameError(), anyOf(
|
||||
containsString("Please specify first name"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
assertThat(registerPage.getInputAccountErrors().getLastNameError(), anyOf(
|
||||
containsString("Please specify last name"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
assertThat(registerPage.getInputAccountErrors().getEmailError(), anyOf(
|
||||
containsString("Please specify email"),
|
||||
containsString("Please specify this field")
|
||||
));
|
||||
|
||||
assertThat(registerPage.getInputPasswordErrors().getPasswordError(), is("Please specify password."));
|
||||
|
||||
events.expectRegister(null, "registerUserMissingUsername@email")
|
||||
.removeDetail(Details.USERNAME)
|
||||
|
|
|
@ -17,9 +17,11 @@
|
|||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
|
@ -29,15 +31,12 @@ import org.junit.Assert;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.representations.idm.UserRepresentation;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.SetDefaultProvider;
|
||||
import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
|
@ -47,19 +46,12 @@ import org.keycloak.testsuite.pages.VerifyEmailPage;
|
|||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||
import org.keycloak.testsuite.util.GreenMailRule;
|
||||
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||
import org.keycloak.userprofile.UserProfileSpi;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
* @author Stan Silvert ssilvert@redhat.com (C) 2016 Red Hat Inc.
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE, skipRestart = false)
|
||||
@SetDefaultProvider(spi = UserProfileSpi.ID, providerId = DeclarativeUserProfileProvider.ID,
|
||||
beforeEnableFeature = false,
|
||||
onlyUpdateDefault = true
|
||||
)
|
||||
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
|
||||
public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
|
@ -110,11 +102,14 @@ public class RegisterWithUserProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
client_scope_optional = KeycloakModelUtils.createClient(testRealm, "client-b");
|
||||
client_scope_optional.setOptionalClientScopes(scopes);
|
||||
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
|
||||
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRregisterUserSuccess_lastNameOptional() {
|
||||
public void testRegisterUserSuccess_lastNameOptional() {
|
||||
setUserProfileConfiguration("{\"attributes\": ["
|
||||
+ UP_CONFIG_BASIC_ATTRIBUTES
|
||||
+ "{\"name\": \"firstName\"," + VerifyProfileTest.PERMISSIONS_ALL + ", \"required\": {}},"
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package org.keycloak.testsuite.forms;
|
||||
|
||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
public class UserProfileRegisterTest extends RegisterTest {
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
super.configureTestRealm(testRealm);
|
||||
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
}
|
|
@ -19,9 +19,11 @@ package org.keycloak.testsuite.forms;
|
|||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
|
@ -33,7 +35,6 @@ import org.junit.Assert;
|
|||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.OAuth2Constants;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.events.EventType;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.ClientRepresentation;
|
||||
|
@ -43,8 +44,6 @@ import org.keycloak.representations.idm.UserRepresentation;
|
|||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.testsuite.AssertEvents;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.SetDefaultProvider;
|
||||
import org.keycloak.testsuite.pages.AppPage;
|
||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||
import org.keycloak.testsuite.pages.LoginPage;
|
||||
|
@ -54,16 +53,10 @@ import org.keycloak.testsuite.util.KeycloakModelUtils;
|
|||
import org.keycloak.testsuite.util.OAuthClient;
|
||||
import org.keycloak.testsuite.util.RealmBuilder;
|
||||
import org.keycloak.testsuite.util.UserBuilder;
|
||||
import org.keycloak.userprofile.UserProfileSpi;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||
|
||||
/**
|
||||
* @author Vlastimil Elias <velias@redhat.com>
|
||||
*/
|
||||
@EnableFeature(value = Profile.Feature.DECLARATIVE_USER_PROFILE, skipRestart = false)
|
||||
@SetDefaultProvider(spi = UserProfileSpi.ID, providerId = DeclarativeUserProfileProvider.ID,
|
||||
beforeEnableFeature = false,
|
||||
onlyUpdateDefault = true)
|
||||
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
|
||||
public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||
|
||||
|
@ -135,6 +128,10 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
|||
client_scope_optional = KeycloakModelUtils.createClient(testRealm, "client-b");
|
||||
client_scope_optional.setOptionalClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
|
||||
client_scope_optional.setRedirectUris(Collections.singletonList("*"));
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
|
||||
@Rule
|
||||
|
|
|
@ -19,6 +19,9 @@
|
|||
|
||||
package org.keycloak.testsuite.user.profile;
|
||||
|
||||
import static org.keycloak.userprofile.DeclarativeUserProfileProvider.REALM_USER_PROFILE_ENABLED;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
@ -27,10 +30,11 @@ import org.keycloak.models.ClientModel;
|
|||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||
import org.keycloak.sessions.RootAuthenticationSessionModel;
|
||||
import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.UserProfileProvider;
|
||||
|
||||
/**
|
||||
|
@ -233,4 +237,12 @@ public abstract class AbstractUserProfileTest extends AbstractTestRealmKeycloakT
|
|||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
if (testRealm.getAttributes() == null) {
|
||||
testRealm.setAttributes(new HashMap<>());
|
||||
}
|
||||
testRealm.getAttributes().put(REALM_USER_PROFILE_ENABLED, Boolean.TRUE.toString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -43,10 +43,8 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
import org.keycloak.component.ComponentValidationException;
|
||||
import org.keycloak.models.ClientScopeModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.RealmModel;
|
||||
import org.keycloak.models.UserModel;
|
||||
|
@ -54,11 +52,8 @@ import org.keycloak.representations.idm.ClientRepresentation;
|
|||
import org.keycloak.representations.idm.RealmRepresentation;
|
||||
import org.keycloak.services.messages.Messages;
|
||||
import org.keycloak.testsuite.arquillian.annotation.AuthServerContainerExclude;
|
||||
import org.keycloak.testsuite.arquillian.annotation.EnableFeature;
|
||||
import org.keycloak.testsuite.arquillian.annotation.SetDefaultProvider;
|
||||
import org.keycloak.testsuite.runonserver.RunOnServer;
|
||||
import org.keycloak.userprofile.UserProfileSpi;
|
||||
import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.DeclarativeUserProfileProvider;
|
||||
import org.keycloak.userprofile.config.UPAttribute;
|
||||
import org.keycloak.userprofile.config.UPAttributePermissions;
|
||||
import org.keycloak.userprofile.config.UPAttributeRequired;
|
||||
|
@ -80,15 +75,12 @@ import org.keycloak.validate.validators.LengthValidator;
|
|||
/**
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*/
|
||||
@EnableFeature(Profile.Feature.DECLARATIVE_USER_PROFILE)
|
||||
@SetDefaultProvider(spi = UserProfileSpi.ID, providerId = DeclarativeUserProfileProvider.ID,
|
||||
beforeEnableFeature = false,
|
||||
onlyUpdateDefault = true)
|
||||
@AuthServerContainerExclude(AuthServerContainerExclude.AuthServer.REMOTE)
|
||||
public class UserProfileTest extends AbstractUserProfileTest {
|
||||
|
||||
@Override
|
||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||
super.configureTestRealm(testRealm);
|
||||
testRealm.setClientScopes(new ArrayList<>());
|
||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("customer").protocol("openid-connect").build());
|
||||
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("client-a").protocol("openid-connect").build());
|
||||
|
|
|
@ -220,10 +220,6 @@
|
|||
|
||||
"userProfile": {
|
||||
"provider": "${keycloak.userProfile.provider:}",
|
||||
"legacy-user-profile": {
|
||||
"read-only-attributes": [ "deniedFoo", "deniedBar*", "deniedSome/thing", "deniedsome*thing" ],
|
||||
"admin-read-only-attributes": [ "deniedSomeAdmin" ]
|
||||
},
|
||||
"declarative-user-profile": {
|
||||
"read-only-attributes": [ "deniedFoo", "deniedBar*", "deniedSome/thing", "deniedsome*thing" ],
|
||||
"admin-read-only-attributes": [ "deniedSomeAdmin" ]
|
||||
|
|
|
@ -140,10 +140,6 @@
|
|||
|
||||
"userProfile": {
|
||||
"provider": "${keycloak.userProfile.provider:}",
|
||||
"legacy-user-profile": {
|
||||
"read-only-attributes": [ "deniedFoo", "deniedBar*", "deniedSome/thing", "deniedsome*thing" ],
|
||||
"admin-read-only-attributes": [ "deniedSomeAdmin" ]
|
||||
},
|
||||
"declarative-user-profile": {
|
||||
"read-only-attributes": [ "deniedFoo", "deniedBar*", "deniedSome/thing", "deniedsome*thing" ],
|
||||
"admin-read-only-attributes": [ "deniedSomeAdmin" ]
|
||||
|
|
|
@ -32,6 +32,8 @@ realm-detail.protocol-endpoints.tooltip=Shows the configuration of the protocol
|
|||
realm-detail.protocol-endpoints.oidc=OpenID Endpoint Configuration
|
||||
realm-detail.protocol-endpoints.saml=SAML 2.0 Identity Provider Metadata
|
||||
realm-detail.userManagedAccess.tooltip=If enabled, users are allowed to manage their resources and permissions using the Account Management Console.
|
||||
userProfileEnabled=User Profile Enabled
|
||||
userProfileEnabled.tooltip=If enabled, allows managing user profiles.
|
||||
userManagedAccess=User-Managed Access
|
||||
registrationAllowed=User registration
|
||||
registrationAllowed.tooltip=Enable/disable the registration page. A link for registration will show on login page too.
|
||||
|
|
|
@ -280,8 +280,10 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
|
|||
}
|
||||
}
|
||||
$scope.realm = angular.copy(realm);
|
||||
$scope.realm.attributes['userProfileEnabled'] = $scope.realm.attributes['userProfileEnabled'] == 'true';
|
||||
|
||||
var oldCopy = angular.copy($scope.realm);
|
||||
$scope.realmCopy = oldCopy;
|
||||
|
||||
$scope.changed = $scope.create;
|
||||
|
||||
|
@ -309,6 +311,7 @@ module.controller('RealmDetailCtrl', function($scope, Current, Realm, realm, ser
|
|||
if (Current.realms[i].realm == realmCopy.realm) {
|
||||
Current.realm = Current.realms[i];
|
||||
oldCopy = angular.copy($scope.realm);
|
||||
$scope.realmCopy = oldCopy;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -55,6 +55,14 @@
|
|||
<kc-tooltip>{{:: 'realm-detail.userManagedAccess.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label" for="userProfileEnabled">{{:: 'userProfileEnabled' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
<input ng-model="realm.attributes['userProfileEnabled']" name="userProfileEnabled" id="userProfileEnabled" onoffswitch on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}" />
|
||||
</div>
|
||||
<kc-tooltip>{{:: 'userProfileEnabled.tooltip' | translate}}</kc-tooltip>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-md-2 control-label">{{:: 'endpoints' | translate}}</label>
|
||||
<div class="col-md-6">
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
<a href="#/realms/{{realm.realm}}/client-policies/profiles">{{:: 'realm-tab-client-policies' | translate}}</a>
|
||||
</li>
|
||||
<li ng-class="{active: path[2] == 'defense'}" data-ng-show="access.viewRealm"><a href="#/realms/{{realm.realm}}/defense/headers">{{:: 'realm-tab-security-defenses' | translate}}</a></li>
|
||||
<li ng-class="{active: path[2] == 'user-profile'}" data-ng-show="access.viewRealm && serverInfo.featureEnabled('DECLARATIVE_USER_PROFILE')"><a href="#/realms/{{realm.realm}}/user-profile">{{:: 'realm-tab-user-profile' | translate}}</a></li>
|
||||
<li ng-class="{active: path[2] == 'user-profile'}" data-ng-show="access.viewRealm && (realm.attributes['userProfileEnabled'] == true || realm.attributes['userProfileEnabled'] == 'true')"><a href="#/realms/{{realm.realm}}/user-profile">{{:: 'realm-tab-user-profile' | translate}}</a></li>
|
||||
</ul>
|
||||
</div>
|
Loading…
Reference in a new issue