Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: allow importing custom & multivalued properties with csv - EXO-65669 - Meeds-io/meeds#1070 #2732

Merged
merged 1 commit into from
Aug 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,14 @@ public interface ProfilePropertyService {
*/
List<ProfilePropertySetting> getSynchronizedPropertySettings();

/**
* Retrieves the ProfileProperty item with given {@link ProfilePropertySetting}
* propertyName
*
* @return {@link ProfilePropertySetting} if exist or null if not
*/
ProfilePropertySetting getProfileSettingById(Long id);

/**
* Retrieves the ProfileProperty item with given {@link ProfilePropertySetting}
* propertyName
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,11 @@ public ProfilePropertySetting getProfileSettingByName(String name) {
return profileSettingStorage.findProfileSettingByName(name);
}

@Override
public ProfilePropertySetting getProfileSettingById(Long id) {
return profileSettingStorage.getProfileSettingById(id);
}

@Override
public ProfilePropertySetting createPropertySetting(ProfilePropertySetting profilePropertySetting) throws ObjectAlreadyExistsException {
if (profilePropertySetting == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
import javax.ws.rs.core.UriInfo;

import org.apache.commons.lang3.StringUtils;
import org.exoplatform.social.core.profileproperty.model.ProfilePropertySetting;
import org.json.JSONException;
import org.json.JSONObject;
import org.picocontainer.Startable;
Expand Down Expand Up @@ -959,7 +960,8 @@ public Response updateUserProfileAttributes(@Context HttpServletRequest request,
}

try {
saveProfile(username, profileEntity);
Map<String, Object> userProfileProperties = extractPropertiesFromEntities(profileEntity);
saveProfile(username, userProfileProperties);
} catch (IllegalAccessException e) {
LOG.error("User {} is not allowed to update attributes", currentUser);
return Response.status(Status.UNAUTHORIZED).build();
Expand All @@ -969,6 +971,60 @@ public Response updateUserProfileAttributes(@Context HttpServletRequest request,
}
return Response.noContent().build();
}

private Map<String, Object> extractPropertiesFromEntities(ProfileEntity profileEntity) {
Map<String, Object> userProfileProperties = new HashMap<>();
for(String key: profileEntity.getDataEntity().keySet()) {
if(profileEntity.getDataEntity().get(key) instanceof List<?>) {
List<Map<String, String>> properties = new ArrayList<>();
if(key.equalsIgnoreCase(Profile.CONTACT_IMS)) {
List<IMEntity> imsEntities = (List<IMEntity>) profileEntity.getDataEntity().get(key);
for(IMEntity im : imsEntities){
Map<String, String> imMap = new HashMap<>();
imMap.put(im.getImType(), im.getImId());
properties.add(imMap);
}
} else if(key.equalsIgnoreCase(Profile.CONTACT_PHONES)) {
List<PhoneEntity> phoneEntities = (List<PhoneEntity>) profileEntity.getDataEntity().get(key);
for(PhoneEntity phoneEntity : phoneEntities){
Map<String, String> phoneMap = new HashMap<>();
phoneMap.put(phoneEntity.getPhoneType(), phoneEntity.getPhoneNumber());
properties.add(phoneMap);
}

} else if(key.equalsIgnoreCase(Profile.CONTACT_URLS)) {
List<URLEntity> urlEntities = (List<URLEntity>) profileEntity.getDataEntity().get(key);
for(URLEntity url : urlEntities){
Map<String, String> urlMap = new HashMap<>();
urlMap.put(url.getUrl(), url.getUrl());
properties.add(urlMap);
}
} else if(key.equalsIgnoreCase(Profile.EXPERIENCES)) {
@SuppressWarnings("unchecked")
List<ExperienceEntity> experienceEntities = (List<ExperienceEntity>) profileEntity.getDataEntity().get(key);
for(ExperienceEntity experienceEntity : experienceEntities) {
Map<String, String> experienceMap = new HashMap<>();
if(StringUtils.isNotBlank(experienceEntity.getId())) {
experienceMap.put(Profile.EXPERIENCES_ID, experienceEntity.getId());
}
experienceMap.put(Profile.EXPERIENCES_COMPANY, experienceEntity.getCompany());
experienceMap.put(Profile.EXPERIENCES_DESCRIPTION, experienceEntity.getDescription());
experienceMap.put(Profile.EXPERIENCES_SKILLS, experienceEntity.getSkills());
experienceMap.put(Profile.EXPERIENCES_START_DATE, experienceEntity.getStartDate());
experienceMap.put(Profile.EXPERIENCES_END_DATE, experienceEntity.getEndDate());
experienceMap.put(Profile.EXPERIENCES_POSITION, experienceEntity.getPosition());
experienceMap.put(Profile.EXPERIENCES_IS_CURRENT, String.valueOf(experienceEntity.getIsCurrent()));
properties.add(experienceMap);
}
}
userProfileProperties.put(key, properties);
} else {
userProfileProperties.put(key, profileEntity.getDataEntity().get(key));
}
}
return userProfileProperties;
}

@PATCH
@Path("{id}/profile/properties")
@Operation(summary = "Update set of properties in user profile", method = "PATCH", description = "This can only be done by the logged in user.")
Expand Down Expand Up @@ -1029,43 +1085,18 @@ public Response updateUserProfileAttributes(@Context
profile = getUserIdentity(username).getProfile();
}
} else {
switch (profileProperty.getPropertyName()) {
case Profile.CONTACT_PHONES:
List<PhoneEntity> phoneEntities = new ArrayList<>();
for (ProfilePropertySettingEntity child : profileProperty.getChildren()) {
phoneEntities.add(new PhoneEntity(child.getPropertyName(), child.getValue()));
updateProfileField(profile, profileProperty.getPropertyName(), phoneEntities, true);
}
break;
case Profile.CONTACT_IMS:
List<IMEntity> imEntities = new ArrayList<>();
for (ProfilePropertySettingEntity child : profileProperty.getChildren()) {
imEntities.add(new IMEntity(child.getPropertyName(), child.getValue()));
updateProfileField(profile, profileProperty.getPropertyName(), imEntities, true);
}
break;
case Profile.CONTACT_URLS:
List<URLEntity> urlEntities = new ArrayList<>();
for (ProfilePropertySettingEntity child : profileProperty.getChildren()) {
urlEntities.add(new URLEntity(child.getValue()));
updateProfileField(profile, profileProperty.getPropertyName(), urlEntities, true);
}
break;
default:
List<Map<String, String>> maps = new ArrayList<>();
profileProperty.getChildren().stream().forEach(profilePropertySettingEntity -> {
if (profilePropertySettingEntity.getValue() != null && !profilePropertySettingEntity.getValue().isBlank()
&& (profilePropertySettingEntity.getPropertyName() != null && !profilePropertySettingEntity.getPropertyName().isBlank()
|| profileProperty.isMultiValued())) {
Map<String, String> childrenMap = new HashMap<>();
childrenMap.put("key", profilePropertySettingEntity.getPropertyName());
childrenMap.put("value", profilePropertySettingEntity.getValue());
maps.add(childrenMap);
}
return;
});
updateProfileField(profile, profileProperty.getPropertyName(), maps, true);
}
List<Map<String, String>> maps = new ArrayList<>();
profileProperty.getChildren().forEach(profilePropertySettingEntity -> {
if (profilePropertySettingEntity.getValue() != null && !profilePropertySettingEntity.getValue().isBlank()
&& (profilePropertySettingEntity.getPropertyName() != null && !profilePropertySettingEntity.getPropertyName().isBlank()
|| profileProperty.isMultiValued())) {
Map<String, String> childrenMap = new HashMap<>();
childrenMap.put("key", profilePropertySettingEntity.getPropertyName());
childrenMap.put("value", profilePropertySettingEntity.getValue());
maps.add(childrenMap);
}
});
updateProfileField(profile, profileProperty.getPropertyName(), maps, true);
}
} catch (IllegalAccessException e) {
LOG.error("User {} is not allowed to update attributes", currentUser);
Expand Down Expand Up @@ -1639,6 +1670,7 @@ private String importUser(UserImportResultEntity userImportResultEntity,
user.setPassword(null);
if (userStatus) {
organizationService.getUserHandler().setEnabled(userName, Boolean.valueOf(userObject.getString("enabled")), true);
user.setEnabled(true);
}
organizationService.getUserHandler().saveUser(user, true);
onboardUser = onboardUser && existingUser.isEnabled() && (existingUser.getLastLoginTime().getTime() == existingUser.getCreatedDate().getTime());
Expand Down Expand Up @@ -1707,10 +1739,58 @@ private String importUser(UserImportResultEntity userImportResultEntity,
userObject.remove("groups");
userObject.remove("enabled");

ProfileEntity profileEntity = EntityBuilder.fromJsonString(userObject.toString(), ProfileEntity.class);
Map<String, Object> userProfileProperties = new HashMap<>();
Iterator<String> properties = userObject.keys();
while(properties.hasNext()) {
String propertyName = properties.next();
String propertyValue = userObject.getString(propertyName);
ProfilePropertySetting propertySetting;
ProfilePropertySetting parentPropertySetting = null;
if(propertyName.contains(".")) {
String[] propertyNames = propertyName.split("\\.");
if (propertyNames.length > 2) {
continue;
}
String parentProperty = propertyNames[0];
String childProperty = propertyNames[1];

if (profilePropertyService.getProfileSettingByName(propertyName) == null
&& (profilePropertyService.getProfileSettingByName(parentProperty) == null
|| profilePropertyService.getProfileSettingByName(childProperty) == null)) {
LOG.warn("Parent property {} was not found, its value won't be imported");
continue;
}
propertySetting =
profilePropertyService.getProfileSettingByName(propertyName) != null ? profilePropertyService.getProfileSettingByName(propertyName)
: profilePropertyService.getProfileSettingByName(childProperty);
} else {
propertySetting = profilePropertyService.getProfileSettingByName(propertyName);
}
if(propertySetting != null && propertySetting.getParentId() != null) {
parentPropertySetting = profilePropertyService.getProfileSettingById(propertySetting.getParentId());
}
Map<String, String> childPropertyMap = new HashMap<>();
childPropertyMap.put("value", propertyValue);
if (propertySetting != null && propertySetting.isMultiValued()) {
userProfileProperties.computeIfAbsent(propertySetting.getPropertyName(), k -> new ArrayList<Map<String, String>>());
@SuppressWarnings("unchecked")
ArrayList<Map<String, String>> values = (ArrayList<Map<String, String>>) userProfileProperties.get(propertySetting.getPropertyName());
values.add(childPropertyMap);
userProfileProperties.put(propertySetting.getPropertyName(), values);
} else if (parentPropertySetting != null && parentPropertySetting.isMultiValued()){
childPropertyMap.put("key", propertySetting.getPropertyName());
userProfileProperties.computeIfAbsent(parentPropertySetting.getPropertyName(), k -> new ArrayList<Map<String, String>>());
@SuppressWarnings("unchecked")
ArrayList<Map<String, String>> values = (ArrayList<Map<String, String>>) userProfileProperties.get(parentPropertySetting.getPropertyName());
values.add(childPropertyMap);
userProfileProperties.put(parentPropertySetting.getPropertyName(), values);
} else {
userProfileProperties.put(propertyName.toLowerCase(), propertyValue);
}
}
String warnMessage = null;
try {
saveProfile(userName, profileEntity);
saveProfile(userName, userProfileProperties);
} catch (ObjectNotFoundException e) {
// Not a mandatory operation such as IDM store updates
// This may happen when user is disabled
Expand Down Expand Up @@ -1748,14 +1828,14 @@ private boolean isEmailAlreadyExists(String username, String email) throws Excep
return usersLength > 1 || (usersLength == 1 && !StringUtils.equals(users.load(0, 1)[0].getUserName(), username));
}

private void saveProfile(String username, ProfileEntity profileEntity) throws Exception {
private void saveProfile(String username, Map<String, Object> profileProperties) throws Exception {
Identity userIdentity = getUserIdentity(username);
if (userIdentity == null) {
throw new ObjectNotFoundException("User identity of " + username + " wasn't found. It can be due to a disabled user.");
} else {
Profile profile = userIdentity.getProfile();

Set<Entry<String, Object>> profileEntries = profileEntity.getDataEntity().entrySet();
Set<Entry<String, Object>> profileEntries = profileProperties.entrySet();
for (Entry<String, Object> entry : profileEntries) {
String name = entry.getKey();
Object value = entry.getValue();
Expand Down Expand Up @@ -1883,100 +1963,6 @@ private void updateProfileField(Profile profile,
} finally {
uploadService.removeUploadResource(value.toString());
}
} else if (Profile.CONTACT_IMS.equals(name)) {
@SuppressWarnings("unchecked")
List<IMEntity> imEntities = (List<IMEntity>) value;
if (imEntities == null || imEntities.isEmpty()) {
profile.setProperty(Profile.CONTACT_IMS, Collections.emptyList());
} else {
List<Map<String, String>> imMaps = new ArrayList<>();
for (IMEntity imEntity : imEntities) {
String imType = imEntity.getImType();
String imId = imEntity.getImId();
if (StringUtils.isBlank(imId) || StringUtils.isBlank(imType)) {
continue;
}
Map<String, String> imMap = new HashMap<>();
imMap.put("key", imType);
imMap.put("value", imId);
imMaps.add(imMap);
}
profile.setProperty(Profile.CONTACT_IMS, imMaps);
}

if (save) {
identityManager.updateProfile(profile, getCurrentUser(), true);
}
} else if (Profile.CONTACT_PHONES.equals(name)) {
@SuppressWarnings("unchecked")
List<PhoneEntity> phoneEntities = (List<PhoneEntity>) value;
if (phoneEntities == null || phoneEntities.isEmpty()) {
profile.setProperty(Profile.CONTACT_PHONES, Collections.emptyList());
} else {
List<Map<String, String>> phoneMaps = new ArrayList<>();
for (PhoneEntity phoneEntity : phoneEntities) {
String phoneType = phoneEntity.getPhoneType();
String phoneNumber = phoneEntity.getPhoneNumber();
if (StringUtils.isBlank(phoneType) || StringUtils.isBlank(phoneNumber)) {
continue;
}
Map<String, String> phoneMap = new HashMap<>();
phoneMap.put("key", phoneType);
phoneMap.put("value", phoneNumber);
phoneMaps.add(phoneMap);
}
profile.setProperty(Profile.CONTACT_PHONES, phoneMaps);
}

if (save) {
identityManager.updateProfile(profile, getCurrentUser(), true);
}
} else if (Profile.CONTACT_URLS.equals(name)) {
@SuppressWarnings("unchecked")
List<URLEntity> urlEntities = (List<URLEntity>) value;
if (urlEntities == null || urlEntities.isEmpty()) {
profile.setProperty(Profile.CONTACT_URLS, Collections.emptyList());
} else {
List<Map<String, String>> urlMaps = new ArrayList<>();
for (URLEntity urlEntity : urlEntities) {
String url = urlEntity.getUrl();
if (StringUtils.isBlank(url)) {
continue;
}
Map<String, String> urlMap = new HashMap<>();
urlMap.put("value", url);
urlMaps.add(urlMap);
}
profile.setProperty(Profile.CONTACT_URLS, urlMaps);
}

if (save) {
identityManager.updateProfile(profile, getCurrentUser(), true);
}
} else if (Profile.EXPERIENCES.equals(name)) {
@SuppressWarnings("unchecked")
List<ExperienceEntity> experienceEntities = (List<ExperienceEntity>) value;
if (experienceEntities == null || experienceEntities.isEmpty()) {
profile.setProperty(Profile.EXPERIENCES, Collections.emptyList());
} else {
List<Map<String, Object>> experienceMaps = new ArrayList<>();
for (ExperienceEntity experienceEntity : experienceEntities) {
Map<String, Object> experienceMap = new HashMap<>();
experienceMap.put(Profile.EXPERIENCES_ID, experienceEntity.getId());
experienceMap.put(Profile.EXPERIENCES_COMPANY, experienceEntity.getCompany());
experienceMap.put(Profile.EXPERIENCES_DESCRIPTION, experienceEntity.getDescription());
experienceMap.put(Profile.EXPERIENCES_POSITION, experienceEntity.getPosition());
experienceMap.put(Profile.EXPERIENCES_SKILLS, experienceEntity.getSkills());
experienceMap.put(Profile.EXPERIENCES_IS_CURRENT, experienceEntity.getIsCurrent());
experienceMap.put(Profile.EXPERIENCES_START_DATE, experienceEntity.getStartDate());
experienceMap.put(Profile.EXPERIENCES_END_DATE, experienceEntity.getEndDate());
experienceMaps.add(experienceMap);
}
profile.setProperty(Profile.EXPERIENCES, experienceMaps);
}
if (save) {
identityManager.updateProfile(profile, getCurrentUser(), true);
}
} else {
profile.setProperty(name, value);
if (save) {
Expand Down
Loading