/* license-start
*
* Copyright (C) 2008 - 2013 Crispico, <http://www.crispico.com/>.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details, at <http://www.gnu.org/licenses/>.
*
* Contributors:
* Crispico - Initial API and implementation
*
* license-end
*/
package org.flowerplatform.web.security.service;
import static org.flowerplatform.web.security.sandbox.SecurityEntityAdaptor.ANONYMOUS;
import java.io.File;
import java.text.MessageFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.flowerplatform.common.CommonPlugin;
import org.flowerplatform.common.FlowerProperties.AddBooleanProperty;
import org.flowerplatform.common.FlowerProperties.AddIntegerProperty;
import org.flowerplatform.common.FlowerProperties.AddProperty;
import org.flowerplatform.communication.CommunicationPlugin;
import org.flowerplatform.communication.channel.CommunicationChannel;
import org.flowerplatform.communication.service.ServiceInvocationContext;
import org.flowerplatform.communication.service.ServiceRegistry;
import org.flowerplatform.web.WebPlugin;
import org.flowerplatform.web.database.DatabaseOperation;
import org.flowerplatform.web.database.DatabaseOperationWrapper;
import org.flowerplatform.web.entity.EntityFactory;
import org.flowerplatform.web.entity.Group;
import org.flowerplatform.web.entity.GroupUser;
import org.flowerplatform.web.entity.NamedEntity;
import org.flowerplatform.web.entity.Organization;
import org.flowerplatform.web.entity.OrganizationMembershipStatus;
import org.flowerplatform.web.entity.OrganizationUser;
import org.flowerplatform.web.entity.PermissionEntity;
import org.flowerplatform.web.entity.SVNCommentEntity;
import org.flowerplatform.web.entity.User;
import org.flowerplatform.web.entity.dto.NamedDto;
import org.flowerplatform.web.security.dto.GroupAdminUIDto;
import org.flowerplatform.web.security.dto.OrganizationAdminUIDto;
import org.flowerplatform.web.security.dto.OrganizationUserAdminUIDto;
import org.flowerplatform.web.security.dto.PermissionAdminUIDto;
import org.flowerplatform.web.security.dto.UserAdminUIDto;
import org.flowerplatform.web.security.dto.User_CurrentUserLoggedInDto;
import org.flowerplatform.web.security.mail.SendMailService;
import org.flowerplatform.web.security.sandbox.FlowerWebPrincipal;
import org.flowerplatform.web.security.sandbox.SecurityEntityAdaptor;
import org.flowerplatform.web.security.sandbox.SecurityUtils;
import org.hibernate.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service used to make CRUD operations on <code>User</code> entity.
*
* @see BootstrapService#initialize()
* @see ServiceRegistry
*
* @author Cristi
* @author Cristina
* @author Mariana
*
*
*/
public class UserService extends ServiceObservable {
public static final String SERVICE_ID = "userService";
private static final Logger logger = LoggerFactory.getLogger(UserService.class);
public static UserService getInstance() {
return (UserService) CommunicationPlugin.getInstance().getServiceRegistry().getService(SERVICE_ID);
}
/**
* Converts a {@link User} to {@link UserAdminUIDto}. Filters the info that will be sent to the client,
* so any {@link Organization}s or {@link Group}s the <code>user</code> belongs to, but the client that
* requested the info does not have permissions over will not be sent.
*
* @see #findAllAsAdminUIDto()
* @see #findByIdAsAdminUIDto()
*
*/
private UserAdminUIDto convertUserToUserAdminUIDto(User user) {
UserAdminUIDto dto = new UserAdminUIDto();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setEmail(user.getEmail());
dto.setIsActivated(user.isActivated());
dto.setLogin(user.getLogin());
HashSet<GroupAdminUIDto> groups = new HashSet<GroupAdminUIDto>();
if (user.getGroupUsers() != null) {
for (GroupUser groupUser : user.getGroupUsers()) {
try {
if (user.getId() != CommunicationPlugin.tlCurrentPrincipal.get().getUserId())
SecurityUtils.checkAdminSecurityEntitiesPermission(PermissionEntity.GROUP_PREFIX + groupUser.getGroup().getName());
Group group = groupUser.getGroup();
NamedDto orgDto = null;
if (group.getOrganization() != null) {
orgDto = new NamedDto(group.getOrganization().getId(), group.getOrganization().getName());
}
groups.add(new GroupAdminUIDto(group.getId(), group.getName(), orgDto));
} catch (SecurityException e) {
// do nothing
}
}
dto.setGroups(groups);
}
HashSet<OrganizationUserAdminUIDto> organizations = new HashSet<OrganizationUserAdminUIDto>();
if (user.getOrganizationUsers() != null) {
for (OrganizationUser organizationUser : user.getOrganizationUsers()) {
try {
if (user.getId() != CommunicationPlugin.tlCurrentPrincipal.get().getUserId())
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(organizationUser.getOrganization(), null));
OrganizationUserAdminUIDto ouDto = new OrganizationUserAdminUIDto();
ouDto.setId(organizationUser.getId());
ouDto.setOrganization(OrganizationService.getInstance().convertOrganizationToOrganizationAdminUIDto(organizationUser.getOrganization(), user));
ouDto.setStatus(organizationUser.getStatus());
organizations.add(ouDto);
} catch (SecurityException e) {
// do nothing
}
}
dto.setOrganizationUsers(organizations);
}
return dto;
}
/**
* @author Cristi
*/
public User_CurrentUserLoggedInDto convertUserToUser_LoggedInDto(User user) {
User_CurrentUserLoggedInDto dto = new User_CurrentUserLoggedInDto();
dto.setId(user.getId());
dto.setName(user.getName());
dto.setEmail(user.getEmail());
dto.setLogin(user.getLogin());
dto.setIsAdmin(user.isAdmin());
dto.setHasAdminSecurityEntitiesPermissions(SecurityUtils.hasAdminSecurityEntitiesPermission());
return dto;
}
/**
* Finds the user given by its id and returns an {@link UserAdminUIDto}.
*
*/
public UserAdminUIDto findByIdAsAdminUIDto(final long id) {
logger.debug("Find user with id = {}", id);
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
User user = wrapper.find(User.class, id);
if (user == null)
throw new RuntimeException(String.format("User with id=%s was not found in the DB.", id));
String groupOwners = SecurityEntityAdaptor.toCsvString(getUserGroups(user), Collections.<GroupAdminUIDto>emptyList(), PermissionEntity.GROUP_PREFIX);
String orgOwners = SecurityEntityAdaptor.toCsvString(getOrganizationsWhereUserBelongs(user, true), null, PermissionEntity.ORGANIZATION_PREFIX);
String owners = groupOwners + (groupOwners.length() > 0 && orgOwners.length() > 0 ? "," : "") + orgOwners;
try {
if (id != CommunicationPlugin.tlCurrentPrincipal.get().getUserId()) {
SecurityUtils.checkAdminSecurityEntitiesPermission(owners);
}
} catch (SecurityException e) {
throw new RuntimeException(String.format("User with id=%s is not available.", id));
}
wrapper.setOperationResult(convertUserToUserAdminUIDto(user));
}
});
return (UserAdminUIDto) wrapper.getOperationResult();
}
/**
* Returns the {@link User}s that belong to the {@link Organization}s, {@link Group}s
* or whose <code>login</code>s match the filter. I.e. filter is
* <ul>
* <li>#org2, get all users that belong to #org2
* <li>@org3.group, get all users that belong to @org3.group
* <li>#org2, @org3.group, get users that belong to #org2 or @org3.group
* <li>$user1, get user with login user3
* </ul>
*
* <p>
* Supports wildcards, i.e. #*org3*, @*org2*.*, $*user* is a valid filter.
*
* <p>
* If the client that requested the info does not have rights over <b>any</b> of the {@link Organization}s
* or {@link Group}s a matched user belongs to, the user will not be returned.
*
* <p>
* If <code>filter</code> is <code>null</code>, get all the users in the DB, and returns only those that the
* user has rights over.
*/
public List<UserAdminUIDto> findAllAsAdminUIDto(ServiceInvocationContext context, String filter) {
logger.debug("Find users filtered by = {}", filter);
if (filter == null || filter.equals("")) {
filter = "$%";
}
filter = filter.replaceAll("\\*", "%");
final String[] entities = filter.split("\\s+");
final List<UserAdminUIDto> result = new ArrayList<UserAdminUIDto>();
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
boolean union = true; // true for OR, false for AND
List<User> users = new ArrayList<User>();
for (String entity : entities) {
Query query = null;
if (entity.startsWith(PermissionEntity.ORGANIZATION_PREFIX)) {
entity = addDefaultWildcards(entity.substring(PermissionEntity.ORGANIZATION_PREFIX.length()));
query = wrapper.createQuery("SELECT u " +
"FROM Organization o JOIN o.organizationUsers ou JOIN ou.user u " +
"WHERE o.name LIKE :organization_name OR o.label LIKE :organization_name");
query.setParameter("organization_name", entity);
} else {
if (entity.startsWith(PermissionEntity.GROUP_PREFIX)) {
entity = addDefaultWildcards(entity.substring(PermissionEntity.GROUP_PREFIX.length()));
query = wrapper.createQuery("SELECT u " +
"FROM Group g JOIN g.groupUsers gu JOIN gu.user u " +
"WHERE g.name LIKE :group_name");
query.setParameter("group_name", entity);
} else {
if (entity.startsWith(PermissionEntity.USER_PREFIX)) {
entity = addDefaultWildcards(entity.substring(PermissionEntity.USER_PREFIX.length()));
query = wrapper.createQuery("SELECT u " +
"FROM User u " +
"WHERE u.name LIKE :user_name OR u.login LIKE :user_name OR u.email LIKE :user_name");
query.setParameter("user_name", entity);
} else {
// filter by status
entity = entity.toUpperCase();
OrganizationMembershipStatus status = null;
for (OrganizationMembershipStatus value : OrganizationMembershipStatus.values()) {
if (value.toString().startsWith(entity) && value.toString().contains(entity)) {
status = value;
break;
}
}
if (status != null) {
query = wrapper.createQuery("SELECT u " +
"FROM User u JOIN u.organizationUsers ou " +
"WHERE ou.status = :status");
query.setParameter("status", status);
} else {
// operator, i.e. || && ,
if (entity.equals("&&")) {
union = false;
}
}
}
}
}
if (query != null) {
@SuppressWarnings("unchecked")
List<User> newList = query.list();
if (union) {
for (User u : newList) {
if (!users.contains(u)) {
users.add(u);
}
}
} else {
for (Iterator<User> it = users.iterator(); it.hasNext(); ) {
User u = it.next();
if (!newList.contains(u)) {
it.remove();
}
}
}
// reset operator
union = true;
}
}
for (User user : users) {
try {
UserAdminUIDto dto = findByIdAsAdminUIDto(user.getId());
result.add(dto);
} catch (Exception e) {
// do nothing
}
}
}
});
return result;
}
/**
* Returns <code>%entity%</code>, if <code>entity</code> does not contain any wildcards already.
*
* @author Mariana
*/
private String addDefaultWildcards(String entity) {
if (!entity.contains("%"))
return "%" + entity + "%";
return entity;
}
/**
* Creates/Updates the {@link User} based on {@link UserAdminUIDto} stored information.
* Returns an error message, or <code>null</code> if there were no errors.
*
*
*/
public String mergeAdminUIDto(final UserAdminUIDto dto) {
logger.debug("Merge user = {}", dto.getLogin());
User initialUser = (User) new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
wrapper.setOperationResult(wrapper.find(User.class, dto.getId()));
}
}).getOperationResult();
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
// first check if there is already a user with the same login
List<User> usersWithSameLogin = wrapper.findByField(User.class, "login", dto.getLogin());
if (dto.getId() == 0 && usersWithSameLogin != null && usersWithSameLogin.size() > 0) {
wrapper.setOperationResult(WebPlugin.getInstance().getMessage("authentication.register.loginAlreadyExists"));
return;
}
if (dto.getLogin().startsWith(ANONYMOUS)) {
if (CommunicationPlugin.tlCurrentPrincipal.get() != null) {
if (dto.getId() == 0 && !((FlowerWebPrincipal) CommunicationPlugin.tlCurrentPrincipal.get()).getUser().isAdmin()) {
wrapper.setOperationResult("You cannot create an anonymous user!");
return;
} else {
if (dto.getId() == CommunicationPlugin.tlCurrentPrincipal.get().getUserId()) {
wrapper.setOperationResult("You cannot edit an anonymous user!"); // don't allow an anonymous user to edit its own details
// note: the permissions logic will take care of the case when another user tries to edit anonymous
// i.e. org1 admin will be allowed to edit anonymous.org1, but not anonymous.org2; FDC admin can edit any user by default
return;
}
}
}
}
User user = wrapper.mergeDto(User.class, dto);
user = wrapper.merge(user);
// check permissions
List<Group> originalGroups = getUserGroups(user);
if (CommunicationPlugin.tlCurrentPrincipal.get() != null &&
CommunicationPlugin.tlCurrentPrincipal.get().getUserId() != user.getId()) {
String groupsCsvList = SecurityEntityAdaptor.toCsvString(originalGroups, dto.getGroups(), PermissionEntity.GROUP_PREFIX);
String orgsCsvList = SecurityEntityAdaptor.toCsvString(getOrganizationsWhereUserBelongs(user, false), dto.getOrganizations(false), PermissionEntity.ORGANIZATION_PREFIX);
String csvList = groupsCsvList + (groupsCsvList.length() > 0 && orgsCsvList.length() > 0 ? "," : "") + orgsCsvList;
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(csvList);
} catch (Exception e) {
// if logged in user does not have permissions over user, then maybe the user has PENDING status in one of logged in user's organizations
// in this case, return the corresponding error message
String orgsWithPendingStatus = SecurityEntityAdaptor.toCsvString(getOrganizationsWhereUserBelongs(user, true), dto.getOrganizations(true), PermissionEntity.ORGANIZATION_PREFIX);
SecurityUtils.checkAdminSecurityEntitiesPermission(orgsWithPendingStatus);
wrapper.setOperationResult("The user cannot be updated while his/her membership is not approved!");
return;
}
}
user.setLogin(dto.getLogin());
// validate email if changing, only if user is not anonymous
if (!dto.getLogin().startsWith(ANONYMOUS)) {
if (dto.getEmail() == null || !dto.getEmail().equals(user.getEmail())) {
if (!SendMailService.getInstance().validate(dto.getEmail())) {
wrapper.setOperationResult(WebPlugin.getInstance().getMessage("authentication.register.emailNotValid"));
return;
}
// check for uniqueness
if (Boolean.parseBoolean(CommonPlugin.getInstance().getFlowerProperties().getProperty(EMAIL_SHOULD_BE_UNIQUE))) {
Query query = wrapper.createQuery("SELECT u " +
"FROM User u " +
"WHERE u.email = :email");
query.setParameter("email", dto.getEmail());
if (query.list().size() > 0) {
wrapper.setOperationResult(WebPlugin.getInstance().getMessage("authentication.register.emailNotUnique"));
return;
}
}
}
}
user.setEmail(dto.getEmail());
// allow updating the user without providing the password; if a password is provided, update it
if (dto.getPassword() != null) {
// check for min length
int minLength = Integer.parseInt(CommonPlugin.getInstance().getFlowerProperties().getProperty(MIN_PASSWORD_LENGTH));
if (dto.getPassword().length() < minLength) {
wrapper.setOperationResult(WebPlugin.getInstance().getMessage("authentication.register.passwordTooShort", minLength));
}
user.setHashedPassword(Util.encrypt(dto.getPassword()));
}
user.setActivated(dto.getIsActivated());
// user = wrapper.merge(user);
// get original organizations
List<Organization> originalOrganizations = getOrganizationsWhereUserBelongs(user, true);
// get added and removed organizations
List<Long>[] list = Util.getAddedRemovedElements(originalOrganizations, dto.getOrganizations(true));
// for each removed organization, remove it from the database too
for (Long id : list[1]) {
for (Iterator<OrganizationUser> it = user.getOrganizationUsers().iterator(); it.hasNext();) {
OrganizationUser organizationUser = it.next();
try {
// check if the current user has permissions, unless the current user is leaving the organization
if (CommunicationPlugin.tlCurrentPrincipal.get() != null &&
CommunicationPlugin.tlCurrentPrincipal.get().getUserId() != user.getId()) {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(Collections.singletonList(organizationUser.getOrganization()), null, PermissionEntity.ORGANIZATION_PREFIX));
}
if (organizationUser.getOrganization().getId() == id) {
it.remove();
// wrapper.merge(organizationUser.getUser());
removeOrganizationUserDependency(organizationUser);
break;
}
} catch (Exception e) {
// do nothing
}
}
}
// for each added organization, add it in the database too
for (Long id : list[0]) {
Organization organization = wrapper.find(Organization.class, id);
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(organization, null));
addOrganizationUserDependency(user, organization, OrganizationMembershipStatus.MEMBER, wrapper);
} catch (Exception e) {
// do nothing
}
}
// get added and removed groups
list = Util.getAddedRemovedElements(originalGroups, dto.getGroups());
// for each removed group, remove it from database too
// important: only if current user has permission
for (Long id : list[1]) {
for (Iterator<GroupUser> it = user.getGroupUsers().iterator(); it.hasNext();) {
GroupUser groupUser = it.next();
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(Collections.singletonList(groupUser.getGroup()), null, PermissionEntity.GROUP_PREFIX));
if (groupUser.getGroup().getId() == id) {
it.remove();
// wrapper.merge(groupUser.getUser());
removeGroupUserDependency(groupUser);
break;
}
} catch (Exception e) {
// do nothing
}
}
}
// for each added group, add it in database too
for (Long id : list[0]) {
Group group = wrapper.find(Group.class, id);
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(Collections.singletonList(group), null, PermissionEntity.GROUP_PREFIX));
// only if the user is a member of the organization of the group
if (group.getOrganization() == null || OrganizationService.getInstance().getOrganizationMembershipStatus(group.getOrganization(), user) != null) {
addGroupUserDependency(user, group, wrapper);
} else {
wrapper.setOperationResult("The user cannot be added to a group of an organization that he doesn't belong to!");
return;
}
} catch (Exception e) {
// do nothing
}
}
// // save changes
// wrapper.merge(user);
}
});
User newUser = (User) new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
wrapper.setOperationResult(wrapper.find(User.class, dto.getId()));
}
}).getOperationResult();
observable.notifyObservers(Arrays.asList(UPDATE, initialUser, newUser));
return (String) wrapper.getOperationResult();
}
/**
* Deletes all {@link User}s based on the list of their ids. This operations is allowed iff the user has global permissions.
*
*
*/
public String delete(final ServiceInvocationContext context, final List<Integer> ids) {
final User[] deletedUser = new User[1];
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
for (Integer id : ids) {
if (((CommunicationChannel) context.getCommunicationChannel()).getPrincipal().getUserId() != id)
SecurityUtils.checkAdminSecurityEntitiesPermission(PermissionEntity.ANY_ENTITY);
User user = wrapper.find(User.class, Long.valueOf(id));
if (user.getLogin().startsWith(ANONYMOUS) &&
!((User) context.getCommunicationChannel().getPrincipal().getUser()).isAdmin()) {
wrapper.setOperationResult("You cannot delete an anonymous user!");
return;
}
logger.debug("Delete {}", user);
// user is removed so remove entities from GroupUser
// for (Iterator<GroupUser> it = user.getGroupUsers().iterator(); it.hasNext();) {
// GroupUser groupUser = it.next();
// it.remove();
// user = wrapper.merge(user);
// removeGroupUserDependency(groupUser);
// }
// user is removed so remove entities from OrganizationUser
// for (Iterator<OrganizationUser> it = user.getOrganizationUsers().iterator(); it.hasNext();) {
// OrganizationUser organizationUser = it.next();
// it.remove();
// user = wrapper.merge(user);
// removeOrganizationUserDependency(organizationUser);
// }
// user is removed so update entities from RecentResource
Query q = wrapper.createQuery("UPDATE RecentResource r SET lastAccessUser = NULL WHERE r.lastAccessUser = :user");
q.setParameter("user", user);
q.executeUpdate();
q = wrapper.createQuery("UPDATE RecentResource r SET lastSaveUser = NULL WHERE r.lastSaveUser = :user");
q.setParameter("user", user);
q.executeUpdate();
wrapper.delete(user);
deletedUser[0] = user;
}
}
});
observable.notifyObservers(Arrays.asList(DELETE, deletedUser[0]));
return (String) wrapper.getOperationResult();
}
/**
* This method does not remove the groupUser from the groupUsers
* of the user (it does not have access to the iterator from which the method
* is called).
*
* Removes the corresponding {@link GroupUser} from database.
* Removes the association between groupUser and group.
*
* Note cache: Because of the cache mechanism, this isn't done automatically.
*
*/
private void removeGroupUserDependency(final GroupUser groupUser) {
logger.debug("Remove {} from {}", groupUser.getUser(), groupUser.getGroup());
groupUser.getGroup().getGroupUsers().remove(groupUser);
}
private void removeOrganizationUserDependency(final OrganizationUser organizationUser) {
logger.debug("Remove {} from {}", organizationUser.getUser(), organizationUser.getOrganization());
organizationUser.getOrganization().getOrganizationUsers().remove(organizationUser);
}
/**
* TODO : temporary method
* This method makes also modifications on target entities (User and Group).
*
* Note cache: Because of the cache mechanism, this isn't done automatically.
*
*/
private void addGroupUserDependency(final User user, final Group group, DatabaseOperationWrapper wrapper) {
logger.debug("Add {} to {}", user, group);
GroupUser gr = EntityFactory.eINSTANCE.createGroupUser();
gr.setGroup(group);
gr.setUser(user);
}
private OrganizationUser addOrganizationUserDependency(final User user, final Organization organization, final OrganizationMembershipStatus status, DatabaseOperationWrapper wrapper) {
if (logger.isDebugEnabled()) {
String[] params = {user.toString(), organization.toString(), status.toString()};
logger.debug("Add {} to {} with status {}", params);
}
OrganizationUser ou = EntityFactory.eINSTANCE.createOrganizationUser();
ou = wrapper.merge(ou);
ou.setOrganization(organization);
ou.setUser(user);
ou.setStatus(status);
return ou;
}
@SuppressWarnings("unchecked")
public List<Group> getUserGroups(final User user) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
Query query = wrapper.createQuery("SELECT g " +
"FROM User u JOIN u.groupUsers gu JOIN gu.group g " +
"WHERE u.id = :user_id");
query.setParameter("user_id", user.getId());
wrapper.setOperationResult(query.list());
}
});
return (List<Group>) wrapper.getOperationResult();
}
@SuppressWarnings("unchecked")
private List<Organization> getOrganizationsWhereUserBelongs(final User user, final boolean withPendingStatus) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
Query query;
query = wrapper.createQuery("SELECT o " +
"FROM User u JOIN u.organizationUsers ou JOIN ou.organization o " +
"WHERE u.id = :user_id" +
(withPendingStatus ? "" : " AND ou.status != :pending_status"));
query.setParameter("user_id", user.getId());
if (!withPendingStatus) {
query.setParameter("pending_status", OrganizationMembershipStatus.PENDING_MEMBERSHIP_APPROVAL);
}
wrapper.setOperationResult(query.list());
}
});
return (List<Organization>) wrapper.getOperationResult();
}
@SuppressWarnings("unchecked")
public List<Organization> getOrganizations(final User user) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
if (user.isAdmin()) {
wrapper.setOperationResult(wrapper.findAll(Organization.class));
} else {
wrapper.setOperationResult(getOrganizationsWhereUserBelongs(user, true));
}
}
});
return (List<Organization>) wrapper.getOperationResult();
}
public Organization getOrganization(final User user, final long orgId) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
Query query = wrapper.createQuery("SELECT o " +
"FROM User u JOIN u.organizationUsers ou JOIN ou.organization o " +
"WHERE u.id = :user_id AND o.id = :org_id");
query.setParameter("user_id", user.getId());
query.setParameter("org_id", orgId);
@SuppressWarnings("unchecked")
List<Organization> list = query.list();
if (!list.isEmpty()) {
wrapper.setOperationResult(list.get(0));
}
}
});
return (Organization) wrapper.getOperationResult();
}
public List<String> getAllUserRepositories(User user) {
List<Organization> list = getOrganizations(user);
List<String> repositories = new ArrayList<String>();
for (Organization org : list) {
for (NamedEntity dto : org.getSvnRepositoryURLs()) {
if (!repositories.contains(dto.getName())) {
repositories.add(dto.getName());
}
}
}
return repositories;
}
@SuppressWarnings("unchecked")
public List<SVNCommentEntity> getSVNCommentsOrderedByTimestamp(final User user, final boolean asc) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
Query query = wrapper.createQuery("SELECT s " +
"FROM SVNCommentEntity s JOIN s.user u WHERE u.id = :user_id ORDER BY s.timestamp " + (asc ? "ASC" : "DESC"));
query.setParameter("user_id", user.getId());
wrapper.setOperationResult(query.list());
}
});
return (List<SVNCommentEntity>) wrapper.getOperationResult();
}
public List<OrganizationAdminUIDto> getUserOrganizations(ServiceInvocationContext context, boolean addUnassignedNode) {
CommunicationChannel channel = (CommunicationChannel) context.getCommunicationChannel();
User user = (User) channel.getPrincipal().getUser();
List<OrganizationAdminUIDto> list = new ArrayList<OrganizationAdminUIDto>();
for (Organization org : UserService.getInstance().getOrganizations(user)) {
list.add(OrganizationService.getInstance().convertOrganizationToOrganizationAdminUIDto(org, user));
}
if (addUnassignedNode && user.isAdmin()) {
OrganizationAdminUIDto unknownOrg = new OrganizationAdminUIDto();
unknownOrg.setId(-1);
unknownOrg.setName("Unassigned");
list.add(unknownOrg);
}
return list;
}
/**
* Checks if the <code>activationCode</code> provided is correct and activates the user.
*/
public boolean activateUser(final String login, final String activationCode) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
User user = wrapper.findByField(User.class, "login", login).get(0);
if (user.isActivated()) {
wrapper.setOperationResult(true);
return;
}
if (user.getActivationCode().equals(activationCode)) {
// activation successful
user.setActivated(true);
wrapper.merge(user);
logger.debug("Activation successful for {} with activation code = {}", user, activationCode);
wrapper.setOperationResult(true);
return;
}
logger.debug("Activation failed for {}. Reason: wrong activation code.", user);
wrapper.setOperationResult(false);
}
});
return (boolean) wrapper.getOperationResult();
}
private final String USER_ACTIVATED_SUBJECT = "mail.template.user-activated.subject";
private final String USER_ACTIVATED_BODY = "mail.template.user-activated.body";
private final String USER_ACTIVATED_WELCOME_MESSAGE_WITH_ORGANIZATION = "entity.organization.help.afterActivation.contentWithOrganizationFilter";
private final String USER_ACTIVATED_WELCOME_MESSAGE_WITHOUT_ORGANIZATION = "entity.organization.help.afterActivation.contentWithoutOrganizationFilter";
private final String ORGANIZATION_INFORMATION = "entity.organization.help.whatis.content";
/**
* Called from client side after the user activates his account.
*
* @param organizationName the organization filter; a membership request was automatically made for the user on activation
*/
public void sendActivationEmail(final long userId, final String organizationName) {
new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
User user = wrapper.find(User.class, userId);
String subject = WebPlugin.getInstance().getMessage(USER_ACTIVATED_SUBJECT);
String url, welcomeMessage;
if (organizationName == null) {
url = SendMailService.getInstance().getServerUrl();
welcomeMessage = WebPlugin.getInstance().getMessage(USER_ACTIVATED_WELCOME_MESSAGE_WITHOUT_ORGANIZATION);
} else {
url = SendMailService.getInstance().getServerUrlForOrganization(organizationName);
Organization organization = wrapper.findByField(Organization.class, "name", organizationName).get(0);
welcomeMessage = WebPlugin.getInstance().getMessage(USER_ACTIVATED_WELCOME_MESSAGE_WITH_ORGANIZATION, organization.getLabel());
}
String text = WebPlugin.getInstance().getMessage(USER_ACTIVATED_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
url,
welcomeMessage.replace("\n", "<br/>"),
WebPlugin.getInstance().getMessage(ORGANIZATION_INFORMATION)
});
SendMailService.getInstance().send(user.getEmail(), subject, text);
}
});
}
public final static String ORGANIZATION_DIRECTORIES = "users.directories-for-organization";
public final static String ORGANIZATION_DEFAULT_DIRECTORY = "users.default-directory-for-organization";
private final static String ORGANIZATION_APPROVED_CREATE_DIRS = "users.on-approve-organization.create-dirs";
private final static String ORGANIZATION_APPROVED_CREATE_GROUPS = "users.on-approve-organization.create-groups";
private final static String ORGANIZATION_APPROVED_CREATE_USERS = "users.on-approve-organization.create-users";
private final static String ORGANIZATION_APPROVED_CREATE_PERMISSIONS = "users.on-approve-organization.create-permissions";
private final static String ON_BECOME_ADMIN_ADD_USER_TO_GROUPS = "users.on-become-org-admin.add-user-to-groups";
private final static String ON_APPROVE_MEMBERSHIP_ADD_USER_TO_GROUPS = "users.on-approve-membership-to-organization.add-member-to-groups";
private final static String EMAIL_SHOULD_BE_UNIQUE = "users.email-unique";
private final static String MIN_PASSWORD_LENGTH = "users.min-password-length";
private static AddProperty getDefaultAddProperty(String propertyName, String propertyDefaultValue) {
return new AddProperty(propertyName, propertyDefaultValue) {
@Override
protected String validateProperty(String input) {
// do nothing
return null;
}
};
}
private static AddProperty getPropertyWithGroupValidation(String propertyName, String propertyDefaultValue) {
return new AddProperty(propertyName, propertyDefaultValue) {
@Override
protected String validateProperty(String input) {
String createdGroups = CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_GROUPS);
for (String group : input.split(",")) {
if (!createdGroups.contains(group)) {
return "Group " + group + " is not created on organization approve: " + createdGroups;
}
}
return null;
}
};
}
static {
CommonPlugin.getInstance().getFlowerProperties().addProperty(getDefaultAddProperty(ORGANIZATION_DIRECTORIES, "{0}"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(getDefaultAddProperty(ORGANIZATION_DEFAULT_DIRECTORY, "ws_trunk"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(getDefaultAddProperty(ORGANIZATION_APPROVED_CREATE_DIRS, "{0}, {0}/ws_trunk"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(getDefaultAddProperty(ORGANIZATION_APPROVED_CREATE_GROUPS, "{0}.admin, {0}.user"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(getDefaultAddProperty(ORGANIZATION_APPROVED_CREATE_USERS, "anonymous.{0}"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(new AddProperty(ORGANIZATION_APPROVED_CREATE_PERMISSIONS,
"@{0}.admin: AdminSecurityEntitiesPermission(null, #{0})," +
"@{0}.admin: ModifyTreePermissionsPermission({0}, #{0})," +
"@{0}.admin: ModifyTreePermissionsPermission({0}/*, #{0})," +
"@{0}.admin: FlowerWebFilePermission({0}, read-write-delete)," +
"@{0}.admin: FlowerWebFilePermission({0}/*, read-write-delete)," +
"#{0}: FlowerWebFilePermission({0}, read)," +
"#{0}: FlowerWebFilePermission({0}/*, read)") {
@Override
protected String validateProperty(String input) {
String createdGroups = CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_GROUPS);
Pattern pattern = Pattern.compile("s*?@(.*?):s*?");
Matcher matcher = pattern.matcher(input);
while (matcher.find()) {
String group = matcher.group(1);
if (!createdGroups.contains(group)) {
return "Group " + group + " is not created on organization approve: " + createdGroups;
}
}
return null;
}
}) ;
CommonPlugin.getInstance().getFlowerProperties().addProperty(getPropertyWithGroupValidation(ON_BECOME_ADMIN_ADD_USER_TO_GROUPS, "{0}.admin"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(getPropertyWithGroupValidation(ON_APPROVE_MEMBERSHIP_ADD_USER_TO_GROUPS, "{0}.user"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(new AddBooleanProperty(EMAIL_SHOULD_BE_UNIQUE, "true"));
CommonPlugin.getInstance().getFlowerProperties().addProperty(new AddIntegerProperty(MIN_PASSWORD_LENGTH, "4") {
@Override
protected String validateProperty(String input) {
String result = super.validateProperty(input);
if (result != null) {
return result;
}
if (Integer.valueOf(input) == 0) {
return "Value cannot be 0";
}
return null;
}
});
}
private final String COMMENT_TEMPLATE_WITH_ADMIN_COMMENT = "mail.template.withAdminComment";
private final String COMMENT_TEMPLATE_WITH_USER_COMMENT = "mail.template.withUserComment";
private final String COMMENT_TEMPLATE_WITH_YOUR_COMMENT = "mail.template.withYourComment";
private String getCommentForTemplate(String id, String comment) {
if (comment == null)
return "";
return WebPlugin.getInstance().getMessage(id, comment.replace("\r", "<br/>"));
}
private final String ORGANIZATION_CREATED_TO_USER_SUBJECT = "mail.template.organization-created.to-user.subject";
private final String ORGANIZATION_CREATED_TO_USER_BODY = "mail.template.organization-created.to-user.body";
private final String ORGANIZATION_CREATED_TO_FDC_ADMIN_SUBJECT = "mail.template.organization-created.to-fdc-admin.subject";
private final String ORGANIZATION_CREATED_TO_FDC_ADMIN_BODY = "mail.template.organization-created.to-fdc-admin.body";
/**
* Creates the {@link Organization}. The {@link User} who requested this organization will be added to
* the organization with status <code>PENDING_NEW_ORGANIZATION_APPROVAL</code>. Mails are sent to the organization
* creator and FDC admins to notify about the creation of a new organization.
*
* @param dto new organization
* @param commentForAdmin will be sent to the FDC administrators
* @return error message if the organization could not be created
*/
public String requestNewOrganization(final ServiceInvocationContext context, final OrganizationAdminUIDto dto, final String commentForAdmin) {
logger.debug("New organization request from {} with comment = {}",
((User) context.getCommunicationChannel().getPrincipal().getUser()), commentForAdmin);
if (((User) context.getCommunicationChannel().getPrincipal().getUser()).getLogin().startsWith(ANONYMOUS)) {
return "Anonymous user cannot request a new organization!";
}
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
String errorMessage = OrganizationService.getInstance().mergeAdminUIDto(context, dto, true);
if (errorMessage != null) {
wrapper.setOperationResult(errorMessage);
return;
}
long userId = CommunicationPlugin.tlCurrentPrincipal.get().getUserId();
User currentUser = wrapper.find(User.class, userId);
if (currentUser.isAdmin()) {
// activate org automatically if this user is FDC admin
wrapper.setOperationResult(approveDenyNewOrganization(context, dto, true, null));
return;
} else {
// add the user to the organization
Organization organization = wrapper.findByField(Organization.class, "name", dto.getName()).get(0);
User organizationOwner = currentUser;
UserService.getInstance().addOrganizationUserDependency(organizationOwner, organization, OrganizationMembershipStatus.ADMIN, wrapper);
// send email to user
String subject = WebPlugin.getInstance().getMessage(ORGANIZATION_CREATED_TO_USER_SUBJECT, dto.getLabel());
String content = WebPlugin.getInstance().getMessage(ORGANIZATION_CREATED_TO_USER_BODY,
new Object[] {
organizationOwner.getLogin(),
organizationOwner.getName(),
organizationOwner.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
dto.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_YOUR_COMMENT, commentForAdmin)
});
SendMailService.getInstance().send(organizationOwner.getEmail(), subject, content);
// send email to FDC admins
subject = WebPlugin.getInstance().getMessage(ORGANIZATION_CREATED_TO_FDC_ADMIN_SUBJECT, dto.getLabel());
for (User user : wrapper.findAll(User.class)) {
if (user.isAdmin()) {
content = WebPlugin.getInstance().getMessage(ORGANIZATION_CREATED_TO_FDC_ADMIN_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organizationOwner.getName(),
dto.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_USER_COMMENT, commentForAdmin)
});
SendMailService.getInstance().send(user.getEmail(), subject, content);
}
}
}
}
});
return (String) wrapper.getOperationResult();
}
private final String ORGANIZATION_APPROVED_SUBJECT = "mail.template.organization-approved.subject";
private final String ORGANIZATION_APPROVED_BODY = "mail.template.organization-approved.body";
private final String ORGANIZATION_DENIED_SUBJECT = "mail.template.organization-denied.subject";
private final String ORGANIZATION_DENIED_BODY = "mail.template.organization-denied.body";
private final String ORGANIZATION_APPROVED_OPERATIONS_EXECUTED = "mail.template.organization-approved.operations-executed";
/**
* Only available to FDC admin.
*/
public String approveDenyNewOrganization(final ServiceInvocationContext context, final OrganizationAdminUIDto dto, final boolean approve, final String commentFromAdmin) {
SecurityUtils.checkAdminSecurityEntitiesPermission(PermissionEntity.ANY_ENTITY);
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
Organization organization = wrapper.findByField(Organization.class, "name", dto.getName()).get(0);
List<OrganizationUser> list = organization.getOrganizationUsers();
User organizationOwner = null;
if (list.size() > 0) {
OrganizationUser organizationUser = (OrganizationUser) list.toArray()[0];
organizationOwner = organizationUser.getUser();
}
if (approve) {
// approve request
logger.debug("Approve {}", organization);
organization.setActivated(true);
organization = wrapper.merge(organization);
// create directories
String createDirsProp = MessageFormat.format(
CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_DIRS),
organization.getName());
logger.debug("Create directories = {}", createDirsProp);
String[] dirsList = createDirsProp.split(",\\s*");
for (String dirName : dirsList) {
File dir = new File(CommonPlugin.getInstance().getWorkspaceRoot(), dirName);
if (!dir.exists()) {
dir.mkdirs();
}
}
// create groups
String createGroupsProp = MessageFormat.format(
CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_GROUPS),
organization.getName());
logger.debug("Create new groups = {}", createGroupsProp);
String[] groupNames = createGroupsProp.split(",\\s*");
for (String groupName : groupNames) {
GroupAdminUIDto groupDto = new GroupAdminUIDto();
groupDto.setName(groupName);
groupDto.setOrganization(new NamedDto(dto.getId(), dto.getName()));
groupDto.setOrganizationLabel(dto.getLabel());
GroupService.getInstance().mergeAdminUIDto(context, groupDto);
}
// create users
String createUsersProp = MessageFormat.format(CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_USERS), organization.getName());
logger.debug("Create new users = {}", createUsersProp);
String[] userNames = createUsersProp.split(",\\s*");
for (String username : userNames) {
UserAdminUIDto userDto = new UserAdminUIDto();
userDto.setName(username);
userDto.setLogin(username);
userDto.setPassword(username);
userDto.setIsActivated(true);
try {
mergeAdminUIDto(userDto);
User newUser = wrapper.findByField(User.class, "login", username).get(0);
addOrganizationUserDependency(newUser, organization, OrganizationMembershipStatus.MEMBER, wrapper);
} catch (Exception e) {
// do nothing
}
}
// add admin to admin groups
String adminGroups = MessageFormat.format(
CommonPlugin.getInstance().getFlowerProperties().getProperty(ON_BECOME_ADMIN_ADD_USER_TO_GROUPS),
organization.getName());
if (organizationOwner != null) {
logger.debug("Add {} to admin groups = {}", organizationOwner, adminGroups);
addRemoveUserFromGroups(organizationOwner, adminGroups, true, wrapper);
}
// create permissions
String createPermissionsProp = MessageFormat.format(
CommonPlugin.getInstance().getFlowerProperties().getProperty(ORGANIZATION_APPROVED_CREATE_PERMISSIONS),
organization.getName());
logger.debug("Create permissions = {}", createPermissionsProp);
Pattern pattern = Pattern.compile("\\s*?(.*?)\\s*:\\s*(\\w*)\\s*\\((.*?)\\),?\\s*?");
Matcher matcher = pattern.matcher(createPermissionsProp);
String permissionsCreated = "";
while (matcher.find()) {
String assignedTo = matcher.group(1);
String permissionType = "org.flowerplatform.web.security.permission." + matcher.group(2);
String paramList = matcher.group(3);
String[] params = paramList.split("\\s*,\\s*");
PermissionAdminUIDto permissionDto = new PermissionAdminUIDto();
permissionDto.setAssignedTo(assignedTo);
permissionDto.setType(permissionType);
permissionDto.setName(params[0].equals("null") ? "" : params[0]);
permissionDto.setActions(params[1]);
try {
PermissionService.getInstance().mergeAdminUIDto(context, permissionDto);
permissionsCreated += "<br/> " + permissionDto; // HTML format, because this info will be sent via mail
} catch (Exception e) {
logger.error(e.getMessage());
// do nothing
}
}
// compose message to send to client
String message = WebPlugin.getInstance().getMessage(ORGANIZATION_APPROVED_OPERATIONS_EXECUTED,
new Object[] {
createDirsProp,
createGroupsProp,
createUsersProp,
organizationOwner != null ? organizationOwner.getName() : "null",
adminGroups,
permissionsCreated
});
if (organizationOwner != null) {
// send mail
String subject = WebPlugin.getInstance().getMessage(ORGANIZATION_APPROVED_SUBJECT, organization.getLabel());
String content = WebPlugin.getInstance().getMessage(ORGANIZATION_APPROVED_BODY,
new Object[] {
organizationOwner.getLogin(),
organizationOwner.getName(),
organizationOwner.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organization.getLabel(),
message,
getCommentForTemplate(COMMENT_TEMPLATE_WITH_ADMIN_COMMENT, commentFromAdmin)
});
SendMailService.getInstance().send(organizationOwner.getEmail(), subject, content);
}
wrapper.setOperationResult(message);
return;
} else {
// deny request => delete organization
logger.debug("Deny organization = {}", dto.getName());
OrganizationService.getInstance().delete(Arrays.asList((int) dto.getId()));
// send mail to user
if (organizationOwner != null) {
String subject = WebPlugin.getInstance().getMessage(ORGANIZATION_DENIED_SUBJECT, organization.getLabel());
String content = WebPlugin.getInstance().getMessage(ORGANIZATION_DENIED_BODY,
new Object[] {
organizationOwner.getLogin(),
organizationOwner.getName(),
organizationOwner.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organization.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_ADMIN_COMMENT, commentFromAdmin)
});
SendMailService.getInstance().send(organizationOwner.getEmail(), subject, content);
}
}
}
});
return (String) wrapper.getOperationResult();
}
private final String MEMBERSHIP_REQUESTED_TO_USER_SUBJECT = "mail.template.membership-requested.to-user.subject";
private final String MEMBERSHIP_REQUESTED_TO_USER_BODY = "mail.template.membership-requested.to-user.body";
private final String MEMBERSHIP_REQUESTED_TO_ORG_ADMIN_SUBJECT = "mail.template.membership-requested.to-org-admin.subject";
private final String MEMBERSHIP_REQUESTED_TO_ORG_ADMIN_BODY = "mail.template.membership-requested.to-org-admin.body";
/**
* Called from client side. Adds the user to the organization's members list, with
* the {@link OrganizationMembershipStatus#PENDING_MEMBERSHIP_APPROVAL} status.
*/
public String requestMembership(final ServiceInvocationContext context, final long userId, final String organizationName, final String commmentForAdmin) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
User user = wrapper.find(User.class, userId);
if (user.getLogin().startsWith(ANONYMOUS) && !((FlowerWebPrincipal) CommunicationPlugin.tlCurrentPrincipal.get()).getUser().isAdmin()) {
wrapper.setOperationResult("Anonymous user cannot join organizations!");
return;
}
List<Organization> list = wrapper.findByField(Organization.class, "name", organizationName);
if (list.size() != 1) {
wrapper.setOperationResult("Organization does not exist");
return;
}
Organization organization = list.get(0);
logger.debug("{} requesting membership to {}", user, organization);
// cannot request membership for an organization that was not activated yet
if (organization.isActivated()) {
// first check if the user is already a member/has requested to be a member before
if (OrganizationService.getInstance().getOrganizationMembershipStatus(organization, user) == null) {
OrganizationUser ou = addOrganizationUserDependency(user, organization, OrganizationMembershipStatus.PENDING_MEMBERSHIP_APPROVAL, wrapper);
// NOTE: if this is in fact the organization owner adding a new user to his organization, the following mails won't be sent at all
if (((CommunicationChannel) context.getCommunicationChannel()).getPrincipal().getUserId() == user.getId()) {
// send mail to user
String subject = WebPlugin.getInstance().getMessage(MEMBERSHIP_REQUESTED_TO_USER_SUBJECT, organization.getLabel());
String content = WebPlugin.getInstance().getMessage(MEMBERSHIP_REQUESTED_TO_USER_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organization.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_YOUR_COMMENT, commmentForAdmin)
});
SendMailService.getInstance().send(user.getEmail(), subject, content);
// send mail to organization admins
subject = WebPlugin.getInstance().getMessage(MEMBERSHIP_REQUESTED_TO_ORG_ADMIN_SUBJECT, organization.getLabel());
for (OrganizationUser organizationUser : organization.getOrganizationUsers()) {
if (organizationUser.getStatus().equals(OrganizationMembershipStatus.ADMIN)) {
User orgOwner = organizationUser.getUser();
content = WebPlugin.getInstance().getMessage(MEMBERSHIP_REQUESTED_TO_ORG_ADMIN_BODY,
new Object[] {
orgOwner.getLogin(),
orgOwner.getName(),
orgOwner.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organization.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_USER_COMMENT , commmentForAdmin),
user.getLogin(),
user.getName(),
user.getEmail()
});
SendMailService.getInstance().send(orgOwner.getEmail(), subject, content);
}
}
}
// approve membership if current user is FDC admin or org admin
String message = null;
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(organization, null));
message = approveDenyMembership(context, ou.getId(), true, commmentForAdmin);
} catch (Exception e) {
// do nothing
}
wrapper.setOperationResult(message);
return;
} else {
logger.debug("{} is already a member of {}", user, organization);
wrapper.setOperationResult("User is already a member of " + organization.getName() + ".");
return;
}
} else {
logger.debug("Cannot request membership to an organization that is not yet activated!");
wrapper.setOperationResult("Cannot request membership to an organization that is not yet activated!");
}
}
});
return (String) wrapper.getOperationResult();
}
private final String MEMBERSHIP_ACCEPTED_SUBJECT = "mail.template.membership.accepted.subject";
private final String MEMBERSHIP_ACCEPTED_BODY = "mail.template.membership.accepted.body";
private final String MEMBERSHIP_DENIED_SUBJECT = "mail.template.membership.denied.subject";
private final String MEMBERSHIP_DENIED_BODY = "mail.template.membership.denied.body";
/**
* Called from client side. The user's pending membership request is approved or denied. Returns a message to inform
* the client about the groups that the user was added to upon approving his membership request, or <code>null</code>
* if the user's request was denied.
*/
public String approveDenyMembership(ServiceInvocationContext context, final long organizationUserId, final boolean approve, final String commentForUser) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
OrganizationUser organizationUser = wrapper.find(OrganizationUser.class, organizationUserId);
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(organizationUser.getOrganization(), null));
if (approve) {
// accept membership request, add user to organization
logger.debug("Approve membership request from {} to {}", organizationUser.getUser(), organizationUser.getOrganization());
organizationUser.setStatus(OrganizationMembershipStatus.MEMBER);
organizationUser = wrapper.merge(organizationUser);
User user = organizationUser.getUser();
for (OrganizationUser ou : user.getOrganizationUsers()) {
if (ou.getId() == organizationUser.getId()) {
ou.setStatus(OrganizationMembershipStatus.MEMBER);
user = wrapper.merge(user);
wrapper.merge(organizationUser.getOrganization());
break;
}
}
// send mail to user if comment was added
if (commentForUser != null) {
String subject = WebPlugin.getInstance().getMessage(MEMBERSHIP_ACCEPTED_SUBJECT, organizationUser.getOrganization().getLabel());
String content = WebPlugin.getInstance().getMessage(MEMBERSHIP_ACCEPTED_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organizationUser.getOrganization().getName()),
organizationUser.getOrganization().getLabel(),
commentForUser == null ? "" : getCommentForTemplate(COMMENT_TEMPLATE_WITH_ADMIN_COMMENT, commentForUser)
// don't add the comment if no comment was sent
});
SendMailService.getInstance().send(user.getEmail(), subject, content);
}
// add user to groups
String prop = CommonPlugin.getInstance().getFlowerProperties().getProperty(ON_APPROVE_MEMBERSHIP_ADD_USER_TO_GROUPS);
String groupsString = MessageFormat.format(prop, organizationUser.getOrganization().getName());
String result = addRemoveUserFromGroups(user, groupsString, true, wrapper);
result += WebPlugin.getInstance().getMessage("entity.group.manuallyAddUserToGroups");
wrapper.setOperationResult(result);
return;
} else {
// deny membership request, unassociate user from organization
logger.debug("Deny membership request from {} to {}", organizationUser.getUser(), organizationUser.getOrganization());
Organization organization = organizationUser.getOrganization();
User user = organizationUser.getUser();
for (Iterator<OrganizationUser> it = user.getOrganizationUsers().iterator(); it.hasNext();) {
OrganizationUser ou = it.next();
if (ou.getId() == organizationUser.getId()) {
it.remove();
organizationUser.getOrganization().getOrganizationUsers().remove(ou);
break;
}
}
wrapper.delete(organizationUser);
// send mail to user
String subject = WebPlugin.getInstance().getMessage(MEMBERSHIP_DENIED_SUBJECT, organization.getLabel());
String content = WebPlugin.getInstance().getMessage(MEMBERSHIP_DENIED_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(organization.getName()),
organization.getLabel(),
getCommentForTemplate(COMMENT_TEMPLATE_WITH_ADMIN_COMMENT, commentForUser)
});
SendMailService.getInstance().send(user.getEmail(), subject, content);
}
}
});
return (String) wrapper.getOperationResult();
}
public String createAndApproveAsOrganizationMember(final ServiceInvocationContext context, final UserAdminUIDto userDto) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
String errorMessage = mergeAdminUIDto(userDto);
if (errorMessage != null) {
wrapper.setOperationResult(errorMessage);
return;
}
User user = wrapper.findByField(User.class, "login", userDto.getLogin()).get(0);
OrganizationUser ou = null;
if (user.getOrganizationUsers().size() > 0) {
ou = (OrganizationUser) user.getOrganizationUsers().toArray()[0];
}
if (!user.isActivated()) {
sendActivationCodeForUser(user, ou == null ? null : ou.getOrganization().getName());
}
if (ou != null) {
approveDenyMembership(context, ou.getId(), true, null);
}
}
});
return (String) wrapper.getOperationResult();
}
private final String LEAVE_ORGANIZATION_TO_USER_SUBJECT = "mail.template.leave-organization.to-user.subject";
private final String LEAVE_ORGANIZATION_TO_USER_BODY = "mail.template.leave-organization.to-user.body";
private final String LEAVE_ORGANIZATION_TO_ADMIN_SUBJECT = "mail.template.leave-organization.to-admin.subject";
private final String LEAVE_ORGANIZATION_TO_ADMIN_BODY = "mail.template.leave-organization.to-admin.body";
/**
* Removes the user from the given organizations and from the organization's groups. Sends mail to
* user and organization's admins to notify that the user left the organization.
*
* @param userDto user that requested to leave
* @param organizationDtos the organizations that the user is leaving
*/
public String leaveOrganizations(final ServiceInvocationContext context, final UserAdminUIDto userDto, final List<OrganizationAdminUIDto> organizationDtos, final String commentForAdmin) {
if (userDto.getLogin().startsWith(ANONYMOUS) && !((FlowerWebPrincipal) CommunicationPlugin.tlCurrentPrincipal.get()).getUser().isAdmin()) {
return "Anonymous user cannot be removed from organizations!";
}
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
String removedFromGroupsMessage = new String();
for (Iterator<OrganizationUserAdminUIDto> organizationIt = userDto.getOrganizationUsers().iterator(); organizationIt.hasNext();) {
OrganizationUserAdminUIDto ou = organizationIt.next();
for (OrganizationAdminUIDto removedOrg : organizationDtos) {
if (ou.getOrganization().getId() == removedOrg.getId()) {
// remove from organization
logger.debug("Remove user = {} from organization = {}", userDto.getLogin(), removedOrg.getName());
organizationIt.remove();
// remove from groups belonging to organization
for (@SuppressWarnings("unchecked")
Iterator<GroupAdminUIDto> groupIt = (Iterator<GroupAdminUIDto>) userDto.getGroups().iterator(); groupIt.hasNext();) {
GroupAdminUIDto group = groupIt.next();
if (group.getOrganization() != null && group.getOrganization().getId() == ou.getOrganization().getId()) {
groupIt.remove();
removedFromGroupsMessage += "<li>" + group.getName() + "</li>";
}
}
boolean wasRemovedByAdmin = ((CommunicationChannel) context.getCommunicationChannel()).getPrincipal().getUserId() != userDto.getId();
if (!wasRemovedByAdmin) {
removedFromGroupsMessage = WebPlugin.getInstance().getMessage("entity.group.youWereRemovedFromGroups", removedFromGroupsMessage);
} else {
removedFromGroupsMessage = WebPlugin.getInstance().getMessage("entity.group.userWasRemovedFromGroups", removedFromGroupsMessage);
}
// send mail to user if a comment was added
if (commentForAdmin != null) {
String subject = WebPlugin.getInstance().getMessage(LEAVE_ORGANIZATION_TO_USER_SUBJECT, ou.getOrganization().getLabel());
String text = WebPlugin.getInstance().getMessage(LEAVE_ORGANIZATION_TO_USER_BODY,
new Object[] {
userDto.getLogin(),
userDto.getName(),
userDto.getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(ou.getOrganization().getURL()),
ou.getOrganization().getLabel(),
wasRemovedByAdmin && (commentForAdmin != null) ? getCommentForTemplate(COMMENT_TEMPLATE_WITH_ADMIN_COMMENT, commentForAdmin) : ""
});
SendMailService.getInstance().send(userDto.getEmail(), subject, text);
}
// send mail to organization admins
Organization organization = wrapper.findByField(Organization.class, "name", ou.getOrganization().getName()).get(0);
for (OrganizationUser orgAdmin : organization.getOrganizationUsers()) {
if (orgAdmin.getStatus().equals(OrganizationMembershipStatus.ADMIN)) {
String subject = WebPlugin.getInstance().getMessage(LEAVE_ORGANIZATION_TO_ADMIN_SUBJECT, ou.getOrganization().getLabel());
String text = WebPlugin.getInstance().getMessage(LEAVE_ORGANIZATION_TO_ADMIN_BODY,
new Object[] {
orgAdmin.getUser().getLogin(),
orgAdmin.getUser().getName(),
orgAdmin.getUser().getEmail(),
SendMailService.getInstance().getServerUrlForOrganization(ou.getOrganization().getURL()),
ou.getOrganization().getName(),
userDto.getLogin(),
userDto.getName(),
userDto.getEmail()
});
SendMailService.getInstance().send(orgAdmin.getUser().getEmail(), subject, text);
}
}
break;
}
}
}
// add changes to DB
String message = mergeAdminUIDto(userDto);
if (message != null) {
wrapper.setOperationResult(message);
return;
}
wrapper.setOperationResult(removedFromGroupsMessage);
}
});
return (String) wrapper.getOperationResult();
}
public String upgradeDowngradeUser(final List<Integer> organizationUserIds, final boolean upgrade) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
OrganizationMembershipStatus newStatus = upgrade ? OrganizationMembershipStatus.ADMIN : OrganizationMembershipStatus.MEMBER;
String message = "";
for (int ouId : organizationUserIds) {
OrganizationUser ou = wrapper.find(OrganizationUser.class, (long) ouId);
if (!ou.getStatus().equals(newStatus)) {
try {
SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(ou.getOrganization(), null));
ou.setStatus(newStatus);
ou = wrapper.merge(ou);
logger.debug("{} is now " + newStatus + " in {}", ou.getUser(), ou.getOrganization());
// in case of upgrade, add to admin groups and remove from member groups
// in case of downgrade, add to member groups and remove from admin groups
String adminGroups = MessageFormat.format(CommonPlugin.getInstance().getFlowerProperties().getProperty(ON_BECOME_ADMIN_ADD_USER_TO_GROUPS),
ou.getOrganization().getName());
message += addRemoveUserFromGroups(ou.getUser(), adminGroups, upgrade, wrapper);
String memberGroups = MessageFormat.format(CommonPlugin.getInstance().getFlowerProperties().getProperty(ON_APPROVE_MEMBERSHIP_ADD_USER_TO_GROUPS),
ou.getOrganization().getName());
message += addRemoveUserFromGroups(ou.getUser(), memberGroups, !upgrade, wrapper);
} catch (Exception e) {
logger.debug("{} is not allowed to upgrade/downgrade members of {}",
CommunicationPlugin.tlCurrentPrincipal.get().getUser(),
ou.getOrganization());
}
}
}
wrapper.setOperationResult(message);
}
});
return (String) wrapper.getOperationResult();
}
private String addRemoveUserFromGroups(final User user, final String groupsNames, final boolean add, DatabaseOperationWrapper wrapper) {
String[] adminGroupNames = groupsNames.split(",\\s*");
String namesList = "";
String doNotExist = "";
for (String groupName : adminGroupNames) {
List<Group> list = wrapper.findByField(Group.class, "name", groupName);
if (list.size() == 0) {
doNotExist += "<li>" + groupName + "</li>";
} else {
Group group = list.get(0);
namesList += "<li>" + group.getName() + "</li>";
if (add) {
// check if user is already part of the group
boolean alreadyInGroup = false;
for (GroupUser groupUser : user.getGroupUsers()) {
if (groupUser.getGroup().getId() == group.getId()) {
alreadyInGroup = true;
}
}
if (!alreadyInGroup) {
addGroupUserDependency(user, group, wrapper);
}
} else {
for (GroupUser gu : group.getGroupUsers()) {
if (gu.getUser().getId() == user.getId()) {
removeGroupUserDependency(gu);
user.getGroupUsers().remove(gu);
wrapper.merge(user);
break;
}
}
}
}
}
String message = add ? WebPlugin.getInstance().getMessage("entity.group.userWasAddedToGroups", namesList) :
WebPlugin.getInstance().getMessage("entity.group.userWasRemovedFromGroups", namesList);
if (doNotExist.length() > 0) {
message += WebPlugin.getInstance().getMessage("entity.group.userWasNotAddedToGroups", doNotExist);
}
return message;
}
private final String SEND_ACTIVATION_CODE_SUBJECT = "mail.template.send-activation-code.subject";
private final String SEND_ACTIVATION_CODE_BODY = "mail.template.send-activation-code.body";
/**
* Sends an email containing the user's activation link and activation code. Returns
* the activation code.
*/
protected String sendActivationCodeForUser(final User user, String organizationFilter) {
DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() {
@Override
public void run() {
String activationCode = user.getActivationCode();
if (activationCode == null) {
activationCode = generateRandomString();
user.setActivationCode(activationCode);
wrapper.merge(user);
wrapper.setOperationResult(activationCode);
}
}
});
String activationCode = (String) wrapper.getOperationResult();
String subject = WebPlugin.getInstance().getMessage(SEND_ACTIVATION_CODE_SUBJECT);
String url = organizationFilter == null ? SendMailService.getInstance().getServerUrl() :
SendMailService.getInstance().getServerUrlForOrganization(organizationFilter);
String text = WebPlugin.getInstance().getMessage(SEND_ACTIVATION_CODE_BODY,
new Object[] {
user.getLogin(),
user.getName(),
user.getEmail(),
url,
url + (url.indexOf("?") < 0 ? "?" : "&") + "login=" + user.getLogin() + "&activationCode=" + activationCode,
activationCode
});
SendMailService.getInstance().send(user.getEmail(), subject, text);
return activationCode;
}
private static Random random = new Random();
protected String generateRandomString() {
byte[] bytes = new byte[20];
for (int i =0; i < bytes.length; i++) {
bytes[i] = (byte) (97 + random.nextInt(25));
}
return new String(bytes);
}
}