Java tutorial
/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you 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.apache.syncope.core.rest.data; import java.util.Collections; import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Set; import javax.annotation.Resource; import org.apache.commons.lang3.StringUtils; import org.apache.syncope.common.mod.MembershipMod; import org.apache.syncope.common.mod.UserMod; import org.apache.syncope.common.to.MembershipTO; import org.apache.syncope.common.to.UserTO; import org.apache.syncope.common.types.AttributableType; import org.apache.syncope.common.types.CipherAlgorithm; import org.apache.syncope.common.types.IntMappingType; import org.apache.syncope.common.types.PasswordPolicySpec; import org.apache.syncope.common.types.ResourceOperation; import org.apache.syncope.common.types.ClientExceptionType; import org.apache.syncope.common.util.BeanUtils; import org.apache.syncope.common.SyncopeClientCompositeException; import org.apache.syncope.common.SyncopeClientException; import org.apache.syncope.common.mod.AttributeMod; import org.apache.syncope.core.connid.ConnObjectUtil; import org.apache.syncope.core.persistence.beans.AbstractAttr; import org.apache.syncope.core.persistence.beans.AbstractDerAttr; import org.apache.syncope.core.persistence.beans.AbstractMappingItem; import org.apache.syncope.core.persistence.beans.AbstractVirAttr; import org.apache.syncope.core.persistence.beans.ExternalResource; import org.apache.syncope.core.persistence.beans.PasswordPolicy; import org.apache.syncope.core.persistence.beans.membership.MAttr; import org.apache.syncope.core.persistence.beans.membership.MDerAttr; import org.apache.syncope.core.persistence.beans.membership.MVirAttr; import org.apache.syncope.core.persistence.beans.membership.Membership; import org.apache.syncope.core.persistence.beans.role.SyncopeRole; import org.apache.syncope.core.persistence.beans.user.SyncopeUser; import org.apache.syncope.core.persistence.dao.NotFoundException; import org.apache.syncope.core.propagation.PropagationByResource; import org.apache.syncope.core.rest.controller.UnauthorizedRoleException; import org.apache.syncope.core.util.AttributableUtil; import org.apache.syncope.core.util.EntitlementUtil; import org.apache.syncope.core.util.Encryptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @Component @Transactional(rollbackFor = { Throwable.class }) public class UserDataBinder extends AbstractAttributableDataBinder { private static final String[] IGNORE_USER_PROPERTIES = { "memberships", "attrs", "derAttrs", "virAttrs", "resources" }; @Autowired private ConnObjectUtil connObjectUtil; @Resource(name = "adminUser") private String adminUser; @Resource(name = "anonymousUser") private String anonymousUser; private final Encryptor encryptor = Encryptor.getInstance(); @Transactional(readOnly = true) public SyncopeUser getUserFromId(final Long userId) { if (userId == null) { throw new NotFoundException("Null user id"); } SyncopeUser user = userDAO.find(userId); if (user == null) { throw new NotFoundException("User " + userId); } // Allows anonymous (during self-registration) and self (during self-update) to read own SyncopeUser, // otherwise goes thorugh security checks to see if needed role entitlements are owned if (!EntitlementUtil.getAuthenticatedUsername().equals(anonymousUser) && !EntitlementUtil.getAuthenticatedUsername().equals(user.getUsername())) { Set<Long> roleIds = user.getRoleIds(); Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()); roleIds.removeAll(adminRoleIds); if (!roleIds.isEmpty()) { throw new UnauthorizedRoleException(roleIds); } } return user; } @Transactional(readOnly = true) public Membership getMembershipFromId(final Long membershipId) { if (membershipId == null) { throw new NotFoundException("Null membership id"); } Membership membership = membershipDAO.find(membershipId); if (membership == null) { throw new NotFoundException("Membership " + membershipId); } return membership; } @Transactional(readOnly = true) public Set<String> getResourceNamesForUserId(final Long userId) { return getUserFromId(userId).getResourceNames(); } @Transactional(readOnly = true) public UserTO getAuthenticatedUserTO() { final UserTO authUserTO; final String authUsername = SecurityContextHolder.getContext().getAuthentication().getName(); if (anonymousUser.equals(authUsername)) { authUserTO = new UserTO(); authUserTO.setId(-2); authUserTO.setUsername(anonymousUser); } else if (adminUser.equals(authUsername)) { authUserTO = new UserTO(); authUserTO.setId(-1); authUserTO.setUsername(adminUser); } else { SyncopeUser authUser = userDAO.find(authUsername); authUserTO = getUserTO(authUser); } return authUserTO; } @Transactional(readOnly = true) public boolean verifyPassword(final String username, final String password) { return verifyPassword(getUserFromUsername(username), password); } @Transactional(readOnly = true) public boolean verifyPassword(final SyncopeUser user, final String password) { return encryptor.verify(password, user.getCipherAlgorithm(), user.getPassword()); } @Transactional(readOnly = true) public SyncopeUser getUserFromUsername(final String username) { if (username == null) { throw new NotFoundException("Null username"); } SyncopeUser user = userDAO.find(username); if (user == null) { throw new NotFoundException("User " + username); } Set<Long> roleIds = user.getRoleIds(); Set<Long> adminRoleIds = EntitlementUtil.getRoleIds(EntitlementUtil.getOwnedEntitlementNames()); roleIds.removeAll(adminRoleIds); if (!roleIds.isEmpty()) { throw new UnauthorizedRoleException(roleIds); } return user; } /** * Get predefined password cipher algorithm from SyncopeConf. * * @return cipher algorithm. */ private CipherAlgorithm getPredefinedCipherAlgoritm() { final String algorithm = confDAO.find("password.cipher.algorithm", CipherAlgorithm.AES.name()).getValues() .get(0).getStringValue(); try { return CipherAlgorithm.valueOf(algorithm); } catch (IllegalArgumentException e) { throw new NotFoundException("Cipher algorithm " + algorithm); } } private void setPassword(final SyncopeUser user, final String password, final SyncopeClientCompositeException scce) { int passwordHistorySize = 0; PasswordPolicy policy = policyDAO.getGlobalPasswordPolicy(); if (policy != null && policy.getSpecification(PasswordPolicySpec.class) != null) { passwordHistorySize = policy.getSpecification(PasswordPolicySpec.class).getHistoryLength(); } try { user.setPassword(password, getPredefinedCipherAlgoritm(), passwordHistorySize); } catch (NotFoundException e) { final SyncopeClientException invalidCiperAlgorithm = SyncopeClientException .build(ClientExceptionType.NotFound); invalidCiperAlgorithm.getElements().add(e.getMessage()); scce.addException(invalidCiperAlgorithm); throw scce; } } public void create(final SyncopeUser user, final UserTO userTO) { SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); // memberships SyncopeRole role; for (MembershipTO membershipTO : userTO.getMemberships()) { role = roleDAO.find(membershipTO.getRoleId()); if (role == null) { if (LOG.isDebugEnabled()) { LOG.debug("Ignoring invalid role " + membershipTO.getRoleName()); } } else { Membership membership = null; if (user.getId() != null) { membership = user.getMembership(role.getId()) == null ? membershipDAO.find(user, role) : user.getMembership(role.getId()); } if (membership == null) { membership = new Membership(); membership.setSyncopeRole(role); membership.setSyncopeUser(user); user.addMembership(membership); } fill(membership, membershipTO, AttributableUtil.getInstance(AttributableType.MEMBERSHIP), scce); } } // attributes, derived attributes, virtual attributes and resources fill(user, userTO, AttributableUtil.getInstance(AttributableType.USER), scce); // set password if (StringUtils.isBlank(userTO.getPassword())) { LOG.error("No password provided"); } else { setPassword(user, userTO.getPassword(), scce); } // set username user.setUsername(userTO.getUsername()); } /** * Update user, given UserMod. * * @param toBeUpdated user to be updated * @param userMod bean containing update request * @return updated user + propagation by resource * @see PropagationByResource */ public PropagationByResource update(final SyncopeUser toBeUpdated, final UserMod userMod) { // Re-merge any pending change from workflow tasks SyncopeUser user = userDAO.save(toBeUpdated); PropagationByResource propByRes = new PropagationByResource(); SyncopeClientCompositeException scce = SyncopeClientException.buildComposite(); Set<String> currentResources = user.getResourceNames(); // password if (StringUtils.isNotBlank(userMod.getPassword())) { setPassword(user, userMod.getPassword(), scce); user.setChangePwdDate(new Date()); propByRes.addAll(ResourceOperation.UPDATE, currentResources); } // username if (userMod.getUsername() != null && !userMod.getUsername().equals(user.getUsername())) { String oldUsername = user.getUsername(); user.setUsername(userMod.getUsername()); propByRes.addAll(ResourceOperation.UPDATE, currentResources); for (ExternalResource resource : user.getResources()) { for (AbstractMappingItem mapItem : resource.getUmapping().getItems()) { if (mapItem.isAccountid() && mapItem.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.getMembershipsToAdd()) { membershipToBeAddedRoleIds.add(membToBeAdded.getRole()); } final Set<String> toBeDeprovisioned = new HashSet<String>(); final Set<String> toBeProvisioned = new HashSet<String>(); // memberships to be removed for (Long membershipId : userMod.getMembershipsToRemove()) { LOG.debug("Membership to be removed: {}", membershipId); Membership 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.getAttrs().size()); for (AbstractAttr attribute : membership.getAttrs()) { attributeIds.add(attribute.getId()); } for (Long attributeId : attributeIds) { attrDAO.delete(attributeId, MAttr.class); } attributeIds.clear(); // remove derived attributes for (AbstractDerAttr derAttr : membership.getDerAttrs()) { attributeIds.add(derAttr.getId()); } for (Long derAttrId : attributeIds) { derAttrDAO.delete(derAttrId, MDerAttr.class); } attributeIds.clear(); // remove virtual attributes for (AbstractVirAttr virAttr : membership.getVirAttrs()) { 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.getMembershipsToAdd()) { 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 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)); } } propByRes.addAll(ResourceOperation.DELETE, toBeDeprovisioned); propByRes.addAll(ResourceOperation.UPDATE, toBeProvisioned); /** * In case of new memberships all the current resources have to be updated in order to propagate new role and * membership attribute values. */ if (!toBeDeprovisioned.isEmpty() || !toBeProvisioned.isEmpty()) { currentResources.removeAll(toBeDeprovisioned); propByRes.addAll(ResourceOperation.UPDATE, currentResources); } return propByRes; } @Transactional(readOnly = true) public UserTO getUserTO(final SyncopeUser user) { UserTO userTO = new UserTO(); BeanUtils.copyProperties(user, userTO, IGNORE_USER_PROPERTIES); connObjectUtil.retrieveVirAttrValues(user, AttributableUtil.getInstance(AttributableType.USER)); fillTO(userTO, user.getAttrs(), user.getDerAttrs(), user.getVirAttrs(), user.getResources()); MembershipTO membershipTO; for (Membership membership : user.getMemberships()) { membershipTO = new MembershipTO(); // set sys info membershipTO.setCreator(membership.getCreator()); membershipTO.setCreationDate(membership.getCreationDate()); membershipTO.setLastModifier(membership.getLastModifier()); membershipTO.setLastChangeDate(membership.getLastChangeDate()); membershipTO.setId(membership.getId()); membershipTO.setRoleId(membership.getSyncopeRole().getId()); membershipTO.setRoleName(membership.getSyncopeRole().getName()); // SYNCOPE-458 retrieve also membership virtual attributes connObjectUtil.retrieveVirAttrValues(membership, AttributableUtil.getInstance(AttributableType.MEMBERSHIP)); fillTO(membershipTO, membership.getAttrs(), membership.getDerAttrs(), membership.getVirAttrs(), Collections.<ExternalResource>emptyList()); userTO.getMemberships().add(membershipTO); } return userTO; } @Transactional(readOnly = true) public UserTO getUserTO(final String username) { return getUserTO(getUserFromUsername(username)); } @Transactional(readOnly = true) public UserTO getUserTO(final Long userId) { return getUserTO(getUserFromId(userId)); } /** * SYNCOPE-459: build virtual attribute changes in case no other changes were made. * * @param id user id * @param vAttrsToBeRemoved virtual attributes to be removed. * @param vAttrsToBeUpdated virtual attributes to be updated. * @return operations to be performed on external resources for virtual attributes changes */ public PropagationByResource fillVirtual(final Long id, final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated) { return fillVirtual(getUserFromId(id), vAttrsToBeRemoved, vAttrsToBeUpdated, AttributableUtil.getInstance(AttributableType.USER)); } /** * SYNCOPE-501: build membership virtual attribute changes in case no other changes were made. * * @param userId user id * @param roleId role id * @param membershipId membership id * @param vAttrsToBeRemoved virtual attributes to be removed. * @param vAttrsToBeUpdated virtual attributes to be updated. * @param isRemoval flag to check if fill is on removed or added membership * @return operations to be performed on external resources for membership virtual attributes changes */ public PropagationByResource fillMembershipVirtual(final Long userId, final Long roleId, final Long membershipId, final Set<String> vAttrsToBeRemoved, final Set<AttributeMod> vAttrsToBeUpdated, final boolean isRemoval) { final Membership membership = membershipId == null ? getUserFromId(userId).getMembership(roleId) : getMembershipFromId(membershipId); return membership == null ? new PropagationByResource() : isRemoval ? fillVirtual(membership, membership.getVirAttrs() == null ? Collections.<String>emptySet() : getAttributeNames(membership.getVirAttrs()), vAttrsToBeUpdated, AttributableUtil.getInstance(AttributableType.MEMBERSHIP)) : fillVirtual(membership, vAttrsToBeRemoved, vAttrsToBeUpdated, AttributableUtil.getInstance(AttributableType.MEMBERSHIP)); } private Set<String> getAttributeNames(final List<? extends AbstractVirAttr> virAttrs) { final Set<String> virAttrNames = new HashSet<String>(); for (AbstractVirAttr attr : virAttrs) { virAttrNames.add(attr.getSchema().getName()); } return virAttrNames; } }