KEYCLOAK-14536 Migrate UserModel fields to attributes
- In order to make lastName/firstName/email/username field configurable in profile we need to store it as an attribute - Keep database as is for now (no impact on performance, schema) - Keep field names and getters and setters (no impact on FTL files) Fix tests with logic changes - PolicyEvaluationTest: We need to take new user attributes into account - UserTest: We need to take into account new user attributes Potential impact on users: - When subclassing UserModel, consistency issues may occur since one can now set e.g. username via setSingleAttribute also - When using PolicyEvaluations, the number of attributes has changed
This commit is contained in:
parent
8a31c331f5
commit
05b6ef8327
27 changed files with 412 additions and 248 deletions
|
@ -29,7 +29,10 @@ import org.keycloak.storage.ldap.idm.query.EscapeStrategy;
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.EqualCondition;
|
import org.keycloak.storage.ldap.idm.query.internal.EqualCondition;
|
||||||
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
import org.keycloak.storage.ldap.idm.query.internal.LDAPQuery;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,7 +99,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName != null? firstName : super.getFirstName();
|
return firstName != null ? firstName : super.getFirstName();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -105,19 +108,61 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFirstName(String firstName) {
|
public List<String> getAttribute(String name) {
|
||||||
this.firstName = firstName;
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
setFullNameToLDAPObject();
|
return firstName != null ? Collections.singletonList(firstName) : super.getAttribute(name);
|
||||||
super.setFirstName(firstName);
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return lastName != null ? Collections.singletonList(lastName) : super.getAttribute(name);
|
||||||
|
}
|
||||||
|
return super.getAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLastName(String lastName) {
|
public String getFirstAttribute(String name) {
|
||||||
this.lastName = lastName;
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
setFullNameToLDAPObject();
|
return firstName != null ? firstName : super.getFirstAttribute(name);
|
||||||
super.setLastName(lastName);
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return lastName != null ? lastName : super.getFirstAttribute(name);
|
||||||
|
}
|
||||||
|
return super.getFirstAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
this.firstName = value;
|
||||||
|
setFullNameToLDAPObject();
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
this.lastName = value;
|
||||||
|
setFullNameToLDAPObject();
|
||||||
|
}
|
||||||
|
super.setSingleAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
this.firstName = values.get(0);
|
||||||
|
setFullNameToLDAPObject();
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
this.lastName = values.get(0);
|
||||||
|
setFullNameToLDAPObject();
|
||||||
|
}
|
||||||
|
super.setSingleAttribute(name, values.get(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, List<String>> getAttributes() {
|
||||||
|
Map<String, List<String>> attributes = delegate.getAttributes();
|
||||||
|
if (firstName != null) {
|
||||||
|
attributes.put(UserModel.FIRST_NAME, Collections.singletonList(firstName));
|
||||||
|
} else if (lastName != null) {
|
||||||
|
attributes.put(UserModel.FIRST_NAME, Collections.singletonList(lastName));
|
||||||
|
}
|
||||||
|
return attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void setFullNameToLDAPObject() {
|
private void setFullNameToLDAPObject() {
|
||||||
String fullName = getFullNameForWriteToLDAP(getFirstName(), getLastName(), getUsername());
|
String fullName = getFullNameForWriteToLDAP(getFirstName(), getLastName(), getUsername());
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
|
@ -130,7 +175,6 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
String ldapFullNameAttrName = getLdapFullNameAttrName();
|
String ldapFullNameAttrName = getLdapFullNameAttrName();
|
||||||
ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName);
|
ldapUser.setSingleAttribute(ldapFullNameAttrName, fullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return txDelegate;
|
return txDelegate;
|
||||||
|
@ -183,7 +227,7 @@ public class FullNameLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
EscapeStrategy escapeStrategy = firstNameCondition!=null ? firstNameCondition.getEscapeStrategy() : lastNameCondition.getEscapeStrategy();
|
EscapeStrategy escapeStrategy = firstNameCondition != null ? firstNameCondition.getEscapeStrategy() : lastNameCondition.getEscapeStrategy();
|
||||||
|
|
||||||
EqualCondition fullNameCondition = new EqualCondition(ldapFullNameAttrName, fullName, escapeStrategy);
|
EqualCondition fullNameCondition = new EqualCondition(ldapFullNameAttrName, fullName, escapeStrategy);
|
||||||
query.addWhereCondition(fullNameCondition);
|
query.addWhereCondition(fullNameCondition);
|
||||||
|
|
|
@ -191,6 +191,11 @@ public class UserAttributeLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSingleAttribute(String name, String value) {
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
checkDuplicateUsername(userModelAttrName, value, realm, ldapProvider.getSession(), this);
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
checkDuplicateEmail(userModelAttrName, value, realm, ldapProvider.getSession(), this);
|
||||||
|
}
|
||||||
if (setLDAPAttribute(name, value)) {
|
if (setLDAPAttribute(name, value)) {
|
||||||
super.setSingleAttribute(name, value);
|
super.setSingleAttribute(name, value);
|
||||||
}
|
}
|
||||||
|
@ -198,6 +203,11 @@ public class UserAttributeLDAPStorageMapper extends AbstractLDAPStorageMapper {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(String name, List<String> values) {
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
checkDuplicateUsername(userModelAttrName, values.get(0), realm, ldapProvider.getSession(), this);
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
checkDuplicateEmail(userModelAttrName, values.get(0), realm, ldapProvider.getSession(), this);
|
||||||
|
}
|
||||||
if (setLDAPAttribute(name, values)) {
|
if (setLDAPAttribute(name, values)) {
|
||||||
super.setAttribute(name, values);
|
super.setAttribute(name, values);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,8 @@ import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.utils.UserModelDelegate;
|
import org.keycloak.models.utils.UserModelDelegate;
|
||||||
import org.keycloak.storage.ReadOnlyException;
|
import org.keycloak.storage.ReadOnlyException;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Readonly proxy for a SSSD UserModel that prevents attributes from being updated.
|
* Readonly proxy for a SSSD UserModel that prevents attributes from being updated.
|
||||||
*
|
*
|
||||||
|
@ -52,6 +54,24 @@ public class ReadonlySSSDUserModelDelegate extends UserModelDelegate implements
|
||||||
throw new ReadOnlyException("Federated storage is not writable");
|
throw new ReadOnlyException("Federated storage is not writable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
setSpecialAttributesToReadonly(name);
|
||||||
|
super.setSingleAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setAttribute(String name, List<String> value) {
|
||||||
|
setSpecialAttributesToReadonly(name);
|
||||||
|
super.setAttribute(name, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSpecialAttributesToReadonly(String name) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name) || UserModel.LAST_NAME.equals(name) || UserModel.EMAIL.equals(name) || USERNAME.equals(name)) {
|
||||||
|
throw new ReadOnlyException("Federated storage is not writable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
throw new ReadOnlyException("Federated storage is not writable");
|
throw new ReadOnlyException("Federated storage is not writable");
|
||||||
|
|
|
@ -59,6 +59,37 @@ public class UserAdapter implements CachedUserModel {
|
||||||
this.modelSupplier = this::getUserModel;
|
this.modelSupplier = this::getUserModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstName() {
|
||||||
|
return getFirstAttribute(FIRST_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
setSingleAttribute(FIRST_NAME, firstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLastName() {
|
||||||
|
return getFirstAttribute(LAST_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
setSingleAttribute(LAST_NAME, lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return getFirstAttribute(EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
email = email == null ? null : email.toLowerCase();
|
||||||
|
setSingleAttribute(EMAIL, email);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UserModel getDelegateForUpdate() {
|
public UserModel getDelegateForUpdate() {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
|
@ -101,15 +132,13 @@ public class UserAdapter implements CachedUserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
if (updated != null) return updated.getUsername();
|
return getFirstAttribute(UserModel.USERNAME);
|
||||||
return cached.getUsername();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
getDelegateForUpdate();
|
username = username==null ? null : username.toLowerCase();
|
||||||
username = KeycloakModelUtils.toLowerCaseSafe(username);
|
setSingleAttribute(UserModel.USERNAME, username);
|
||||||
updated.setUsername(username);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -202,43 +231,6 @@ public class UserAdapter implements CachedUserModel {
|
||||||
updated.removeRequiredAction(action);
|
updated.removeRequiredAction(action);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFirstName() {
|
|
||||||
if (updated != null) return updated.getFirstName();
|
|
||||||
return cached.getFirstName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFirstName(String firstName) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
updated.setFirstName(firstName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLastName() {
|
|
||||||
if (updated != null) return updated.getLastName();
|
|
||||||
return cached.getLastName();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
updated.setLastName(lastName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEmail() {
|
|
||||||
if (updated != null) return updated.getEmail();
|
|
||||||
return cached.getEmail();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEmail(String email) {
|
|
||||||
getDelegateForUpdate();
|
|
||||||
email = KeycloakModelUtils.toLowerCaseSafe(email);
|
|
||||||
updated.setEmail(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmailVerified() {
|
public boolean isEmailVerified() {
|
||||||
if (updated != null) return updated.isEmailVerified();
|
if (updated != null) return updated.isEmailVerified();
|
||||||
|
|
|
@ -40,8 +40,6 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
private final String realm;
|
private final String realm;
|
||||||
private final String username;
|
private final String username;
|
||||||
private final Long createdTimestamp;
|
private final Long createdTimestamp;
|
||||||
private final String firstName;
|
|
||||||
private final String lastName;
|
|
||||||
private final String email;
|
private final String email;
|
||||||
private final boolean emailVerified;
|
private final boolean emailVerified;
|
||||||
private final boolean enabled;
|
private final boolean enabled;
|
||||||
|
@ -58,8 +56,6 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
this.realm = realm.getId();
|
this.realm = realm.getId();
|
||||||
this.username = user.getUsername();
|
this.username = user.getUsername();
|
||||||
this.createdTimestamp = user.getCreatedTimestamp();
|
this.createdTimestamp = user.getCreatedTimestamp();
|
||||||
this.firstName = user.getFirstName();
|
|
||||||
this.lastName = user.getLastName();
|
|
||||||
this.email = user.getEmail();
|
this.email = user.getEmail();
|
||||||
this.emailVerified = user.isEmailVerified();
|
this.emailVerified = user.isEmailVerified();
|
||||||
this.enabled = user.isEnabled();
|
this.enabled = user.isEnabled();
|
||||||
|
@ -84,14 +80,6 @@ public class CachedUser extends AbstractExtendableRevisioned implements InRealm
|
||||||
return createdTimestamp;
|
return createdTimestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
|
||||||
return firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLastName() {
|
|
||||||
return lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return email;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import org.keycloak.storage.UserStorageProvider;
|
||||||
import org.keycloak.storage.client.ClientStorageProvider;
|
import org.keycloak.storage.client.ClientStorageProvider;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
import javax.persistence.Query;
|
||||||
import javax.persistence.TypedQuery;
|
import javax.persistence.TypedQuery;
|
||||||
import javax.persistence.criteria.CriteriaBuilder;
|
import javax.persistence.criteria.CriteriaBuilder;
|
||||||
import javax.persistence.criteria.CriteriaQuery;
|
import javax.persistence.criteria.CriteriaQuery;
|
||||||
|
@ -852,22 +853,22 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
|
||||||
case UserModel.SEARCH:
|
case UserModel.SEARCH:
|
||||||
List<Predicate> orPredicates = new ArrayList();
|
List<Predicate> orPredicates = new ArrayList();
|
||||||
|
|
||||||
orPredicates.add(builder.like(builder.lower(root.get(UserModel.USERNAME)), "%" + value.toLowerCase() + "%"));
|
orPredicates.add(builder.like(builder.lower(root.get(USERNAME)), "%" + value.toLowerCase() + "%"));
|
||||||
orPredicates.add(builder.like(builder.lower(root.get(UserModel.EMAIL)), "%" + value.toLowerCase() + "%"));
|
orPredicates.add(builder.like(builder.lower(root.get(EMAIL)), "%" + value.toLowerCase() + "%"));
|
||||||
orPredicates.add(builder.like(
|
orPredicates.add(builder.like(
|
||||||
builder.lower(builder.concat(builder.concat(
|
builder.lower(builder.concat(builder.concat(
|
||||||
builder.coalesce(root.get(UserModel.FIRST_NAME), builder.literal("")), " "),
|
builder.coalesce(root.get(FIRST_NAME), builder.literal("")), " "),
|
||||||
builder.coalesce(root.get(UserModel.LAST_NAME), builder.literal("")))),
|
builder.coalesce(root.get(LAST_NAME), builder.literal("")))),
|
||||||
"%" + value.toLowerCase() + "%"));
|
"%" + value.toLowerCase() + "%"));
|
||||||
|
|
||||||
predicates.add(builder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
|
predicates.add(builder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case UserModel.USERNAME:
|
case USERNAME:
|
||||||
case UserModel.FIRST_NAME:
|
case FIRST_NAME:
|
||||||
case UserModel.LAST_NAME:
|
case LAST_NAME:
|
||||||
case UserModel.EMAIL:
|
case EMAIL:
|
||||||
if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
|
if (Boolean.valueOf(attributes.getOrDefault(UserModel.EXACT, Boolean.FALSE.toString()))) {
|
||||||
predicates.add(builder.equal(builder.lower(root.get(key)), value.toLowerCase()));
|
predicates.add(builder.equal(builder.lower(root.get(key)), value.toLowerCase()));
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import javax.persistence.criteria.Predicate;
|
||||||
import javax.persistence.criteria.Root;
|
import javax.persistence.criteria.Root;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashSet;
|
import java.util.LinkedHashSet;
|
||||||
|
@ -115,6 +116,20 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSingleAttribute(String name, String value) {
|
public void setSingleAttribute(String name, String value) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
user.setFirstName(value);
|
||||||
|
return;
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
user.setLastName(value);
|
||||||
|
return;
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
setEmail(value);
|
||||||
|
return;
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
setUsername(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// Remove all existing
|
||||||
if (value == null) {
|
if (value == null) {
|
||||||
user.getAttributes().removeIf(a -> a.getName().equals(name));
|
user.getAttributes().removeIf(a -> a.getName().equals(name));
|
||||||
} else {
|
} else {
|
||||||
|
@ -149,6 +164,19 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(String name, List<String> values) {
|
public void setAttribute(String name, List<String> values) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
user.setFirstName(values.get(0));
|
||||||
|
return;
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
user.setLastName(values.get(0));
|
||||||
|
return;
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
setEmail(values.get(0));
|
||||||
|
return;
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
setUsername(values.get(0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
// Remove all existing
|
// Remove all existing
|
||||||
removeAttribute(name);
|
removeAttribute(name);
|
||||||
for (Iterator<String> it = values.stream().filter(Objects::nonNull).iterator(); it.hasNext();) {
|
for (Iterator<String> it = values.stream().filter(Objects::nonNull).iterator(); it.hasNext();) {
|
||||||
|
@ -190,6 +218,15 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return user.getFirstName();
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return user.getLastName();
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return user.getEmail();
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return user.getUsername();
|
||||||
|
}
|
||||||
for (UserAttributeEntity attr : user.getAttributes()) {
|
for (UserAttributeEntity attr : user.getAttributes()) {
|
||||||
if (attr.getName().equals(name)) {
|
if (attr.getName().equals(name)) {
|
||||||
return attr.getValue();
|
return attr.getValue();
|
||||||
|
@ -200,6 +237,15 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(user.getFirstName());
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(user.getLastName());
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return Collections.singletonList(user.getEmail());
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return Collections.singletonList(user.getUsername());
|
||||||
|
}
|
||||||
List<String> result = new ArrayList<>();
|
List<String> result = new ArrayList<>();
|
||||||
for (UserAttributeEntity attr : user.getAttributes()) {
|
for (UserAttributeEntity attr : user.getAttributes()) {
|
||||||
if (attr.getName().equals(name)) {
|
if (attr.getName().equals(name)) {
|
||||||
|
@ -215,6 +261,10 @@ public class UserAdapter implements UserModel, JpaModel<UserEntity> {
|
||||||
for (UserAttributeEntity attr : user.getAttributes()) {
|
for (UserAttributeEntity attr : user.getAttributes()) {
|
||||||
result.add(attr.getName(), attr.getValue());
|
result.add(attr.getName(), attr.getValue());
|
||||||
}
|
}
|
||||||
|
result.add(UserModel.FIRST_NAME, user.getFirstName());
|
||||||
|
result.add(UserModel.LAST_NAME, user.getLastName());
|
||||||
|
result.add(UserModel.EMAIL, user.getEmail());
|
||||||
|
result.add(UserModel.USERNAME, user.getUsername());
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -223,8 +223,18 @@ public class ModelToRepresentation {
|
||||||
|
|
||||||
rep.setRequiredActions(reqActions);
|
rep.setRequiredActions(reqActions);
|
||||||
|
|
||||||
if (user.getAttributes() != null && !user.getAttributes().isEmpty()) {
|
Map<String, List<String>> attributes = user.getAttributes();
|
||||||
Map<String, List<String>> attrs = new HashMap<>(user.getAttributes());
|
Map<String, List<String>> copy = null;
|
||||||
|
|
||||||
|
if (attributes != null) {
|
||||||
|
copy = new HashMap<>(attributes);
|
||||||
|
copy.remove(UserModel.LAST_NAME);
|
||||||
|
copy.remove(UserModel.FIRST_NAME);
|
||||||
|
copy.remove(UserModel.EMAIL);
|
||||||
|
copy.remove(UserModel.USERNAME);
|
||||||
|
}
|
||||||
|
if (attributes != null && !copy.isEmpty()) {
|
||||||
|
Map<String, List<String>> attrs = new HashMap<>(copy);
|
||||||
rep.setAttributes(attrs);
|
rep.setAttributes(attrs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,7 @@ import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserModelDefaultMethods;
|
||||||
import org.keycloak.models.utils.DefaultRoles;
|
import org.keycloak.models.utils.DefaultRoles;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.storage.ReadOnlyException;
|
import org.keycloak.storage.ReadOnlyException;
|
||||||
|
@ -39,17 +40,11 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public class InMemoryUserAdapter implements UserModel {
|
public class InMemoryUserAdapter extends UserModelDefaultMethods {
|
||||||
private String username;
|
|
||||||
private Long createdTimestamp = Time.currentTimeMillis();
|
private Long createdTimestamp = Time.currentTimeMillis();
|
||||||
private String firstName;
|
|
||||||
private String lastName;
|
|
||||||
private String email;
|
|
||||||
private boolean emailVerified;
|
private boolean emailVerified;
|
||||||
private boolean enabled;
|
private boolean enabled;
|
||||||
|
|
||||||
private String realmId;
|
|
||||||
|
|
||||||
private Set<String> roleIds = new HashSet<>();
|
private Set<String> roleIds = new HashSet<>();
|
||||||
private Set<String> groupIds = new HashSet<>();
|
private Set<String> groupIds = new HashSet<>();
|
||||||
|
|
||||||
|
@ -67,8 +62,17 @@ public class InMemoryUserAdapter implements UserModel {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
this.realm = realm;
|
this.realm = realm;
|
||||||
this.id = id;
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getUsername() {
|
||||||
|
return getFirstAttribute(UserModel.USERNAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setUsername(String username) {
|
||||||
|
username = username==null ? null : username.toLowerCase();
|
||||||
|
setSingleAttribute(UserModel.USERNAME, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDefaults() {
|
public void addDefaults() {
|
||||||
|
@ -93,18 +97,6 @@ public class InMemoryUserAdapter implements UserModel {
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getUsername() {
|
|
||||||
return username;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setUsername(String username) {
|
|
||||||
checkReadonly();
|
|
||||||
this.username = username.toLowerCase();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Long getCreatedTimestamp() {
|
public Long getCreatedTimestamp() {
|
||||||
return createdTimestamp;
|
return createdTimestamp;
|
||||||
|
@ -200,43 +192,6 @@ public class InMemoryUserAdapter implements UserModel {
|
||||||
requiredActions.remove(action.name());
|
requiredActions.remove(action.name());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getFirstName() {
|
|
||||||
return firstName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFirstName(String firstName) {
|
|
||||||
checkReadonly();
|
|
||||||
this.firstName = firstName;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLastName() {
|
|
||||||
return lastName;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
checkReadonly();
|
|
||||||
this.lastName = lastName;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEmail() {
|
|
||||||
return email;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setEmail(String email) {
|
|
||||||
checkReadonly();
|
|
||||||
if (email != null) email = email.toLowerCase();
|
|
||||||
this.email = email;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmailVerified() {
|
public boolean isEmailVerified() {
|
||||||
return emailVerified;
|
return emailVerified;
|
||||||
|
|
|
@ -31,8 +31,8 @@ import java.util.stream.Collectors;
|
||||||
*/
|
*/
|
||||||
public interface UserModel extends RoleMapperModel {
|
public interface UserModel extends RoleMapperModel {
|
||||||
String USERNAME = "username";
|
String USERNAME = "username";
|
||||||
String LAST_NAME = "lastName";
|
|
||||||
String FIRST_NAME = "firstName";
|
String FIRST_NAME = "firstName";
|
||||||
|
String LAST_NAME = "lastName";
|
||||||
String EMAIL = "email";
|
String EMAIL = "email";
|
||||||
String LOCALE = "locale";
|
String LOCALE = "locale";
|
||||||
String INCLUDE_SERVICE_ACCOUNT = "keycloak.session.realm.users.query.include_service_account";
|
String INCLUDE_SERVICE_ACCOUNT = "keycloak.session.realm.users.query.include_service_account";
|
||||||
|
@ -48,10 +48,12 @@ public interface UserModel extends RoleMapperModel {
|
||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
|
// No default method here to allow Abstract subclasses where the username is provided in a different manner
|
||||||
String getUsername();
|
String getUsername();
|
||||||
|
|
||||||
|
// No default method here to allow Abstract subclasses where the username is provided in a different manner
|
||||||
void setUsername(String username);
|
void setUsername(String username);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get timestamp of user creation. May be null for old users created before this feature introduction.
|
* Get timestamp of user creation. May be null for old users created before this feature introduction.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
/*
|
||||||
|
* Copyright 2020 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.models;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author <a href="mailto:external.Martin.Idel@bosch.io">Martin Idel</a>
|
||||||
|
* @version $Revision: 1 $
|
||||||
|
*/
|
||||||
|
public abstract class UserModelDefaultMethods implements UserModel {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getFirstName() {
|
||||||
|
return getFirstAttribute(FIRST_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setFirstName(String firstName) {
|
||||||
|
setSingleAttribute(FIRST_NAME, firstName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getLastName() {
|
||||||
|
return getFirstAttribute(LAST_NAME);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setLastName(String lastName) {
|
||||||
|
setSingleAttribute(LAST_NAME, lastName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return getFirstAttribute(EMAIL);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setEmail(String email) {
|
||||||
|
email = email == null ? null : email.toLowerCase();
|
||||||
|
setSingleAttribute(EMAIL, email);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleContainerModel;
|
import org.keycloak.models.RoleContainerModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserModelDefaultMethods;
|
||||||
import org.keycloak.models.utils.DefaultRoles;
|
import org.keycloak.models.utils.DefaultRoles;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.storage.ReadOnlyException;
|
import org.keycloak.storage.ReadOnlyException;
|
||||||
|
@ -49,7 +50,7 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractUserAdapter implements UserModel {
|
public abstract class AbstractUserAdapter extends UserModelDefaultMethods {
|
||||||
protected KeycloakSession session;
|
protected KeycloakSession session;
|
||||||
protected RealmModel realm;
|
protected RealmModel realm;
|
||||||
protected ComponentModel storageProviderModel;
|
protected ComponentModel storageProviderModel;
|
||||||
|
@ -313,16 +314,24 @@ public abstract class AbstractUserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
|
if (name.equals(UserModel.USERNAME)) {
|
||||||
|
return getUsername();
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
return new MultivaluedHashMap<>();
|
MultivaluedHashMap<String, String> attributes = new MultivaluedHashMap<>();
|
||||||
|
attributes.add(UserModel.USERNAME, getUsername());
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
|
if (name.equals(UserModel.USERNAME)) {
|
||||||
|
return Collections.singletonList(getUsername());
|
||||||
|
}
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.keycloak.storage.adapter;
|
package org.keycloak.storage.adapter;
|
||||||
|
|
||||||
|
import org.keycloak.common.util.MultivaluedHashMap;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.GroupModel;
|
import org.keycloak.models.GroupModel;
|
||||||
|
@ -24,6 +25,7 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.RoleContainerModel;
|
import org.keycloak.models.RoleContainerModel;
|
||||||
import org.keycloak.models.RoleModel;
|
import org.keycloak.models.RoleModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.UserModelDefaultMethods;
|
||||||
import org.keycloak.models.utils.DefaultRoles;
|
import org.keycloak.models.utils.DefaultRoles;
|
||||||
import org.keycloak.models.utils.RoleUtils;
|
import org.keycloak.models.utils.RoleUtils;
|
||||||
import org.keycloak.storage.StorageId;
|
import org.keycloak.storage.StorageId;
|
||||||
|
@ -45,7 +47,7 @@ import java.util.Set;
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
|
public abstract class AbstractUserAdapterFederatedStorage extends UserModelDefaultMethods {
|
||||||
public static String FIRST_NAME_ATTRIBUTE = "FIRST_NAME";
|
public static String FIRST_NAME_ATTRIBUTE = "FIRST_NAME";
|
||||||
public static String LAST_NAME_ATTRIBUTE = "LAST_NAME";
|
public static String LAST_NAME_ATTRIBUTE = "LAST_NAME";
|
||||||
public static String EMAIL_ATTRIBUTE = "EMAIL";
|
public static String EMAIL_ATTRIBUTE = "EMAIL";
|
||||||
|
@ -336,7 +338,10 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setSingleAttribute(String name, String value) {
|
public void setSingleAttribute(String name, String value) {
|
||||||
getFederatedStorage().setSingleAttribute(realm, this.getId(), name, value);
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
setUsername(value);
|
||||||
|
}
|
||||||
|
getFederatedStorage().setSingleAttribute(realm, this.getId(), mapAttribute(name), value);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -348,75 +353,55 @@ public abstract class AbstractUserAdapterFederatedStorage implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAttribute(String name, List<String> values) {
|
public void setAttribute(String name, List<String> values) {
|
||||||
getFederatedStorage().setAttribute(realm, this.getId(), name, values);
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
setUsername(values.get(0));
|
||||||
|
}
|
||||||
|
getFederatedStorage().setAttribute(realm, this.getId(), mapAttribute(name), values);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstAttribute(String name) {
|
public String getFirstAttribute(String name) {
|
||||||
return getFederatedStorage().getAttributes(realm, this.getId()).getFirst(name);
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return getUsername();
|
||||||
|
}
|
||||||
|
return getFederatedStorage().getAttributes(realm, this.getId()).getFirst(mapAttribute(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, List<String>> getAttributes() {
|
public Map<String, List<String>> getAttributes() {
|
||||||
return getFederatedStorage().getAttributes(realm, this.getId());
|
MultivaluedHashMap<String, String> attributes = getFederatedStorage().getAttributes(realm, this.getId());
|
||||||
|
if (attributes == null) {
|
||||||
|
attributes = new MultivaluedHashMap<>();
|
||||||
|
}
|
||||||
|
List<String> firstName = attributes.remove(FIRST_NAME_ATTRIBUTE);
|
||||||
|
attributes.add(UserModel.FIRST_NAME, firstName != null && firstName.size() >= 1 ? firstName.get(0) : null);
|
||||||
|
List<String> lastName = attributes.remove(LAST_NAME_ATTRIBUTE);
|
||||||
|
attributes.add(UserModel.LAST_NAME, lastName != null && lastName.size() >= 1 ? lastName.get(0) : null);
|
||||||
|
List<String> email = attributes.remove(EMAIL_ATTRIBUTE);
|
||||||
|
attributes.add(UserModel.EMAIL, email != null && email.size() >= 1 ? email.get(0) : null);
|
||||||
|
attributes.add(UserModel.USERNAME, getUsername());
|
||||||
|
return attributes;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
List<String> result = getFederatedStorage().getAttributes(realm, this.getId()).get(name);
|
if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return Collections.singletonList(getUsername());
|
||||||
|
}
|
||||||
|
List<String> result = getFederatedStorage().getAttributes(realm, this.getId()).get(mapAttribute(name));
|
||||||
return (result == null) ? Collections.emptyList() : result;
|
return (result == null) ? Collections.emptyList() : result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private String mapAttribute(String attributeName) {
|
||||||
public String getFirstName() {
|
if (UserModel.FIRST_NAME.equals(attributeName)) {
|
||||||
return getFirstAttribute(FIRST_NAME_ATTRIBUTE);
|
return FIRST_NAME_ATTRIBUTE;
|
||||||
}
|
} else if (UserModel.LAST_NAME.equals(attributeName)) {
|
||||||
|
return LAST_NAME_ATTRIBUTE;
|
||||||
/**
|
} else if (UserModel.EMAIL.equals(attributeName)) {
|
||||||
* Stores as attribute in federated storage.
|
return EMAIL_ATTRIBUTE;
|
||||||
* FIRST_NAME_ATTRIBUTE
|
}
|
||||||
*
|
return attributeName;
|
||||||
* @param firstName
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setFirstName(String firstName) {
|
|
||||||
setSingleAttribute(FIRST_NAME_ATTRIBUTE, firstName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getLastName() {
|
|
||||||
return getFirstAttribute(LAST_NAME_ATTRIBUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores as attribute in federated storage.
|
|
||||||
* LAST_NAME_ATTRIBUTE
|
|
||||||
*
|
|
||||||
* @param lastName
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setLastName(String lastName) {
|
|
||||||
setSingleAttribute(LAST_NAME_ATTRIBUTE, lastName);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getEmail() {
|
|
||||||
return getFirstAttribute(EMAIL_ATTRIBUTE);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stores as attribute in federated storage.
|
|
||||||
* EMAIL_ATTRIBUTE
|
|
||||||
*
|
|
||||||
* @param email
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public void setEmail(String email) {
|
|
||||||
setSingleAttribute(EMAIL_ATTRIBUTE, email);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -29,6 +29,8 @@ import org.keycloak.models.IdentityProviderModel;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.ModelException;
|
import org.keycloak.models.ModelException;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.protocol.openshift.OpenShiftTokenReviewResponseRepresentation;
|
||||||
import org.keycloak.services.resources.IdentityBrokerService;
|
import org.keycloak.services.resources.IdentityBrokerService;
|
||||||
import org.keycloak.sessions.AuthenticationSessionModel;
|
import org.keycloak.sessions.AuthenticationSessionModel;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
@ -46,10 +48,6 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
|
||||||
|
|
||||||
private String id;
|
private String id;
|
||||||
private String brokerUsername;
|
private String brokerUsername;
|
||||||
private String modelUsername;
|
|
||||||
private String email;
|
|
||||||
private String firstName;
|
|
||||||
private String lastName;
|
|
||||||
private String brokerSessionId;
|
private String brokerSessionId;
|
||||||
private String brokerUserId;
|
private String brokerUserId;
|
||||||
private String code;
|
private String code;
|
||||||
|
@ -78,20 +76,20 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
|
||||||
@JsonIgnore
|
@JsonIgnore
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
return modelUsername;
|
return getFirstAttribute(UserModel.USERNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setUsername(String username) {
|
public void setUsername(String username) {
|
||||||
this.modelUsername = username;
|
setSingleAttribute(UserModel.USERNAME, username);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getModelUsername() {
|
public String getModelUsername() {
|
||||||
return modelUsername;
|
return getFirstAttribute(UserModel.USERNAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setModelUsername(String modelUsername) {
|
public void setModelUsername(String modelUsername) {
|
||||||
this.modelUsername = modelUsername;
|
setSingleAttribute(UserModel.USERNAME, modelUsername);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBrokerUsername() {
|
public String getBrokerUsername() {
|
||||||
|
@ -104,32 +102,32 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
return email;
|
return getFirstAttribute(UserModel.EMAIL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setEmail(String email) {
|
public void setEmail(String email) {
|
||||||
this.email = email;
|
setSingleAttribute(UserModel.EMAIL, email);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return getFirstAttribute(UserModel.FIRST_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setFirstName(String firstName) {
|
public void setFirstName(String firstName) {
|
||||||
this.firstName = firstName;
|
setSingleAttribute(UserModel.FIRST_NAME, firstName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLastName() {
|
public String getLastName() {
|
||||||
return lastName;
|
return getFirstAttribute(UserModel.LAST_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setLastName(String lastName) {
|
public void setLastName(String lastName) {
|
||||||
this.lastName = lastName;
|
setSingleAttribute(UserModel.LAST_NAME, lastName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getBrokerSessionId() {
|
public String getBrokerSessionId() {
|
||||||
|
|
|
@ -143,7 +143,14 @@ public class AccountRestService {
|
||||||
rep.setLastName(user.getLastName());
|
rep.setLastName(user.getLastName());
|
||||||
rep.setEmail(user.getEmail());
|
rep.setEmail(user.getEmail());
|
||||||
rep.setEmailVerified(user.isEmailVerified());
|
rep.setEmailVerified(user.isEmailVerified());
|
||||||
rep.setAttributes(user.getAttributes());
|
rep.setEmailVerified(user.isEmailVerified());
|
||||||
|
Map<String, List<String>> attributes = user.getAttributes();
|
||||||
|
Map<String, List<String>> copiedAttributes = new HashMap<>(attributes);
|
||||||
|
copiedAttributes.remove(UserModel.FIRST_NAME);
|
||||||
|
copiedAttributes.remove(UserModel.LAST_NAME);
|
||||||
|
copiedAttributes.remove(UserModel.EMAIL);
|
||||||
|
copiedAttributes.remove(UserModel.USERNAME);
|
||||||
|
rep.setAttributes(copiedAttributes);
|
||||||
|
|
||||||
return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
|
return Cors.add(request, Response.ok(rep)).auth().allowedOrigins(auth.getToken()).build();
|
||||||
}
|
}
|
||||||
|
|
|
@ -160,6 +160,10 @@ public class UserResource {
|
||||||
if (rep.getAttributes() != null) {
|
if (rep.getAttributes() != null) {
|
||||||
attrsToRemove = new HashSet<>(user.getAttributes().keySet());
|
attrsToRemove = new HashSet<>(user.getAttributes().keySet());
|
||||||
attrsToRemove.removeAll(rep.getAttributes().keySet());
|
attrsToRemove.removeAll(rep.getAttributes().keySet());
|
||||||
|
attrsToRemove.remove(UserModel.FIRST_NAME);
|
||||||
|
attrsToRemove.remove(UserModel.LAST_NAME);
|
||||||
|
attrsToRemove.remove(UserModel.EMAIL);
|
||||||
|
attrsToRemove.remove(UserModel.USERNAME);
|
||||||
} else {
|
} else {
|
||||||
attrsToRemove = Collections.emptySet();
|
attrsToRemove = Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class FreeMarkerUtil {
|
||||||
|
|
||||||
private Template getTemplate(String templateName, Theme theme) throws IOException {
|
private Template getTemplate(String templateName, Theme theme) throws IOException {
|
||||||
Configuration cfg = new Configuration();
|
Configuration cfg = new Configuration();
|
||||||
|
|
||||||
// Assume *.ftl files are html. This lets freemarker know how to
|
// Assume *.ftl files are html. This lets freemarker know how to
|
||||||
// sanitize and prevent XSS attacks.
|
// sanitize and prevent XSS attacks.
|
||||||
if (templateName.toLowerCase().endsWith(".ftl")) {
|
if (templateName.toLowerCase().endsWith(".ftl")) {
|
||||||
|
|
|
@ -128,7 +128,7 @@ public class FailableHardcodedStorageProvider implements UserStorageProvider, Us
|
||||||
@Override
|
@Override
|
||||||
public void setUsername(String name) {
|
public void setUsername(String name) {
|
||||||
super.setUsername(name);
|
super.setUsername(name);
|
||||||
name = name;
|
username = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -76,6 +76,11 @@ public class LDAPTestUtils {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
|
@ -87,13 +92,30 @@ public class LDAPTestUtils {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEmail() {
|
public String getFirstAttribute(String name) {
|
||||||
return email;
|
if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return lastName;
|
||||||
|
} else if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return firstName;
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return email;
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
return super.getFirstAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
if ("postal_code".equals(name) && postalCode != null && postalCode.length > 0) {
|
if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(lastName);
|
||||||
|
} else if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(firstName);
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return Collections.singletonList(email);
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return Collections.singletonList(username);
|
||||||
|
} else if ("postal_code".equals(name) && postalCode != null && postalCode.length > 0) {
|
||||||
return Arrays.asList(postalCode);
|
return Arrays.asList(postalCode);
|
||||||
} else if ("street".equals(name) && street != null) {
|
} else if ("street".equals(name) && street != null) {
|
||||||
return Collections.singletonList(street);
|
return Collections.singletonList(street);
|
||||||
|
|
|
@ -580,7 +580,7 @@ public class PolicyEvaluationTest extends AbstractAuthzTest {
|
||||||
|
|
||||||
builder.append("var realm = $evaluation.getRealm();");
|
builder.append("var realm = $evaluation.getRealm();");
|
||||||
builder.append("var attributes = realm.getUserAttributes('jdoe');");
|
builder.append("var attributes = realm.getUserAttributes('jdoe');");
|
||||||
builder.append("if (attributes.size() == 2 && attributes.containsKey('a1') && attributes.containsKey('a2') && attributes.get('a1').size() == 2 && attributes.get('a2').get(0).equals('3')) { $evaluation.grant(); }");
|
builder.append("if (attributes.size() == 6 && attributes.containsKey('a1') && attributes.containsKey('a2') && attributes.get('a1').size() == 2 && attributes.get('a2').get(0).equals('3')) { $evaluation.grant(); }");
|
||||||
|
|
||||||
policyRepresentation.setCode(builder.toString());
|
policyRepresentation.setCode(builder.toString());
|
||||||
|
|
||||||
|
|
|
@ -204,7 +204,7 @@ public class ExportImportUtil {
|
||||||
// Test attributes
|
// Test attributes
|
||||||
Map<String, List<String>> attrs = wburke.getAttributes();
|
Map<String, List<String>> attrs = wburke.getAttributes();
|
||||||
Assert.assertEquals(1, attrs.size());
|
Assert.assertEquals(1, attrs.size());
|
||||||
List<String> attrVals = attrs.get("email");
|
List<String> attrVals = attrs.get("old-email");
|
||||||
Assert.assertEquals(1, attrVals.size());
|
Assert.assertEquals(1, attrVals.size());
|
||||||
Assert.assertEquals("bburke@redhat.com", attrVals.get(0));
|
Assert.assertEquals("bburke@redhat.com", attrVals.get(0));
|
||||||
|
|
||||||
|
|
|
@ -197,6 +197,11 @@ public class LDAPBinaryAttributesTest extends AbstractLDAPTest {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getEmail() {
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
|
@ -208,13 +213,30 @@ public class LDAPBinaryAttributesTest extends AbstractLDAPTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEmail() {
|
public String getFirstAttribute(String name) {
|
||||||
return email;
|
if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return lastName;
|
||||||
|
} else if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return firstName;
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return email;
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return username;
|
||||||
|
}
|
||||||
|
return super.getFirstAttribute(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
if (LDAPConstants.JPEG_PHOTO.equals(name)) {
|
if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(lastName);
|
||||||
|
} else if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(firstName);
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return Collections.singletonList(email);
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return Collections.singletonList(username);
|
||||||
|
} else if (LDAPConstants.JPEG_PHOTO.equals(name)) {
|
||||||
return Arrays.asList(jpegPhoto);
|
return Arrays.asList(jpegPhoto);
|
||||||
} else {
|
} else {
|
||||||
return Collections.emptyList();
|
return Collections.emptyList();
|
||||||
|
|
|
@ -95,14 +95,6 @@ public class LDAPLegacyImportTest extends AbstractLDAPTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//@Test
|
|
||||||
public void runit() throws Exception {
|
|
||||||
Thread.sleep(10000000);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void loginClassic() {
|
public void loginClassic() {
|
||||||
loginPage.open();
|
loginPage.open();
|
||||||
|
|
|
@ -114,15 +114,6 @@ public class LDAPProvidersIntegrationTest extends AbstractLDAPTest {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// @Test
|
|
||||||
// @Ignore
|
|
||||||
// public void runit() throws Exception {
|
|
||||||
// Thread.sleep(10000000);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* KEYCLOAK-3986
|
* KEYCLOAK-3986
|
||||||
*
|
*
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.keycloak.testsuite.util.RealmBuilder;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
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;
|
||||||
|
@ -237,7 +238,7 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
|
||||||
|
|
||||||
List<String> attrVals = user.getAttribute("key1");
|
List<String> attrVals = user.getAttribute("key1");
|
||||||
Assert.assertThat(attrVals, hasSize(1));
|
Assert.assertThat(attrVals, hasSize(1));
|
||||||
Assert.assertThat(attrVals.get(0), equalTo("value1"));
|
Assert.assertThat(attrVals, contains("value1"));
|
||||||
Assert.assertThat(user.getFirstAttribute("key1"), equalTo("value1"));
|
Assert.assertThat(user.getFirstAttribute("key1"), equalTo("value1"));
|
||||||
|
|
||||||
attrVals = user.getAttribute("key2");
|
attrVals = user.getAttribute("key2");
|
||||||
|
@ -249,7 +250,8 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
|
||||||
Assert.assertThat(user.getFirstAttribute("key3"), nullValue());
|
Assert.assertThat(user.getFirstAttribute("key3"), nullValue());
|
||||||
|
|
||||||
Map<String, List<String>> allAttrVals = user.getAttributes();
|
Map<String, List<String>> allAttrVals = user.getAttributes();
|
||||||
Assert.assertThat(allAttrVals.keySet(), hasSize(2));
|
Assert.assertThat(allAttrVals.keySet(), hasSize(6));
|
||||||
|
Assert.assertThat(allAttrVals.keySet(), containsInAnyOrder(UserModel.USERNAME, UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, "key1", "key2"));
|
||||||
Assert.assertThat(allAttrVals.get("key1"), equalTo(user.getAttribute("key1")));
|
Assert.assertThat(allAttrVals.get("key1"), equalTo(user.getAttribute("key1")));
|
||||||
Assert.assertThat(allAttrVals.get("key2"), equalTo(user.getAttribute("key2")));
|
Assert.assertThat(allAttrVals.get("key2"), equalTo(user.getAttribute("key2")));
|
||||||
|
|
||||||
|
@ -298,9 +300,9 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
|
||||||
Map<String, List<String>> allAttrVals = user.getAttributes();
|
Map<String, List<String>> allAttrVals = user.getAttributes();
|
||||||
|
|
||||||
// Ensure same transaction is able to see updated value
|
// Ensure same transaction is able to see updated value
|
||||||
Assert.assertThat(allAttrVals.keySet(), hasSize(1));
|
Assert.assertThat(allAttrVals.keySet(), hasSize(5));
|
||||||
|
Assert.assertThat(allAttrVals.keySet(), containsInAnyOrder("key1", UserModel.FIRST_NAME, UserModel.LAST_NAME, UserModel.EMAIL, UserModel.USERNAME));
|
||||||
Assert.assertThat(allAttrVals.get("key1"), contains("val2"));
|
Assert.assertThat(allAttrVals.get("key1"), contains("val2"));
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +318,12 @@ public class UserModelTest extends AbstractTestRealmKeycloakTest {
|
||||||
RealmModel realm = currentSession.realms().getRealmByName("original");
|
RealmModel realm = currentSession.realms().getRealmByName("original");
|
||||||
|
|
||||||
Map<String, List<String>> expected = new HashMap<>();
|
Map<String, List<String>> expected = new HashMap<>();
|
||||||
expected.put("key1", Arrays.asList("value3"));
|
expected.put("key1", Collections.singletonList("value3"));
|
||||||
expected.put("key2", Arrays.asList("value2"));
|
expected.put("key2", Collections.singletonList("value2"));
|
||||||
|
expected.put(UserModel.FIRST_NAME, Collections.singletonList(null));
|
||||||
|
expected.put(UserModel.LAST_NAME, Collections.singletonList(null));
|
||||||
|
expected.put(UserModel.EMAIL, Collections.singletonList(null));
|
||||||
|
expected.put(UserModel.USERNAME, Collections.singletonList("user"));
|
||||||
|
|
||||||
UserModel user = currentSession.users().addUser(realm, "user");
|
UserModel user = currentSession.users().addUser(realm, "user");
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@
|
||||||
"createdTimestamp" : 123654,
|
"createdTimestamp" : 123654,
|
||||||
"notBefore": 159,
|
"notBefore": 159,
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"email": "bburke@redhat.com"
|
"old-email": "bburke@redhat.com"
|
||||||
},
|
},
|
||||||
"credentials": [
|
"credentials": [
|
||||||
{
|
{
|
||||||
|
|
|
@ -148,14 +148,6 @@ public class LdapManyObjectsInitializerCommand extends AbstractCommand {
|
||||||
private static LDAPObject addLDAPUser(LDAPStorageProvider ldapProvider, RealmModel realm, final String username,
|
private static LDAPObject addLDAPUser(LDAPStorageProvider ldapProvider, RealmModel realm, final String username,
|
||||||
final String firstName, final String lastName, final String email,
|
final String firstName, final String lastName, final String email,
|
||||||
String groupsDN, int startOffsetGroups, int countGroups) {
|
String groupsDN, int startOffsetGroups, int countGroups) {
|
||||||
// LDAPObject ldapUser = new LDAPObject();
|
|
||||||
// LDAPConfig ldapConfig = ldapProvider.getLdapIdentityStore().getConfig();
|
|
||||||
// ldapUser.setRdnAttributeName(ldapConfig.getRdnLdapAttribute());
|
|
||||||
// ldapUser.setObjectClasses(ldapConfig.getUserObjectClasses());
|
|
||||||
// LDAPUtils.computeAndSetDn(ldapConfig, ldapUser);
|
|
||||||
//
|
|
||||||
// ldapUser.setSingleAttribute("uid", )
|
|
||||||
// ldapProvider.getLdapIdentityStore().add(ldapUser);
|
|
||||||
|
|
||||||
UserModel helperUser = new UserModelDelegate(null) {
|
UserModel helperUser = new UserModelDelegate(null) {
|
||||||
|
|
||||||
|
@ -181,7 +173,15 @@ public class LdapManyObjectsInitializerCommand extends AbstractCommand {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> getAttribute(String name) {
|
public List<String> getAttribute(String name) {
|
||||||
if ("street".equals(name)) {
|
if (UserModel.FIRST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(firstName);
|
||||||
|
} else if (UserModel.LAST_NAME.equals(name)) {
|
||||||
|
return Collections.singletonList(lastName);
|
||||||
|
} else if (UserModel.EMAIL.equals(name)) {
|
||||||
|
return Collections.singletonList(email);
|
||||||
|
} else if (UserModel.USERNAME.equals(name)) {
|
||||||
|
return Collections.singletonList(username);
|
||||||
|
} else if ("street".equals(name)) {
|
||||||
|
|
||||||
List<String> groupNamesList = new ArrayList<>();
|
List<String> groupNamesList = new ArrayList<>();
|
||||||
for (int i=startOffsetGroups ; i<startOffsetGroups + countGroups ; i++) {
|
for (int i=startOffsetGroups ; i<startOffsetGroups + countGroups ; i++) {
|
||||||
|
|
Loading…
Reference in a new issue