package org.molgenis.auth; import com.google.common.collect.Iterators; import org.molgenis.data.AbstractRepositoryDecorator; import org.molgenis.data.DataService; import org.molgenis.data.Repository; import org.molgenis.data.support.QueryImpl; import org.springframework.security.crypto.password.PasswordEncoder; import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; import static java.util.Objects.requireNonNull; import static org.molgenis.auth.AuthorityMetaData.ROLE; import static org.molgenis.auth.GroupMemberMetaData.GROUP_MEMBER; import static org.molgenis.auth.UserAuthorityMetaData.USER; import static org.molgenis.auth.UserAuthorityMetaData.USER_AUTHORITY; import static org.molgenis.security.core.utils.SecurityUtils.AUTHORITY_SU; public class UserRepositoryDecorator extends AbstractRepositoryDecorator<User> { private static final int BATCH_SIZE = 1000; private final Repository<User> decoratedRepository; private final UserAuthorityFactory userAuthorityFactory; private final DataService dataService; private final PasswordEncoder passwordEncoder; public UserRepositoryDecorator(Repository<User> decoratedRepository, UserAuthorityFactory userAuthorityFactory, DataService dataService, PasswordEncoder passwordEncoder) { this.decoratedRepository = requireNonNull(decoratedRepository); this.userAuthorityFactory = requireNonNull(userAuthorityFactory); this.dataService = requireNonNull(dataService); this.passwordEncoder = requireNonNull(passwordEncoder); } @Override protected Repository<User> delegate() { return decoratedRepository; } @Override public void add(User entity) { encodePassword(entity); decoratedRepository.add(entity); addSuperuserAuthority(entity); } @Override public void update(User entity) { updatePassword(entity); decoratedRepository.update(entity); updateSuperuserAuthority(entity); } @Override public Integer add(Stream<User> entities) { AtomicInteger count = new AtomicInteger(); Iterators.partition(entities.iterator(), BATCH_SIZE).forEachRemaining(users -> { users.forEach(this::encodePassword); Integer batchCount = decoratedRepository.add(users.stream()); count.addAndGet(batchCount); users.forEach(this::addSuperuserAuthority); }); return count.get(); } @Override public void update(Stream<User> entities) { entities = entities.map(entity -> { updatePassword(entity); return entity; }); decoratedRepository.update(entities); } private void updatePassword(User user) { User currentUser = findOneById(user.getId()); String currentPassword = currentUser.getPassword(); String password = user.getPassword(); //password is updated if (!currentPassword.equals(password)) { password = passwordEncoder.encode(user.getPassword()); } user.setPassword(password); } private void encodePassword(User user) { String password = user.getPassword(); String encodedPassword = passwordEncoder.encode(password); user.setPassword(encodedPassword); } private void addSuperuserAuthority(User user) { Boolean isSuperuser = user.isSuperuser(); if (isSuperuser != null && isSuperuser) { UserAuthority userAuthority = userAuthorityFactory.create(); userAuthority.setUser(user); userAuthority.setRole(AUTHORITY_SU); dataService.add(USER_AUTHORITY, userAuthority); } } private void updateSuperuserAuthority(User user) { UserAuthority suAuthority = dataService .findOne(USER_AUTHORITY, new QueryImpl<UserAuthority>().eq(USER, user).and().eq(ROLE, AUTHORITY_SU), UserAuthority.class); Boolean isSuperuser = user.isSuperuser(); if (isSuperuser != null && isSuperuser) { if (suAuthority == null) { UserAuthority userAuthority = userAuthorityFactory.create(); userAuthority.setUser(user); userAuthority.setRole(AUTHORITY_SU); dataService.add(USER_AUTHORITY, userAuthority); } } else { if (suAuthority != null) { dataService.deleteById(USER_AUTHORITY, suAuthority.getId()); } } } @Override public void delete(User entity) { deleteUserAuthoritiesAndGroupMember(entity); decoratedRepository.delete(entity); } @Override public void delete(Stream<User> entities) { entities = entities.map(entity -> { deleteUserAuthoritiesAndGroupMember(entity); return entity; }); decoratedRepository.delete(entities); } @Override public void deleteById(Object id) { deleteUserAuthoritiesAndGroupMember(findOneById(id)); decoratedRepository.deleteById(id); } @Override public void deleteAll(Stream<Object> ids) { ids = ids.map(id -> { deleteUserAuthoritiesAndGroupMember(findOneById(id)); return id; }); decoratedRepository.deleteAll(ids); } private void deleteUserAuthoritiesAndGroupMember(User user) { Stream<UserAuthority> userAuthorities = dataService .findAll(USER_AUTHORITY, new QueryImpl<UserAuthority>().eq(UserAuthorityMetaData.USER, user), UserAuthority.class); dataService.delete(USER_AUTHORITY, userAuthorities); Stream<GroupMember> groupMembers = dataService .findAll(GROUP_MEMBER, new QueryImpl<GroupMember>().eq(GroupMemberMetaData.USER, user), GroupMember.class); dataService.delete(GROUP_MEMBER, groupMembers); } @Override public void deleteAll() { throw new UnsupportedOperationException("Deleting all users is not supported."); } }