public PropagationByResource update(final SyncopeUser user, final UserMod userMod)
throws SyncopeClientCompositeErrorException {
PropagationByResource propByRes = new PropagationByResource();
SyncopeClientCompositeErrorException scce = new SyncopeClientCompositeErrorException(HttpStatus.BAD_REQUEST);
// when requesting to add user to new resources, either directly or
// through role subscription, password is mandatory (issue 147)
// first, let's take current resources into account
Set<String> currentResources = user.getResourceNames();
// password
if (userMod.getPassword() != null) {
int passwordHistorySize = 0;
try {
Policy policy = policyDAO.getGlobalPasswordPolicy();
PasswordPolicySpec passwordPolicy = policy.getSpecification();
passwordHistorySize = passwordPolicy.getHistoryLength();
} catch (Exception ignore) {
// ignore exceptions
}
user.setPassword(userMod.getPassword(), getCipherAlgoritm(), passwordHistorySize);
user.setChangePwdDate(new Date());
propByRes.addAll(PropagationOperation.UPDATE, currentResources);
}
// username
if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) {
String oldUsername = user.getUsername();
user.setUsername(userMod.getUsername());
propByRes.addAll(PropagationOperation.UPDATE, currentResources);
for (ExternalResource resource : user.getResources()) {
for (SchemaMapping mapping : resource.getMappings()) {
if (mapping.isAccountid() && mapping.getIntMappingType() == IntMappingType.Username) {
propByRes.addOldAccountId(resource.getName(), oldUsername);
}
}
}
}
// attributes, derived attributes, virtual attributes and resources
propByRes.merge(fill(user, userMod, AttributableUtil.getInstance(AttributableType.USER), scce));
// store the role ids of membership required to be added
Set<Long> membershipToBeAddedRoleIds = new HashSet<Long>();
for (MembershipMod membToBeAdded : userMod.getMembershipsToBeAdded()) {
membershipToBeAddedRoleIds.add(membToBeAdded.getRole());
}
final Set<String> toBeDeprovisioned = new HashSet<String>();
final Set<String> toBeProvisioned = new HashSet<String>();
// memberships to be removed
Membership membership = null;
for (Long membershipId : userMod.getMembershipsToBeRemoved()) {
LOG.debug("Membership to be removed: {}", membershipId);
membership = membershipDAO.find(membershipId);
if (membership == null) {
LOG.debug("Invalid membership id specified to be removed: {}", membershipId);
} else {
if (!membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
toBeDeprovisioned.addAll(membership.getSyncopeRole().getResourceNames());
}
// In order to make the removeMembership() below to work,
// we need to be sure to take exactly the same membership
// of the user object currently in memory (which has potentially
// some modifications compared to the one stored in the DB
membership = user.getMembership(membership.getSyncopeRole().getId());
if (membershipToBeAddedRoleIds.contains(membership.getSyncopeRole().getId())) {
Set<Long> attributeIds = new HashSet<Long>(membership.getAttributes().size());
for (AbstractAttr attribute : membership.getAttributes()) {
attributeIds.add(attribute.getId());
}
for (Long attributeId : attributeIds) {
attributeDAO.delete(attributeId, MAttr.class);
}
attributeIds.clear();
// remove derived attributes
for (AbstractDerAttr derAttr : membership.getDerivedAttributes()) {
attributeIds.add(derAttr.getId());
}
for (Long derAttrId : attributeIds) {
derAttrDAO.delete(derAttrId, MDerAttr.class);
}
attributeIds.clear();
// remove virtual attributes
for (AbstractVirAttr virAttr : membership.getVirtualAttributes()) {
attributeIds.add(virAttr.getId());
}
for (Long virAttrId : attributeIds) {
virAttrDAO.delete(virAttrId, MVirAttr.class);
}
attributeIds.clear();
} else {
user.removeMembership(membership);
membershipDAO.delete(membershipId);
}
}
}
// memberships to be added
for (MembershipMod membershipMod : userMod.getMembershipsToBeAdded()) {
LOG.debug("Membership to be added: role({})", membershipMod.getRole());
SyncopeRole role = roleDAO.find(membershipMod.getRole());
if (role == null) {
LOG.debug("Ignoring invalid role {}", membershipMod.getRole());
} else {
membership = user.getMembership(role.getId());
if (membership == null) {
membership = new Membership();
membership.setSyncopeRole(role);
membership.setSyncopeUser(user);
user.addMembership(membership);
toBeProvisioned.addAll(role.getResourceNames());
}
propByRes.merge(fill(membership, membershipMod,
AttributableUtil.getInstance(AttributableType.MEMBERSHIP), scce));
}
}
// now, let's see if there are new resource subscriptions without providing password
Set<String> updatedResources = user.getResourceNames();
updatedResources.removeAll(currentResources);
if (!updatedResources.isEmpty() && StringUtils.isBlank(userMod.getPassword())) {
SyncopeClientException sce = new SyncopeClientException(SyncopeClientExceptionType.RequiredValuesMissing);
sce.addElement("password cannot be empty " + "when subscribing to new resources");
scce.addException(sce);
throw scce;
}
propByRes.addAll(PropagationOperation.DELETE, toBeDeprovisioned);