keycloak-uncached
Changes
broker/core/src/main/java/org/keycloak/broker/provider/HardcodedUserSessionAttributeMapper.java 2(+1 -1)
broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractJsonUserAttributeMapper.java 16(+8 -8)
services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java 48(+35 -13)
services/src/main/java/org/keycloak/authentication/requiredactions/util/UpdateProfileContext.java 4(+4 -0)
Details
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
index ee327c3..f79e8d7 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/AbstractIdentityProviderMapper.java
@@ -1,10 +1,10 @@
package org.keycloak.broker.provider;
-import org.keycloak.broker.provider.IdentityProviderMapper;
import org.keycloak.models.IdentityProviderMapperModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserModel;
/**
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
@@ -35,4 +35,9 @@ public abstract class AbstractIdentityProviderMapper implements IdentityProvider
public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
+
+ @Override
+ public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+
+ }
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java b/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
index 6052f57..95417fd 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/BrokeredIdentityContext.java
@@ -18,9 +18,13 @@
package org.keycloak.broker.provider;
import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderModel;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
/**
@@ -152,6 +156,22 @@ public class BrokeredIdentityContext {
this.contextData = contextData;
}
+ // Set the attribute, which will be available on "Update profile" page and in authenticators
+ public void setUserAttribute(String attributeName, String attributeValue) {
+ List<String> list = new ArrayList<>();
+ list.add(attributeValue);
+ getContextData().put(Constants.USER_ATTRIBUTES_PREFIX + attributeName, list);
+ }
+
+ public String getUserAttribute(String attributeName) {
+ List<String> userAttribute = (List<String>) getContextData().get(Constants.USER_ATTRIBUTES_PREFIX + attributeName);
+ if (userAttribute == null || userAttribute.isEmpty()) {
+ return null;
+ } else {
+ return userAttribute.get(0);
+ }
+ }
+
public String getFirstName() {
return firstName;
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java b/broker/core/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java
index 3f8fcf2..5beb0ab 100644
--- a/broker/core/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/DefaultDataMarshaller.java
@@ -1,6 +1,7 @@
package org.keycloak.broker.provider;
import java.io.IOException;
+import java.util.List;
import org.keycloak.common.util.Base64Url;
import org.keycloak.util.JsonSerialization;
@@ -26,15 +27,20 @@ public class DefaultDataMarshaller implements IdentityProviderDataMarshaller {
@Override
public <T> T deserialize(String serialized, Class<T> clazz) {
- if (clazz.equals(String.class)) {
- return clazz.cast(serialized);
- } else {
- byte[] bytes = Base64Url.decode(serialized);
- try {
- return JsonSerialization.readValue(bytes, clazz);
- } catch (IOException ioe) {
- throw new RuntimeException(ioe);
+ try {
+ if (clazz.equals(String.class)) {
+ return clazz.cast(serialized);
+ } else {
+ byte[] bytes = Base64Url.decode(serialized);
+ if (List.class.isAssignableFrom(clazz)) {
+ List list = JsonSerialization.readValue(bytes, List.class);
+ return clazz.cast(list);
+ } else {
+ return JsonSerialization.readValue(bytes, clazz);
+ }
}
+ } catch (IOException ioe) {
+ throw new RuntimeException(ioe);
}
}
}
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedAttributeMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedAttributeMapper.java
index d182d6f..7ab9944 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedAttributeMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedAttributeMapper.java
@@ -69,10 +69,10 @@ public class HardcodedAttributeMapper extends AbstractIdentityProviderMapper {
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
- user.setSingleAttribute(attribute, attributeValue);
+ context.setUserAttribute(attribute, attributeValue);
}
@Override
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedUserSessionAttributeMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedUserSessionAttributeMapper.java
index 676e6b1..656af48 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedUserSessionAttributeMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/HardcodedUserSessionAttributeMapper.java
@@ -67,7 +67,7 @@ public class HardcodedUserSessionAttributeMapper extends AbstractIdentityProvide
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(ATTRIBUTE);
String attributeValue = mapperModel.getConfig().get(ATTRIBUTE_VALUE);
context.getClientSession().setUserSessionNote(attribute, attributeValue);
diff --git a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
index b76a6af..7599fae 100755
--- a/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
+++ b/broker/core/src/main/java/org/keycloak/broker/provider/IdentityProviderMapper.java
@@ -20,8 +20,10 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
String getDisplayType();
/**
- * Called to determine what keycloak username and email to use to process the login request from the external IDP
- * Usually used to map BrokeredIdentityContet.username or email.
+ * Called to determine what keycloak username and email to use to process the login request from the external IDP.
+ * It's called before "FirstBrokerLogin" flow, so can be used to map attributes to BrokeredIdentityContext ( BrokeredIdentityContext.setUserAttribute ),
+ * which will be available on "Review Profile" page and in authenticators during FirstBrokerLogin flow
+ *
*
* @param session
* @param realm
@@ -31,7 +33,7 @@ public interface IdentityProviderMapper extends Provider, ProviderFactory<Identi
void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context);
/**
- * Called after UserModel is created for first time for this user.
+ * Called after UserModel is created for first time for this user. Called after "FirstBrokerLogin" flow
*
* @param session
* @param realm
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractJsonUserAttributeMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractJsonUserAttributeMapper.java
index 4d2a4e2..5d2147e 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractJsonUserAttributeMapper.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/AbstractJsonUserAttributeMapper.java
@@ -69,8 +69,8 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
* @param user context to store profile data into
* @param profile to store into context
* @param provider identification of social provider to be used in log dump
- *
- * @see #importNewUser(KeycloakSession, RealmModel, UserModel, IdentityProviderMapperModel, BrokeredIdentityContext)
+ *
+ * @see #preprocessFederatedIdentity(KeycloakSession, RealmModel, IdentityProviderMapperModel, BrokeredIdentityContext)
* @see BrokeredIdentityContext#getContextData()
*/
public static void storeUserProfileForMapper(BrokeredIdentityContext user, JsonNode profile, String provider) {
@@ -100,17 +100,17 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(CONF_USER_ATTRIBUTE);
if (attribute == null || attribute.trim().isEmpty()) {
- logger.debug("Attribute is not configured");
+ logger.warnf("Attribute is not configured for mapper %s", mapperModel.getName());
return;
}
attribute = attribute.trim();
String value = getJsonValue(mapperModel, context);
if (value != null) {
- user.setSingleAttribute(attribute, value);
+ context.setUserAttribute(attribute, value);
}
}
@@ -123,13 +123,13 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
String jsonField = mapperModel.getConfig().get(CONF_JSON_FIELD);
if (jsonField == null || jsonField.trim().isEmpty()) {
- logger.debug("JSON field path is not configured");
+ logger.warnf("JSON field path is not configured for mapper %s", mapperModel.getName());
return null;
}
jsonField = jsonField.trim();
if (jsonField.startsWith(JSON_PATH_DELIMITER) || jsonField.endsWith(JSON_PATH_DELIMITER) || jsonField.startsWith("[")) {
- logger.debug("JSON field path is invalid " + jsonField);
+ logger.warnf("JSON field path is invalid %s", jsonField);
return null;
}
@@ -138,7 +138,7 @@ public abstract class AbstractJsonUserAttributeMapper extends AbstractIdentityPr
String value = getJsonValue(profileJsonNode, jsonField);
if (value == null) {
- logger.debug("User profile JSON value '" + jsonField + "' is not available.");
+ logger.debugf("User profile JSON value '%s' is not available.", jsonField);
}
return value;
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
index b810f48..6599388 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UserAttributeMapper.java
@@ -70,11 +70,11 @@ public class UserAttributeMapper extends AbstractClaimMapper {
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getClaimValue(mapperModel, context);
if (value != null) {
- user.setSingleAttribute(attribute, value.toString());
+ context.setUserAttribute(attribute, value.toString());
}
}
diff --git a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java
index d246086..a3eafa9 100755
--- a/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java
+++ b/broker/oidc/src/main/java/org/keycloak/broker/oidc/mappers/UsernameTemplateMapper.java
@@ -66,10 +66,6 @@ public class UsernameTemplateMapper extends AbstractClaimMapper {
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
- }
-
- @Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
index 8d5ccf7..a751b33 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UserAttributeMapper.java
@@ -80,11 +80,11 @@ public class UserAttributeMapper extends AbstractIdentityProviderMapper {
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
+ public void preprocessFederatedIdentity(KeycloakSession session, RealmModel realm, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
String attribute = mapperModel.getConfig().get(USER_ATTRIBUTE);
Object value = getAttribute(mapperModel, context);
if (value != null) {
- user.setSingleAttribute(attribute, value.toString());
+ context.setUserAttribute(attribute, value.toString());
}
}
diff --git a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java
index 0a44e4b..28aa00d 100755
--- a/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java
+++ b/broker/saml/src/main/java/org/keycloak/broker/saml/mappers/UsernameTemplateMapper.java
@@ -72,11 +72,6 @@ public class UsernameTemplateMapper extends AbstractIdentityProviderMapper {
}
@Override
- public void importNewUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
-
- }
-
- @Override
public void updateBrokeredUser(KeycloakSession session, RealmModel realm, UserModel user, IdentityProviderMapperModel mapperModel, BrokeredIdentityContext context) {
}
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
index b2d6b1d..0783529 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/ProfileBean.java
@@ -47,8 +47,9 @@ public class ProfileBean {
this.user = user;
this.formData = formData;
- if (user.getAttributes() != null) {
- for (Map.Entry<String, List<String>> attr : user.getAttributes().entrySet()) {
+ Map<String, List<String>> modelAttrs = user.getAttributes();
+ if (modelAttrs != null) {
+ for (Map.Entry<String, List<String>> attr : modelAttrs.entrySet()) {
List<String> attrValue = attr.getValue();
if (attrValue != null && attrValue.size() > 0) {
attributes.put(attr.getKey(), attrValue.get(0));
diff --git a/model/api/src/main/java/org/keycloak/models/Constants.java b/model/api/src/main/java/org/keycloak/models/Constants.java
index 6437cd5..0c52929 100755
--- a/model/api/src/main/java/org/keycloak/models/Constants.java
+++ b/model/api/src/main/java/org/keycloak/models/Constants.java
@@ -28,4 +28,7 @@ public interface Constants {
String VERIFY_EMAIL_KEY = "VERIFY_EMAIL_KEY";
String KEY = "key";
+
+ // Prefix for user attributes used in various "context"data maps
+ public static final String USER_ATTRIBUTES_PREFIX = "user.attributes.";
}
diff --git a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
index 22c1582..3a105c4 100755
--- a/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
+++ b/model/api/src/main/java/org/keycloak/models/utils/DefaultAuthenticationFlows.java
@@ -432,7 +432,7 @@ public class DefaultAuthenticationFlows {
if (browserFlow == null) {
browserFlow = realm.getFlowByAlias(DefaultAuthenticationFlows.BROWSER_FLOW);
}
-
+
List<AuthenticationExecutionModel> browserExecutions = new LinkedList<>();
KeycloakModelUtils.deepFindAuthenticationExecutions(realm, browserFlow, browserExecutions);
for (AuthenticationExecutionModel browserExecution : browserExecutions) {
diff --git a/services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java b/services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java
index 7e3990b..968831c 100644
--- a/services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java
+++ b/services/src/main/java/org/keycloak/authentication/authenticators/broker/util/SerializedBrokeredIdentityContext.java
@@ -1,6 +1,8 @@
package org.keycloak.authentication.authenticators.broker.util;
import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -14,6 +16,7 @@ import org.keycloak.broker.provider.IdentityProviderDataMarshaller;
import org.keycloak.common.util.Base64Url;
import org.keycloak.common.util.reflections.Reflections;
import org.keycloak.models.ClientSessionModel;
+import org.keycloak.models.Constants;
import org.keycloak.models.IdentityProviderModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelException;
@@ -162,44 +165,52 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
this.contextData = contextData;
}
+ @JsonIgnore
@Override
public Map<String, List<String>> getAttributes() {
Map<String, List<String>> result = new HashMap<>();
for (Map.Entry<String, ContextDataEntry> entry : this.contextData.entrySet()) {
- if (entry.getKey().startsWith("user.attributes.")) {
- ContextDataEntry ctxEntry = entry.getValue();
- String asString = ctxEntry.getData();
- try {
- List<String> asList = JsonSerialization.readValue(asString, List.class);
- result.put(entry.getKey().substring(16), asList);
- } catch (IOException ioe) {
- throw new RuntimeException(ioe);
- }
+ if (entry.getKey().startsWith(Constants.USER_ATTRIBUTES_PREFIX)) {
+ String attrName = entry.getKey().substring(16); // length of USER_ATTRIBUTES_PREFIX
+ List<String> asList = getAttribute(attrName);
+ result.put(attrName, asList);
}
}
return result;
}
+ @JsonIgnore
+ @Override
+ public void setSingleAttribute(String name, String value) {
+ List<String> list = new ArrayList<>();
+ list.add(value);
+ setAttribute(name, list);
+ }
+
+ @JsonIgnore
@Override
public void setAttribute(String key, List<String> value) {
try {
- String listStr = JsonSerialization.writeValueAsString(value);
+ byte[] listBytes = JsonSerialization.writeValueAsBytes(value);
+ String listStr = Base64Url.encode(listBytes);
ContextDataEntry ctxEntry = ContextDataEntry.create(List.class.getName(), listStr);
- this.contextData.put("user.attributes." + key, ctxEntry);
+ this.contextData.put(Constants.USER_ATTRIBUTES_PREFIX + key, ctxEntry);
} catch (IOException ioe) {
throw new RuntimeException(ioe);
}
}
+ @JsonIgnore
@Override
public List<String> getAttribute(String key) {
- ContextDataEntry ctxEntry = this.contextData.get("user.attributes." + key);
+ ContextDataEntry ctxEntry = this.contextData.get(Constants.USER_ATTRIBUTES_PREFIX + key);
if (ctxEntry != null) {
try {
String asString = ctxEntry.getData();
- List<String> asList = JsonSerialization.readValue(asString, List.class);
+ byte[] asBytes = Base64Url.decode(asString);
+ List<String> asList = JsonSerialization.readValue(asBytes, List.class);
return asList;
} catch (IOException ioe) {
throw new RuntimeException(ioe);
@@ -209,6 +220,17 @@ public class SerializedBrokeredIdentityContext implements UpdateProfileContext {
}
}
+ @JsonIgnore
+ @Override
+ public String getFirstAttribute(String name) {
+ List<String> attrs = getAttribute(name);
+ if (attrs == null || attrs.isEmpty()) {
+ return null;
+ } else {
+ return attrs.get(0);
+ }
+ }
+
public BrokeredIdentityContext deserialize(KeycloakSession session, ClientSessionModel clientSession) {
BrokeredIdentityContext ctx = new BrokeredIdentityContext(getId());
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/util/UpdateProfileContext.java b/services/src/main/java/org/keycloak/authentication/requiredactions/util/UpdateProfileContext.java
index 2acef37..a88d173 100644
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/util/UpdateProfileContext.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/util/UpdateProfileContext.java
@@ -31,8 +31,12 @@ public interface UpdateProfileContext {
Map<String, List<String>> getAttributes();
+ void setSingleAttribute(String name, String value);
+
void setAttribute(String key, List<String> value);
+ String getFirstAttribute(String name);
+
List<String> getAttribute(String key);
}
diff --git a/services/src/main/java/org/keycloak/authentication/requiredactions/util/UserUpdateProfileContext.java b/services/src/main/java/org/keycloak/authentication/requiredactions/util/UserUpdateProfileContext.java
index 55d6dda..94a1151 100644
--- a/services/src/main/java/org/keycloak/authentication/requiredactions/util/UserUpdateProfileContext.java
+++ b/services/src/main/java/org/keycloak/authentication/requiredactions/util/UserUpdateProfileContext.java
@@ -70,11 +70,21 @@ public class UserUpdateProfileContext implements UpdateProfileContext {
}
@Override
+ public void setSingleAttribute(String name, String value) {
+ user.setSingleAttribute(name, value);
+ }
+
+ @Override
public void setAttribute(String key, List<String> value) {
user.setAttribute(key, value);
}
@Override
+ public String getFirstAttribute(String name) {
+ return user.getFirstAttribute(name);
+ }
+
+ @Override
public List<String> getAttribute(String key) {
return user.getAttribute(key);
}
diff --git a/services/src/main/java/org/keycloak/services/resources/AttributeFormDataProcessor.java b/services/src/main/java/org/keycloak/services/resources/AttributeFormDataProcessor.java
index 9fb60ec..194ad2d 100755
--- a/services/src/main/java/org/keycloak/services/resources/AttributeFormDataProcessor.java
+++ b/services/src/main/java/org/keycloak/services/resources/AttributeFormDataProcessor.java
@@ -5,6 +5,7 @@ import java.util.List;
import org.keycloak.authentication.requiredactions.util.UpdateProfileContext;
import org.keycloak.authentication.requiredactions.util.UserUpdateProfileContext;
+import org.keycloak.models.Constants;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
@@ -29,11 +30,12 @@ public class AttributeFormDataProcessor {
public static void process(MultivaluedMap<String, String> formData, RealmModel realm, UpdateProfileContext user) {
for (String key : formData.keySet()) {
- if (!key.startsWith("user.attributes.")) continue;
- String attribute = key.substring("user.attributes.".length());
+ if (!key.startsWith(Constants.USER_ATTRIBUTES_PREFIX)) continue;
+ String attribute = key.substring(Constants.USER_ATTRIBUTES_PREFIX.length());
// Need to handle case when attribute has multiple values, but in UI was displayed just first value
- List<String> modelValue = new ArrayList<>(user.getAttribute(attribute));
+ List<String> modelVal = user.getAttribute(attribute);
+ List<String> modelValue = modelVal==null ? new ArrayList<String>() : new ArrayList<>(modelVal);
int index = 0;
for (String value : formData.get(key)) {