[KEYCLOAK-18426] - Support required by role and scopes in Admin UI
This commit is contained in:
parent
52ced98f92
commit
faadb896ea
15 changed files with 329 additions and 104 deletions
|
@ -32,6 +32,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
|
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
|
@ -41,6 +42,8 @@ import org.keycloak.component.AmphibianProviderFactory;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.ComponentValidationException;
|
import org.keycloak.component.ComponentValidationException;
|
||||||
import org.keycloak.models.ClientModel;
|
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.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
@ -48,6 +51,7 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
import org.keycloak.protocol.oidc.OIDCLoginProtocol;
|
||||||
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
||||||
import org.keycloak.provider.ProviderConfigProperty;
|
import org.keycloak.provider.ProviderConfigProperty;
|
||||||
|
import org.keycloak.provider.ProviderEvent;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.userprofile.AttributeContext;
|
import org.keycloak.userprofile.AttributeContext;
|
||||||
import org.keycloak.userprofile.AttributeMetadata;
|
import org.keycloak.userprofile.AttributeMetadata;
|
||||||
|
@ -85,7 +89,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
* @param configuredScopes to be evaluated
|
* @param configuredScopes to be evaluated
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private static boolean requestedScopePredicate(AttributeContext context, List<String> configuredScopes) {
|
private static boolean requestedScopePredicate(AttributeContext context, Set<String> configuredScopes) {
|
||||||
KeycloakSession session = context.getSession();
|
KeycloakSession session = context.getSession();
|
||||||
AuthenticationSessionModel authenticationSession = session.getContext().getAuthenticationSession();
|
AuthenticationSessionModel authenticationSession = session.getContext().getAuthenticationSession();
|
||||||
|
|
||||||
|
@ -277,13 +281,13 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
UPAttributePermissions permissions = attrConfig.getPermissions();
|
UPAttributePermissions permissions = attrConfig.getPermissions();
|
||||||
|
|
||||||
if (permissions != null) {
|
if (permissions != null) {
|
||||||
List<String> editRoles = permissions.getEdit();
|
Set<String> editRoles = permissions.getEdit();
|
||||||
|
|
||||||
if (!editRoles.isEmpty()) {
|
if (!editRoles.isEmpty()) {
|
||||||
writeAllowed = ac -> UPConfigUtils.isRoleForContext(ac.getContext(), editRoles);
|
writeAllowed = ac -> UPConfigUtils.isRoleForContext(ac.getContext(), editRoles);
|
||||||
}
|
}
|
||||||
|
|
||||||
List<String> viewRoles = permissions.getView();
|
Set<String> viewRoles = permissions.getView();
|
||||||
|
|
||||||
if (viewRoles.isEmpty()) {
|
if (viewRoles.isEmpty()) {
|
||||||
readAllowed = writeAllowed;
|
readAllowed = writeAllowed;
|
||||||
|
@ -333,7 +337,7 @@ public class DeclarativeUserProfileProvider extends AbstractUserProfileProvider<
|
||||||
}
|
}
|
||||||
|
|
||||||
private Predicate<AttributeContext> createViewAllowedPredicate(Predicate<AttributeContext> canEdit,
|
private Predicate<AttributeContext> createViewAllowedPredicate(Predicate<AttributeContext> canEdit,
|
||||||
List<String> viewRoles) {
|
Set<String> viewRoles) {
|
||||||
return ac -> UPConfigUtils.isRoleForContext(ac.getContext(), viewRoles) || canEdit.test(ac);
|
return ac -> UPConfigUtils.isRoleForContext(ac.getContext(), viewRoles) || canEdit.test(ac);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
package org.keycloak.userprofile.config;
|
package org.keycloak.userprofile.config;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration of permissions for the attribute
|
* Configuration of permissions for the attribute
|
||||||
|
@ -27,22 +27,22 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class UPAttributePermissions {
|
public class UPAttributePermissions {
|
||||||
|
|
||||||
private List<String> view = Collections.emptyList();
|
private Set<String> view = Collections.emptySet();
|
||||||
private List<String> edit = Collections.emptyList();
|
private Set<String> edit = Collections.emptySet();
|
||||||
|
|
||||||
public List<String> getView() {
|
public Set<String> getView() {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setView(List<String> view) {
|
public void setView(Set<String> view) {
|
||||||
this.view = view;
|
this.view = view;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getEdit() {
|
public Set<String> getEdit() {
|
||||||
return edit;
|
return edit;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setEdit(List<String> edit) {
|
public void setEdit(Set<String> edit) {
|
||||||
this.edit = edit;
|
this.edit = edit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.userprofile.config;
|
package org.keycloak.userprofile.config;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
|
|
||||||
|
@ -28,8 +28,8 @@ import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||||
*/
|
*/
|
||||||
public class UPAttributeRequired {
|
public class UPAttributeRequired {
|
||||||
|
|
||||||
private List<String> roles;
|
private Set<String> roles;
|
||||||
private List<String> scopes;
|
private Set<String> scopes;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this config means that the attribute is ALWAYS required.
|
* Check if this config means that the attribute is ALWAYS required.
|
||||||
|
@ -41,19 +41,19 @@ public class UPAttributeRequired {
|
||||||
return (roles == null || roles.isEmpty()) && (scopes == null || scopes.isEmpty());
|
return (roles == null || roles.isEmpty()) && (scopes == null || scopes.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getRoles() {
|
public Set<String> getRoles() {
|
||||||
return roles;
|
return roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRoles(List<String> roles) {
|
public void setRoles(Set<String> roles) {
|
||||||
this.roles = roles;
|
this.roles = roles;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getScopes() {
|
public Set<String> getScopes() {
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScopes(List<String> scopes) {
|
public void setScopes(Set<String> scopes) {
|
||||||
this.scopes = scopes;
|
this.scopes = scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.userprofile.config;
|
package org.keycloak.userprofile.config;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config of the rules when attribute is selected.
|
* Config of the rules when attribute is selected.
|
||||||
|
@ -26,13 +26,13 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class UPAttributeSelector {
|
public class UPAttributeSelector {
|
||||||
|
|
||||||
private List<String> scopes;
|
private Set<String> scopes;
|
||||||
|
|
||||||
public List<String> getScopes() {
|
public Set<String> getScopes() {
|
||||||
return scopes;
|
return scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setScopes(List<String> scopes) {
|
public void setScopes(Set<String> scopes) {
|
||||||
this.scopes = scopes;
|
this.scopes = scopes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,11 @@ import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.userprofile.UserProfileContext;
|
import org.keycloak.userprofile.UserProfileContext;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
import org.keycloak.validate.ValidationResult;
|
import org.keycloak.validate.ValidationResult;
|
||||||
|
@ -127,6 +130,25 @@ public class UPConfigUtils {
|
||||||
}
|
}
|
||||||
if (attributeConfig.getRequired() != null) {
|
if (attributeConfig.getRequired() != null) {
|
||||||
validateRoles(attributeConfig.getRequired().getRoles(), "required.roles", errors, attributeName);
|
validateRoles(attributeConfig.getRequired().getRoles(), "required.roles", errors, attributeName);
|
||||||
|
validateScopes(attributeConfig.getRequired().getScopes(), "required.scopes", attributeName, errors, session);
|
||||||
|
}
|
||||||
|
if (attributeConfig.getSelector() != null) {
|
||||||
|
validateScopes(attributeConfig.getSelector().getScopes(), "selector.scopes", attributeName, errors, session);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateScopes(Set<String> scopes, String propertyName, String attributeName, List<String> errors, KeycloakSession session) {
|
||||||
|
if (scopes == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (String scope : scopes) {
|
||||||
|
RealmModel realm = session.getContext().getRealm();
|
||||||
|
Stream<ClientScopeModel> realmScopes = realm.getClientScopesStream();
|
||||||
|
|
||||||
|
if (!realmScopes.anyMatch(cs -> cs.getName().equals(scope))) {
|
||||||
|
errors.add(new StringBuilder("'").append(propertyName).append("' configuration for attribute '").append(attributeName).append("' contains unsupported scope '").append(scope).append("'").toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,7 +168,7 @@ public class UPConfigUtils {
|
||||||
* @param errors to ass error message into
|
* @param errors to ass error message into
|
||||||
* @param attributeName we are validating for use in erorr messages
|
* @param attributeName we are validating for use in erorr messages
|
||||||
*/
|
*/
|
||||||
private static void validateRoles(List<String> roles, String fieldName, List<String> errors, String attributeName) {
|
private static void validateRoles(Set<String> roles, String fieldName, List<String> errors, String attributeName) {
|
||||||
if (roles != null) {
|
if (roles != null) {
|
||||||
for (String role : roles) {
|
for (String role : roles) {
|
||||||
if (!PSEUDOROLES.contains(role)) {
|
if (!PSEUDOROLES.contains(role)) {
|
||||||
|
@ -223,7 +245,7 @@ public class UPConfigUtils {
|
||||||
* @param roles to be inspected
|
* @param roles to be inspected
|
||||||
* @return true if roles list contains role representing checked context
|
* @return true if roles list contains role representing checked context
|
||||||
*/
|
*/
|
||||||
public static boolean isRoleForContext(UserProfileContext context, List<String> roles) {
|
public static boolean isRoleForContext(UserProfileContext context, Set<String> roles) {
|
||||||
if (roles == null)
|
if (roles == null)
|
||||||
return false;
|
return false;
|
||||||
if (context == UserProfileContext.USER_API)
|
if (context == UserProfileContext.USER_API)
|
||||||
|
|
|
@ -1,13 +1,16 @@
|
||||||
{
|
{
|
||||||
"attributes": [
|
"attributes": [
|
||||||
{
|
{
|
||||||
"name": "username"
|
"name": "username",
|
||||||
|
"displayName": "${username}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "email"
|
"name": "email",
|
||||||
|
"displayName": "${email}"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "firstName",
|
"name": "firstName",
|
||||||
|
"displayName": "${firstName}",
|
||||||
"required": {"roles" : ["user"]},
|
"required": {"roles" : ["user"]},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"view": ["admin", "user"],
|
"view": ["admin", "user"],
|
||||||
|
@ -16,6 +19,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "lastName",
|
"name": "lastName",
|
||||||
|
"displayName": "${lastName}",
|
||||||
"required": {"roles" : ["user"]},
|
"required": {"roles" : ["user"]},
|
||||||
"permissions": {
|
"permissions": {
|
||||||
"view": ["admin", "user"],
|
"view": ["admin", "user"],
|
||||||
|
|
|
@ -126,7 +126,9 @@ public class VerifyProfileTest extends AbstractTestRealmKeycloakTest {
|
||||||
actions.add(action);
|
actions.add(action);
|
||||||
testRealm.setRequiredActions(actions);
|
testRealm.setRequiredActions(actions);
|
||||||
|
|
||||||
testRealm.setClientScopes(Collections.singletonList(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build()));
|
testRealm.setClientScopes(new ArrayList<>());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name(SCOPE_DEPARTMENT).protocol("openid-connect").build());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("profile").protocol("openid-connect").build());
|
||||||
client_scope_default = KeycloakModelUtils.createClient(testRealm, "client-a");
|
client_scope_default = KeycloakModelUtils.createClient(testRealm, "client-a");
|
||||||
client_scope_default.setDefaultClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
|
client_scope_default.setDefaultClientScopes(Collections.singletonList(SCOPE_DEPARTMENT));
|
||||||
client_scope_default.setRedirectUris(Collections.singletonList("*"));
|
client_scope_default.setRedirectUris(Collections.singletonList("*"));
|
||||||
|
|
|
@ -46,6 +46,7 @@ import org.junit.Test;
|
||||||
import org.keycloak.common.Profile;
|
import org.keycloak.common.Profile;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.component.ComponentValidationException;
|
import org.keycloak.component.ComponentValidationException;
|
||||||
|
import org.keycloak.models.ClientScopeModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
@ -61,6 +62,7 @@ import org.keycloak.userprofile.config.DeclarativeUserProfileProvider;
|
||||||
import org.keycloak.userprofile.config.UPAttribute;
|
import org.keycloak.userprofile.config.UPAttribute;
|
||||||
import org.keycloak.userprofile.config.UPAttributePermissions;
|
import org.keycloak.userprofile.config.UPAttributePermissions;
|
||||||
import org.keycloak.userprofile.config.UPAttributeRequired;
|
import org.keycloak.userprofile.config.UPAttributeRequired;
|
||||||
|
import org.keycloak.userprofile.config.UPAttributeSelector;
|
||||||
import org.keycloak.userprofile.config.UPConfig;
|
import org.keycloak.userprofile.config.UPConfig;
|
||||||
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||||
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
import org.keycloak.testsuite.util.KeycloakModelUtils;
|
||||||
|
@ -87,7 +89,9 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
testRealm.setClientScopes(Collections.singletonList(ClientScopeBuilder.create().name("customer").protocol("openid-connect").build()));
|
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());
|
||||||
ClientRepresentation client = KeycloakModelUtils.createClient(testRealm, "client-a");
|
ClientRepresentation client = KeycloakModelUtils.createClient(testRealm, "client-a");
|
||||||
client.setDefaultClientScopes(Collections.singletonList("customer"));
|
client.setDefaultClientScopes(Collections.singletonList("customer"));
|
||||||
KeycloakModelUtils.createClient(testRealm, "client-b");
|
KeycloakModelUtils.createClient(testRealm, "client-b");
|
||||||
|
@ -284,7 +288,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList(ROLE_USER));
|
permissions.setEdit(Collections.singleton(ROLE_USER));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -709,7 +713,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList(ROLE_USER));
|
permissions.setEdit(Collections.singleton(ROLE_USER));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -820,14 +824,12 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
|
|
||||||
UPAttributeRequired requirements = new UPAttributeRequired();
|
UPAttributeRequired requirements = new UPAttributeRequired();
|
||||||
|
|
||||||
List<String> roles = new ArrayList<>();
|
requirements.setRoles(Collections.singleton(ROLE_USER));
|
||||||
roles.add(ROLE_USER);
|
|
||||||
requirements.setRoles(roles);
|
|
||||||
|
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList(ROLE_USER));
|
permissions.setEdit(Collections.singleton(ROLE_USER));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -889,12 +891,12 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
|
|
||||||
UPAttributeRequired requirements = new UPAttributeRequired();
|
UPAttributeRequired requirements = new UPAttributeRequired();
|
||||||
|
|
||||||
requirements.setRoles(Collections.singletonList(ROLE_ADMIN));
|
requirements.setRoles(Collections.singleton(ROLE_ADMIN));
|
||||||
|
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList(UPConfigUtils.ROLE_ADMIN));
|
permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_ADMIN));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -946,7 +948,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList(UPConfigUtils.ROLE_ADMIN));
|
permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_ADMIN));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -994,9 +996,7 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
List<String> roles = new ArrayList<>();
|
permissions.setEdit(Collections.singleton(UPConfigUtils.ROLE_USER));
|
||||||
roles.add(UPConfigUtils.ROLE_USER);
|
|
||||||
permissions.setEdit(roles);
|
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -1039,14 +1039,12 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
|
|
||||||
UPAttributeRequired requirements = new UPAttributeRequired();
|
UPAttributeRequired requirements = new UPAttributeRequired();
|
||||||
|
|
||||||
List<String> scopes = new ArrayList<>();
|
requirements.setScopes(Collections.singleton("client-a"));
|
||||||
scopes.add("client-a");
|
|
||||||
requirements.setScopes(scopes);
|
|
||||||
|
|
||||||
attribute.setRequired(requirements);
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
UPAttributePermissions permissions = new UPAttributePermissions();
|
UPAttributePermissions permissions = new UPAttributePermissions();
|
||||||
permissions.setEdit(Collections.singletonList("user"));
|
permissions.setEdit(Collections.singleton("user"));
|
||||||
attribute.setPermissions(permissions);
|
attribute.setPermissions(permissions);
|
||||||
|
|
||||||
config.addAttribute(attribute);
|
config.addAttribute(attribute);
|
||||||
|
@ -1113,4 +1111,40 @@ public class UserProfileTest extends AbstractUserProfileTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testConfigurationInvalidScope() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UserProfileTest::testConfigurationInvalidScope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void testConfigurationInvalidScope(KeycloakSession session) throws IOException {
|
||||||
|
RealmModel realm = session.getContext().getRealm();
|
||||||
|
DeclarativeUserProfileProvider provider = getDynamicUserProfileProvider(session);
|
||||||
|
ComponentModel component = provider.getComponentModel();
|
||||||
|
|
||||||
|
assertNotNull(component);
|
||||||
|
|
||||||
|
UPConfig config = new UPConfig();
|
||||||
|
UPAttribute attribute = new UPAttribute();
|
||||||
|
|
||||||
|
attribute.setName(ATT_ADDRESS);
|
||||||
|
|
||||||
|
UPAttributeRequired requirements = new UPAttributeRequired();
|
||||||
|
|
||||||
|
requirements.setScopes(Collections.singleton("invalid"));
|
||||||
|
|
||||||
|
attribute.setRequired(requirements);
|
||||||
|
|
||||||
|
attribute.setSelector(new UPAttributeSelector());
|
||||||
|
attribute.getSelector().setScopes(Collections.singleton("invalid"));
|
||||||
|
|
||||||
|
config.addAttribute(attribute);
|
||||||
|
|
||||||
|
try {
|
||||||
|
provider.setConfiguration(JsonSerialization.writeValueAsString(config));
|
||||||
|
Assert.fail("Expected to fail due to invalid client scope");
|
||||||
|
} catch (ComponentValidationException cve) {
|
||||||
|
//ignore
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -35,6 +36,7 @@ import org.keycloak.testsuite.AbstractTestRealmKeycloakTest;
|
||||||
import org.keycloak.testsuite.runonserver.RunOnServer;
|
import org.keycloak.testsuite.runonserver.RunOnServer;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.JsonMappingException;
|
import com.fasterxml.jackson.databind.JsonMappingException;
|
||||||
|
import org.keycloak.testsuite.util.ClientScopeBuilder;
|
||||||
import org.keycloak.userprofile.config.UPAttribute;
|
import org.keycloak.userprofile.config.UPAttribute;
|
||||||
import org.keycloak.userprofile.config.UPAttributePermissions;
|
import org.keycloak.userprofile.config.UPAttributePermissions;
|
||||||
import org.keycloak.userprofile.config.UPAttributeRequired;
|
import org.keycloak.userprofile.config.UPAttributeRequired;
|
||||||
|
@ -51,6 +53,12 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void configureTestRealm(RealmRepresentation testRealm) {
|
public void configureTestRealm(RealmRepresentation testRealm) {
|
||||||
|
testRealm.setClientScopes(new ArrayList<>());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("phone-1-sel").build());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("phone-1").build());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("phone-2-sel").build());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("phone-2").build());
|
||||||
|
testRealm.getClientScopes().add(ClientScopeBuilder.create().name("phone-3-sel").build());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -158,39 +166,51 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateConfiguration_OK() throws IOException {
|
public void validateConfiguration_OK() {
|
||||||
List<String> errors = validate(null, loadValidConfig());
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UPConfigParserTest::validateConfiguration_OK);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateConfiguration_OK(KeycloakSession session) throws IOException {
|
||||||
|
List<String> errors = validate(session, loadValidConfig());
|
||||||
Assert.assertTrue(errors.isEmpty());
|
Assert.assertTrue(errors.isEmpty());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateConfiguration_attributeNameErrors() throws IOException {
|
public void validateConfiguration_attributeNameErrors() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UPConfigParserTest::validateConfiguration_attributeNameErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateConfiguration_attributeNameErrors(KeycloakSession session) throws IOException {
|
||||||
UPConfig config = loadValidConfig();
|
UPConfig config = loadValidConfig();
|
||||||
//we run this test without KeycloakSession so validator configs are not validated here
|
//we run this test without KeycloakSession so validator configs are not validated here
|
||||||
|
|
||||||
UPAttribute attConfig = config.getAttributes().get(1);
|
UPAttribute attConfig = config.getAttributes().get(1);
|
||||||
|
|
||||||
attConfig.setName(null);
|
attConfig.setName(null);
|
||||||
List<String> errors = validate(null, config);
|
List<String> errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
|
|
||||||
attConfig.setName(" ");
|
attConfig.setName(" ");
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
|
|
||||||
// duplicate attribute name
|
// duplicate attribute name
|
||||||
attConfig.setName("firstName");
|
attConfig.setName("firstName");
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
|
|
||||||
// attribute name format error - unallowed character
|
// attribute name format error - unallowed character
|
||||||
attConfig.setName("ema il");
|
attConfig.setName("ema il");
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateConfiguration_attributePermissionsErrors() throws IOException {
|
public void validateConfiguration_attributePermissionsErrors() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UPConfigParserTest::validateConfiguration_attributePermissionsErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateConfiguration_attributePermissionsErrors(KeycloakSession session) throws IOException {
|
||||||
UPConfig config = loadValidConfig();
|
UPConfig config = loadValidConfig();
|
||||||
//we run this test without KeycloakSession so validator configs are not validated here
|
//we run this test without KeycloakSession so validator configs are not validated here
|
||||||
|
|
||||||
|
@ -198,38 +218,41 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
// no permissions configures at all
|
// no permissions configures at all
|
||||||
attConfig.setPermissions(null);
|
attConfig.setPermissions(null);
|
||||||
List<String> errors = validate(null, config);
|
List<String> errors = validate(session, config);
|
||||||
Assert.assertEquals(0, errors.size());
|
Assert.assertEquals(0, errors.size());
|
||||||
|
|
||||||
// no permissions structure fields configured
|
// no permissions structure fields configured
|
||||||
UPAttributePermissions permsConfig = new UPAttributePermissions();
|
UPAttributePermissions permsConfig = new UPAttributePermissions();
|
||||||
attConfig.setPermissions(permsConfig);
|
attConfig.setPermissions(permsConfig);
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertTrue(errors.isEmpty());
|
Assert.assertTrue(errors.isEmpty());
|
||||||
|
|
||||||
// valid if both are present, even empty
|
// valid if both are present, even empty
|
||||||
permsConfig.setEdit(Collections.emptyList());
|
permsConfig.setEdit(Collections.emptySet());
|
||||||
permsConfig.setView(Collections.emptyList());
|
permsConfig.setView(Collections.emptySet());
|
||||||
attConfig.setPermissions(permsConfig);
|
attConfig.setPermissions(permsConfig);
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(0, errors.size());
|
Assert.assertEquals(0, errors.size());
|
||||||
|
|
||||||
List<String> withInvRole = new ArrayList<>();
|
Set<String> withInvRole = Collections.singleton("invalid");
|
||||||
withInvRole.add("invalid");
|
|
||||||
|
|
||||||
// invalid role used for view
|
// invalid role used for view
|
||||||
permsConfig.setView(withInvRole);
|
permsConfig.setView(withInvRole);
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
|
|
||||||
// invalid role used for edit also
|
// invalid role used for edit also
|
||||||
permsConfig.setEdit(withInvRole);
|
permsConfig.setEdit(withInvRole);
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(2, errors.size());
|
Assert.assertEquals(2, errors.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateConfiguration_attributeRequirementsErrors() throws IOException {
|
public void validateConfiguration_attributeRequirementsErrors() {
|
||||||
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UPConfigParserTest::validateConfiguration_attributeRequirementsErrors);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void validateConfiguration_attributeRequirementsErrors(KeycloakSession session) throws IOException {
|
||||||
UPConfig config = loadValidConfig();
|
UPConfig config = loadValidConfig();
|
||||||
//we run this test without KeycloakSession so validator configs are not validated here
|
//we run this test without KeycloakSession so validator configs are not validated here
|
||||||
|
|
||||||
|
@ -237,22 +260,19 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
// it is OK without requirements configures at all
|
// it is OK without requirements configures at all
|
||||||
attConfig.setRequired(null);
|
attConfig.setRequired(null);
|
||||||
List<String> errors = validate(null, config);
|
List<String> errors = validate(session, config);
|
||||||
Assert.assertEquals(0, errors.size());
|
Assert.assertEquals(0, errors.size());
|
||||||
|
|
||||||
// it is OK with empty config as it means ALWAYS required
|
// it is OK with empty config as it means ALWAYS required
|
||||||
UPAttributeRequired reqConfig = new UPAttributeRequired();
|
UPAttributeRequired reqConfig = new UPAttributeRequired();
|
||||||
attConfig.setRequired(reqConfig);
|
attConfig.setRequired(reqConfig);
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(0, errors.size());
|
Assert.assertEquals(0, errors.size());
|
||||||
Assert.assertTrue(reqConfig.isAlways());
|
Assert.assertTrue(reqConfig.isAlways());
|
||||||
|
|
||||||
List<String> withInvRole = new ArrayList<>();
|
|
||||||
withInvRole.add("invalid");
|
|
||||||
|
|
||||||
// invalid role used
|
// invalid role used
|
||||||
reqConfig.setRoles(withInvRole);;
|
reqConfig.setRoles(Collections.singleton("invalid"));
|
||||||
errors = validate(null, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
Assert.assertFalse(reqConfig.isAlways());
|
Assert.assertFalse(reqConfig.isAlways());
|
||||||
|
|
||||||
|
@ -260,7 +280,7 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void validateConfiguration_attributeValidationsErrors() {
|
public void validateConfiguration_attributeValidationsErrors() {
|
||||||
getTestingClient().server().run((RunOnServer) UPConfigParserTest::validateConfiguration_attributeValidationsErrors);
|
getTestingClient().server(TEST_REALM_NAME).run((RunOnServer) UPConfigParserTest::validateConfiguration_attributeValidationsErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void validateConfiguration_attributeValidationsErrors(KeycloakSession session) throws IOException {
|
private static void validateConfiguration_attributeValidationsErrors(KeycloakSession session) throws IOException {
|
||||||
|
@ -288,9 +308,5 @@ public class UPConfigParserTest extends AbstractTestRealmKeycloakTest {
|
||||||
validationConfig.put("length", vc );
|
validationConfig.put("length", vc );
|
||||||
errors = validate(session, config);
|
errors = validate(session, config);
|
||||||
Assert.assertEquals(1, errors.size());
|
Assert.assertEquals(1, errors.size());
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@ package org.keycloak.testsuite.user.profile.config;
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_ADMIN;
|
||||||
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
import static org.keycloak.userprofile.config.UPConfigUtils.ROLE_USER;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
@ -52,14 +53,14 @@ public class UPConfigUtilsTest {
|
||||||
|
|
||||||
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, null));
|
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, null));
|
||||||
|
|
||||||
List<String> roles = new ArrayList<>();
|
Set<String> roles = new HashSet<>();
|
||||||
roles.add(ROLE_ADMIN);
|
roles.add(ROLE_ADMIN);
|
||||||
Assert.assertTrue(UPConfigUtils.isRoleForContext(UserProfileContext.USER_API, roles));
|
Assert.assertTrue(UPConfigUtils.isRoleForContext(UserProfileContext.USER_API, roles));
|
||||||
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, roles));
|
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, roles));
|
||||||
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT_OLD, roles));
|
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT_OLD, roles));
|
||||||
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.UPDATE_PROFILE, roles));
|
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.UPDATE_PROFILE, roles));
|
||||||
|
|
||||||
roles = new ArrayList<>();
|
roles = new HashSet<>();
|
||||||
roles.add(ROLE_USER);
|
roles.add(ROLE_USER);
|
||||||
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.USER_API, roles));
|
Assert.assertFalse(UPConfigUtils.isRoleForContext(UserProfileContext.USER_API, roles));
|
||||||
Assert.assertTrue(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, roles));
|
Assert.assertTrue(UPConfigUtils.isRoleForContext(UserProfileContext.ACCOUNT, roles));
|
||||||
|
|
|
@ -9,8 +9,8 @@
|
||||||
"name":"email ",
|
"name":"email ",
|
||||||
"validations": {
|
"validations": {
|
||||||
"length" : { "max": 255 },
|
"length" : { "max": 255 },
|
||||||
"emailFormat": {},
|
"email": {},
|
||||||
"emailDomainDenyList": {}
|
"not-blank": {}
|
||||||
},
|
},
|
||||||
"required": {
|
"required": {
|
||||||
"roles" : ["user", "admin"]
|
"roles" : ["user", "admin"]
|
||||||
|
@ -44,7 +44,7 @@
|
||||||
"name":"phone",
|
"name":"phone",
|
||||||
"displayName" : "${profile.phone}",
|
"displayName" : "${profile.phone}",
|
||||||
"validations": {
|
"validations": {
|
||||||
"phoneNumberFormatInternational":{}
|
"not-blank":{}
|
||||||
},
|
},
|
||||||
"required": {
|
"required": {
|
||||||
"scopes" : ["phone-1", "phone-2"],
|
"scopes" : ["phone-1", "phone-2"],
|
||||||
|
|
|
@ -1896,8 +1896,14 @@ user.profile.attribute.name=Name
|
||||||
user.profile.attribute.name.tooltip=The name of the attribute.
|
user.profile.attribute.name.tooltip=The name of the attribute.
|
||||||
user.profile.attribute.displayName=Display name
|
user.profile.attribute.displayName=Display name
|
||||||
user.profile.attribute.displayName.tooltip=Display name for the attribute. Supports keys for localized values as well. For example\: ${profile.attribute.phoneNumber}
|
user.profile.attribute.displayName.tooltip=Display name for the attribute. Supports keys for localized values as well. For example\: ${profile.attribute.phoneNumber}
|
||||||
|
user.profile.attribute.selector.scopes=Enabled when scope
|
||||||
|
user.profile.attribute.selector.scopes.tooltip=Set the attribute as enabled only when a set of one or more scopes are requested by clients. This constraint only applies to flows where clients are able to ask for scopes (e.g.: during login or registration).
|
||||||
user.profile.attribute.required=Required
|
user.profile.attribute.required=Required
|
||||||
user.profile.attribute.required.tooltip=Set the attribute as required. If enabled, the attribute must be set by users and administrators. Otherwise, the attribute is optional.
|
user.profile.attribute.required.tooltip=Set the attribute as required. If enabled, the attribute must be set by users and administrators. Otherwise, the attribute is optional.
|
||||||
|
user.profile.attribute.required.roles=Required for roles
|
||||||
|
user.profile.attribute.required.roles.tooltip=Set the attribute as required for specific types of users. If set to 'user', the attribute is required for users. If set to 'admin' the attribute is required only for administrators.
|
||||||
|
user.profile.attribute.required.scopes=Required for scopes
|
||||||
|
user.profile.attribute.required.scopes.tooltip=Set the attribute as required only when a set of one or more scopes are requested by clients. This constraint only applies to flows where clients are able to ask for scopes (e.g.: during login or registration).
|
||||||
user.profile.attribute.permission=Permission
|
user.profile.attribute.permission=Permission
|
||||||
user.profile.attribute.canUserView=Can user view?
|
user.profile.attribute.canUserView=Can user view?
|
||||||
user.profile.attribute.canUserView.tooltip=If enabled, users can view the attribute. Otherwise, users don't have access to the attribute.
|
user.profile.attribute.canUserView.tooltip=If enabled, users can view the attribute. Otherwise, users don't have access to the attribute.
|
||||||
|
|
|
@ -268,7 +268,10 @@ module.config([ '$routeProvider', function($routeProvider) {
|
||||||
},
|
},
|
||||||
realm : function(RealmLoader) {
|
realm : function(RealmLoader) {
|
||||||
return RealmLoader();
|
return RealmLoader();
|
||||||
}
|
},
|
||||||
|
clientScopes : function(ClientScopeListLoader) {
|
||||||
|
return ClientScopeListLoader();
|
||||||
|
},
|
||||||
},
|
},
|
||||||
controller : 'RealmUserProfileCtrl'
|
controller : 'RealmUserProfileCtrl'
|
||||||
})
|
})
|
||||||
|
|
|
@ -1401,7 +1401,7 @@ module.controller('RealmTokenDetailCtrl', function($scope, Realm, realm, $http,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http, $location, $route, UserProfile, Dialog, Notifications, serverInfo) {
|
module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, clientScopes, $http, $location, $route, UserProfile, Dialog, Notifications, serverInfo) {
|
||||||
$scope.realm = realm;
|
$scope.realm = realm;
|
||||||
$scope.validatorProviders = serverInfo.componentTypes['org.keycloak.validate.Validator'];
|
$scope.validatorProviders = serverInfo.componentTypes['org.keycloak.validate.Validator'];
|
||||||
|
|
||||||
|
@ -1420,14 +1420,29 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
|
|
||||||
$scope.showJsonEditor = function() {
|
$scope.showJsonEditor = function() {
|
||||||
$scope.isShowAttributes = false;
|
$scope.isShowAttributes = false;
|
||||||
|
delete $scope.currentAttribute;
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.canViewPermission = {
|
$scope.isRequiredRoles = {
|
||||||
minimumInputLength: 0,
|
minimumInputLength: 0,
|
||||||
delay: 500,
|
delay: 500,
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
|
id: function(e) { return e; },
|
||||||
query: function (query) {
|
query: function (query) {
|
||||||
query.callback({results: ['user', 'admin']});
|
var expectedRoles = ['user', 'admin'];
|
||||||
|
var roles = [];
|
||||||
|
|
||||||
|
if ('' == query.term.trim()) {
|
||||||
|
roles = expectedRoles;
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < expectedRoles.length; i++) {
|
||||||
|
if (expectedRoles[i].indexOf(query.term.trim()) != -1) {
|
||||||
|
roles.push(expectedRoles[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query.callback({results: roles});
|
||||||
},
|
},
|
||||||
formatResult: function(object, container, query) {
|
formatResult: function(object, container, query) {
|
||||||
return object;
|
return object;
|
||||||
|
@ -1437,18 +1452,57 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
$scope.canEditPermission = {
|
$scope.isRequiredScopes = {
|
||||||
minimumInputLength: 0,
|
minimumInputLength: 1,
|
||||||
delay: 500,
|
delay: 500,
|
||||||
allowClear: true,
|
allowClear: true,
|
||||||
query: function (query) {
|
query: function (query) {
|
||||||
query.callback({results: ['user', 'admin']});
|
var scopes = [];
|
||||||
|
|
||||||
|
if ('' == query.term.trim()) {
|
||||||
|
scopes = clientScopes;
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < clientScopes.length; i++) {
|
||||||
|
if (clientScopes[i].name.indexOf(query.term.trim()) != -1) {
|
||||||
|
scopes.push(clientScopes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query.callback({results: scopes});
|
||||||
},
|
},
|
||||||
formatResult: function(object, container, query) {
|
formatResult: function(object, container, query) {
|
||||||
return object;
|
return object.name;
|
||||||
},
|
},
|
||||||
formatSelection: function(object, container, query) {
|
formatSelection: function(object, container, query) {
|
||||||
return object;
|
return object.name;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$scope.selectorByScopeSelect = {
|
||||||
|
minimumInputLength: 1,
|
||||||
|
delay: 500,
|
||||||
|
allowClear: true,
|
||||||
|
query: function (query) {
|
||||||
|
var scopes = [];
|
||||||
|
|
||||||
|
if ('' == query.term.trim()) {
|
||||||
|
scopes = clientScopes;
|
||||||
|
} else {
|
||||||
|
for (var i = 0; i < clientScopes.length; i++) {
|
||||||
|
if (clientScopes[i].name.indexOf(query.term.trim()) != -1) {
|
||||||
|
scopes.push(clientScopes[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
query.callback({results: scopes});
|
||||||
|
},
|
||||||
|
formatResult: function(object, container, query) {
|
||||||
|
return object.name;
|
||||||
|
},
|
||||||
|
formatSelection: function(object, container, query) {
|
||||||
|
return object.name;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1461,6 +1515,13 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
$scope.create = function() {
|
$scope.create = function() {
|
||||||
$scope.isCreate = true;
|
$scope.isCreate = true;
|
||||||
$scope.currentAttribute = {
|
$scope.currentAttribute = {
|
||||||
|
selector: {
|
||||||
|
scopes: []
|
||||||
|
},
|
||||||
|
required: {
|
||||||
|
roles: [],
|
||||||
|
scopes: []
|
||||||
|
},
|
||||||
permissions: {
|
permissions: {
|
||||||
view: [],
|
view: [],
|
||||||
edit: []
|
edit: []
|
||||||
|
@ -1503,6 +1564,35 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (attribute.selector == null) {
|
||||||
|
attribute.selector = {
|
||||||
|
scopes: []
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.required) {
|
||||||
|
if (attribute.required.roles) {
|
||||||
|
$scope.requiredRoles = attribute.required.roles;
|
||||||
|
}
|
||||||
|
if (attribute.required.scopes) {
|
||||||
|
for (var i = 0; i < attribute.required.scopes.length; i++) {
|
||||||
|
$scope.requiredScopes.push({
|
||||||
|
id: attribute.required.scopes[i],
|
||||||
|
name: attribute.required.scopes[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attribute.selector && attribute.selector.scopes) {
|
||||||
|
for (var i = 0; i < attribute.selector.scopes.length; i++) {
|
||||||
|
$scope.selectorByScope.push({
|
||||||
|
id: attribute.selector.scopes[i],
|
||||||
|
name: attribute.selector.scopes[i]
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$scope.isRequired = attribute.required != null;
|
$scope.isRequired = attribute.required != null;
|
||||||
$scope.canUserView = attribute.permissions.view.includes('user');
|
$scope.canUserView = attribute.permissions.view.includes('user');
|
||||||
$scope.canAdminView = attribute.permissions.view.includes('admin');
|
$scope.canAdminView = attribute.permissions.view.includes('admin');
|
||||||
|
@ -1514,27 +1604,33 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
|
|
||||||
$scope.$watch('isRequired', function() {
|
$scope.$watch('isRequired', function() {
|
||||||
if ($scope.isRequired) {
|
if ($scope.isRequired) {
|
||||||
$scope.currentAttribute.required = {};
|
$scope.currentAttribute.required = {
|
||||||
} else {
|
roles: [],
|
||||||
|
scopes: []
|
||||||
|
};
|
||||||
|
} else if ($scope.currentAttribute) {
|
||||||
delete $scope.currentAttribute.required;
|
delete $scope.currentAttribute.required;
|
||||||
}
|
}
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
handlePermission = function(permission, role, allowed) {
|
handlePermission = function(permission, role, allowed) {
|
||||||
let attribute = $scope.currentAttribute;
|
let attribute = $scope.currentAttribute;
|
||||||
let roles = [];
|
|
||||||
|
|
||||||
for (let r of attribute.permissions[permission]) {
|
if (attribute && attribute.permissions) {
|
||||||
if (r != role) {
|
let roles = [];
|
||||||
roles.push(r);
|
|
||||||
|
for (let r of attribute.permissions[permission]) {
|
||||||
|
if (r != role) {
|
||||||
|
roles.push(r);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (allowed) {
|
if (allowed) {
|
||||||
roles.push(role);
|
roles.push(role);
|
||||||
}
|
}
|
||||||
|
|
||||||
attribute.permissions[permission] = roles;
|
attribute.permissions[permission] = roles;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$scope.$watch('canUserView', function() {
|
$scope.$watch('canUserView', function() {
|
||||||
|
@ -1603,8 +1699,24 @@ module.controller('RealmUserProfileCtrl', function($scope, Realm, realm, $http,
|
||||||
$scope.config = JSON.parse($scope.rawConfig);
|
$scope.config = JSON.parse($scope.rawConfig);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($scope.isCreate && $scope.currentAttribute) {
|
if ($scope.currentAttribute) {
|
||||||
$scope.config['attributes'].push($scope.currentAttribute);
|
if ($scope.isRequired) {
|
||||||
|
$scope.currentAttribute.required.roles = $scope.requiredRoles;
|
||||||
|
|
||||||
|
for (var i = 0; i < $scope.requiredScopes.length; i++) {
|
||||||
|
$scope.currentAttribute.required.scopes.push($scope.requiredScopes[i].name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope.currentAttribute.selector = {scopes: []};
|
||||||
|
|
||||||
|
for (var i = 0; i < $scope.selectorByScope.length; i++) {
|
||||||
|
$scope.currentAttribute.selector.scopes.push($scope.selectorByScope[i].name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($scope.isCreate) {
|
||||||
|
$scope.config['attributes'].push($scope.currentAttribute);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
UserProfile.update({realm: realm.realm},
|
UserProfile.update({realm: realm.realm},
|
||||||
|
|
|
@ -85,6 +85,13 @@
|
||||||
type="text" class="form-control"/>
|
type="text" class="form-control"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-md-2 control-label" for="selectorByScopeSelect">{{:: 'user.profile.attribute.selector.scopes' | translate}}</label>
|
||||||
|
<kc-tooltip>{{:: 'user.profile.attribute.selector.scopes.tooltip' | translate}}</kc-tooltip>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="hidden" ui-select2="selectorByScopeSelect" id="selectorByScopeSelect" data-ng-model="selectorByScope" data-placeholder="Select a scope..." multiple/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label class="col-md-2 control-label" for="isRequired">{{:: 'user.profile.attribute.required' | translate}}</label>
|
<label class="col-md-2 control-label" for="isRequired">{{:: 'user.profile.attribute.required' | translate}}</label>
|
||||||
<kc-tooltip>{{:: 'user.profile.attribute.required.tooltip' | translate}}</kc-tooltip>
|
<kc-tooltip>{{:: 'user.profile.attribute.required.tooltip' | translate}}</kc-tooltip>
|
||||||
|
@ -93,6 +100,20 @@
|
||||||
on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
on-text="{{:: 'onText' | translate}}" off-text="{{:: 'offText' | translate}}"/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="form-group" data-ng-show="isRequired">
|
||||||
|
<label class="col-md-2 control-label" for="isRequiredRoles">{{:: 'user.profile.attribute.required.roles' | translate}}</label>
|
||||||
|
<kc-tooltip>{{:: 'user.profile.attribute.required.roles.tooltip' | translate}}</kc-tooltip>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="hidden" ui-select2="isRequiredRoles" id="isRequiredRoles" data-ng-model="requiredRoles" data-placeholder="Select a role..." multiple/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="form-group" data-ng-show="isRequired">
|
||||||
|
<label class="col-md-2 control-label" for="isRequiredScopes">{{:: 'user.profile.attribute.required.scopes' | translate}}</label>
|
||||||
|
<kc-tooltip>{{:: 'user.profile.attribute.required.scopes.tooltip' | translate}}</kc-tooltip>
|
||||||
|
<div class="col-md-6">
|
||||||
|
<input type="hidden" ui-select2="isRequiredScopes" id="isRequiredScopes" data-ng-model="requiredScopes" data-placeholder="Select a scope..." multiple/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<fieldset class="border-top">
|
<fieldset class="border-top">
|
||||||
<legend collapsed><span class="text">{{:: 'user.profile.attribute.permission' | translate}}</span></legend>
|
<legend collapsed><span class="text">{{:: 'user.profile.attribute.permission' | translate}}</span></legend>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
|
|
Loading…
Reference in a new issue