/* 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 java.text.MessageFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Observable; import java.util.Observer; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.flowerplatform.common.CommonPlugin; import org.flowerplatform.communication.CommunicationPlugin; import org.flowerplatform.communication.service.ServiceInvocationContext; import org.flowerplatform.communication.service.ServiceRegistry; import org.flowerplatform.web.database.DatabaseOperation; import org.flowerplatform.web.database.DatabaseOperationWrapper; import org.flowerplatform.web.entity.FavoriteItem; import org.flowerplatform.web.entity.Group; 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.RecentResource; import org.flowerplatform.web.entity.SVNRepositoryURLEntity; import org.flowerplatform.web.entity.User; import org.flowerplatform.web.entity.dto.NamedDto; import org.flowerplatform.web.security.dto.OrganizationAdminUIDto; 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>Organization</code> entity. * * @see BootstrapService#initialize() * @see ServiceRegistry * * @author Cristi * @author Cristina * @author Mariana * * */ public class OrganizationService extends ServiceObservable { public static final String SERVICE_ID = "organizationService"; private static final Logger logger = LoggerFactory.getLogger(OrganizationService.class); public static OrganizationService getInstance() { return (OrganizationService) CommunicationPlugin.getInstance().getServiceRegistry().getService(SERVICE_ID); } /** * Converts an {@link Organization} to {@link OrganizationAdminUIDto}. * * @see #findByIdAsAdminUIDto() * * */ public OrganizationAdminUIDto convertOrganizationToOrganizationAdminUIDto(Organization organization, User user) { OrganizationAdminUIDto orgAdminUIDto = new OrganizationAdminUIDto(); orgAdminUIDto.setId(organization.getId()); orgAdminUIDto.setName(organization.getName()); orgAdminUIDto.setURL(organization.getURL()); orgAdminUIDto.setLabel(organization.getLabel()); orgAdminUIDto.setLogoURL(organization.getLogoURL()); orgAdminUIDto.setIconURL(organization.getIconURL()); orgAdminUIDto.setActivated(organization.isActivated()); orgAdminUIDto.setProjectsCount(organization.getProjectsCount()); orgAdminUIDto.setFilesCount(organization.getFilesCount()); orgAdminUIDto.setModelsCount(organization.getModelsCount()); orgAdminUIDto.setDiagramsCount(organization.getDiagramsCount()); if (user != null) { orgAdminUIDto.setStatus(getOrganizationMembershipStatus(organization, user)); } List<NamedDto> list = new ArrayList<NamedDto>(); for (NamedEntity url : organization.getSvnRepositoryURLs()) { list.add(new NamedDto(url.getId(), url.getName())); } orgAdminUIDto.setSVNRepositoryURLs(list); list = new ArrayList<NamedDto>(); for (Group group : organization.getGroups()) { list.add(new NamedDto(group.getId(), group.getName())); } orgAdminUIDto.setGroups(list); return orgAdminUIDto; } public OrganizationMembershipStatus getOrganizationMembershipStatus(Organization organization, User user) { if (organization.getOrganizationUsers() != null) { for (OrganizationUser ou : organization.getOrganizationUsers()) { if (ou.getUser().getId() == user.getId()) { return ou.getStatus(); } } } // not a member of organization return null; } /** * Finds the {@link Organization} given by its id and returns an {@link OrganizationAdminUIDto}. * */ public OrganizationAdminUIDto findByIdAsAdminUIDto(final ServiceInvocationContext context, final long id) { logger.debug("Find organization with id = {}", id); DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { Organization org = wrapper.find(Organization.class, id); if (org == null) throw new RuntimeException(String.format("Organization with id=%s was not found in the DB.", id)); try { SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(org, null)); } catch (SecurityException e) { throw new RuntimeException(String.format("Organization with id=%s was not available.", id)); } User user = (User) context.getCommunicationChannel().getPrincipal().getUser(); wrapper.setOperationResult(convertOrganizationToOrganizationAdminUIDto(org, user)); } }); return (OrganizationAdminUIDto) wrapper.getOperationResult(); } /** * Called from client side to request the current organization filter. */ public OrganizationAdminUIDto findByNameAsAdminUIDto(final String name) { logger.debug("Find organization with name = {}", name); DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { List<Organization> rslt = wrapper.findByField(Organization.class, "name", name); if (rslt.size() == 1) { wrapper.setOperationResult(convertOrganizationToOrganizationAdminUIDto(rslt.get(0), null)); } } }); return (OrganizationAdminUIDto) wrapper.getOperationResult(); } /** * Finds all {@link Organization}s and returns a list of their corresponding {@link OrganizationAdminUIDto}. * * */ public List<OrganizationAdminUIDto> findAllAsAdminUIDto(final ServiceInvocationContext context, final boolean getAll) { logger.debug("Find all organizations"); final List<OrganizationAdminUIDto> list = new ArrayList<OrganizationAdminUIDto>(); new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { User user = (User) context.getCommunicationChannel().getPrincipal().getUser(); for (Organization org : wrapper.findAll(Organization.class)) { if (!getAll) { try { SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(org, null)); } catch (SecurityException e) { continue; } } else { if (!org.isActivated()) { continue; // do not return organizations that are not activated } if (getOrganizationMembershipStatus(org, user) != null) { continue; // do not return organizations where the user is already a member } } list.add(convertOrganizationToOrganizationAdminUIDto(org, user)); } } }); return list; } public List<OrganizationAdminUIDto> findMyOrganizationsAsADminUIDto(final long userId) { final List<OrganizationAdminUIDto> list = new ArrayList<OrganizationAdminUIDto>(); new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { if (userId != 0) { User user = wrapper.findByField(User.class, "id", userId).get(0); logger.debug("Find organizations where {} belongs", user); for (OrganizationUser ou : user.getOrganizationUsers()) { try { if (user.getId() != CommunicationPlugin.tlCurrentPrincipal.get().getUserId()) SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(ou.getOrganization(), null)); list.add(convertOrganizationToOrganizationAdminUIDto(ou.getOrganization(), user)); } catch (Exception e) { // do nothing } } } } }); return list; } /** * Finds all {@link Organization}s and returns a list of their corresponding {@link NamedDto}. * * <p> * Used for listboxes. * * */ public List<NamedDto> findAllAsNamedDto() { final List<NamedDto> list = new ArrayList<NamedDto>(); new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { for (Organization org : wrapper.findAll(Organization.class)) { try { SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(org, null)); } catch (SecurityException e) { continue; } list.add(new NamedDto(org.getId(), org.getName())); } } }); return list; } /** * Creates/Updates the {@link Organization} based on {@link OrganizationAdminUIDto} stored information. * Returns an error message (if there is already an organization with the same name, or <code>null</code> * if there were no errors. * * */ public String mergeAdminUIDto(ServiceInvocationContext context, OrganizationAdminUIDto dto) { return mergeAdminUIDto(context, dto, false); } String mergeAdminUIDto(ServiceInvocationContext context, final OrganizationAdminUIDto dto, final boolean requestMode) { logger.debug("Merge organization = {}", dto.getName()); final Organization initialOrganization = (Organization) new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { wrapper.setOperationResult(wrapper.find(Organization.class, dto.getId())); } }).getOperationResult(); DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { // first check if there is already an organization with the same name List<Organization> orgsWithSameName = wrapper.findByField(Organization.class, "name", dto.getName()); if (dto.getId() == 0 && orgsWithSameName != null && orgsWithSameName.size() > 0) { wrapper.setOperationResult("There is already an organization with this name!"); return; } // normal users are only allowed to add new organizations in request mode if (!requestMode) SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(initialOrganization, dto)); Organization org = wrapper.mergeDto(Organization.class, dto); org.setName(dto.getName()); org.setURL(dto.getURL()); org.setLabel(dto.getLabel()); org.setLogoURL(dto.getLogoURL()); org.setIconURL(dto.getIconURL()); org.setActivated(dto.isActivated()); org = wrapper.merge(org); } }); Organization newOrganization = (Organization) new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { wrapper.setOperationResult(wrapper.find(Organization.class, dto.getId())); } }).getOperationResult(); observable.notifyObservers(Arrays.asList(UPDATE, initialOrganization, newOrganization)); return (String) wrapper.getOperationResult(); } /** * Deletes all {@link Organization}s based on the list of their ids. * */ public void delete(final List<Integer> ids) { for (final Integer id : ids) { DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { Organization org = wrapper.find(Organization.class, Long.valueOf(id)); SecurityUtils.checkAdminSecurityEntitiesPermission(SecurityEntityAdaptor.toCsvString(org, null)); logger.debug("Delete {}", org); // this will also remove the Organization from the Group org.getGroups().clear(); // for (Iterator<OrganizationUser> it = org.getOrganizationUsers().iterator(); it.hasNext();) { // OrganizationUser ou = it.next(); // // manually remove the OrganizationUser from the User // ou.getUser().getOrganizationUsers().remove(ou); // it.remove(); // // and delete the OrganizationUser // wrapper.delete(ou); // } Query recentResources = wrapper.createQuery("SELECT e FROM RecentResource e WHERE e.organization = :organization"); recentResources.setParameter("organization", org); for (Object object : recentResources.list()) { RecentResource rr = (RecentResource) object; rr.setOrganization(null); } Query favoriteItems = wrapper.createQuery("SELECT e FROM FavoriteItem e WHERE e.organization = :organization"); favoriteItems.setParameter("organization", org); for (Object object : favoriteItems.list()) { FavoriteItem fav = (FavoriteItem) object; wrapper.delete(fav); } wrapper.delete(org); wrapper.setOperationResult(org); } }); observable.notifyObservers(Arrays.asList(DELETE, wrapper.getOperationResult())); } } /** * Returns the default path for one of the {@link Organization}s where the current user belongs. * * @author Mariana */ public String getOrganizationDefaultPath(List<String> organizationsFilter) { List<OrganizationAdminUIDto> organizations = findMyOrganizationsAsADminUIDto(CommunicationPlugin.tlCurrentPrincipal.get().getUserId()); for (OrganizationAdminUIDto organization : organizations) { if (organizationsFilter.contains(organization.getName()) || organizationsFilter.size() == 0) { String organizationDir = CommonPlugin.getInstance().getFlowerProperties().getProperty(UserService.ORGANIZATION_DIRECTORIES).split(",\\s*")[0]; organizationDir = MessageFormat.format(organizationDir, organization.getName()); organizationDir += "/" + CommonPlugin.getInstance().getFlowerProperties().getProperty(UserService.ORGANIZATION_DEFAULT_DIRECTORY); return organizationDir; } } return null; } public Organization getOrganizationForResource(String resourcePath) { String[] organizationDirectories = CommonPlugin.getInstance().getFlowerProperties().getProperty(UserService.ORGANIZATION_DIRECTORIES).split(",\\s*"); for (String dir : organizationDirectories) { dir = CommonPlugin.getInstance().getWorkspaceRoot().getPath() + "/" + dir; // append a / if the dir doesn't end in / if (dir.lastIndexOf("/") != dir.length() - 1) { dir += "/"; } dir = dir.replace(Matcher.quoteReplacement("{0}"), Matcher.quoteReplacement("(.*?)")); Matcher matcher = Pattern.compile(dir).matcher(resourcePath); matcher.find(); try { final String organizationName = matcher.group(1); DatabaseOperationWrapper wrapper = new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { List<Organization> orgs = wrapper.findByField(Organization.class, "name", organizationName); if (orgs.size() > 0) { wrapper.setOperationResult(orgs.get(0)); } } }); return (Organization) wrapper.getOperationResult(); } catch (IllegalStateException e) { // do nothing } } return null; } public SVNRepositoryURLEntity findSVNRepositoryURLByName(Organization org, String name) { for (SVNRepositoryURLEntity url : org.getSvnRepositoryURLs()) { if (url.getName().endsWith(name)) { return url; } } return null; } /** * Rename the anonymous user and groups belonging to the organization. */ private void onOrganizationUpdate(final Organization initialOrganization, final Organization newOrganization) { new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { String initialName = initialOrganization.getName(); String newName = newOrganization.getName(); if (!initialName.equals(newName)) { List<User> list = wrapper.findByField(User.class, "login", SecurityEntityAdaptor.getAnonymousUserLogin(initialOrganization)); if (list.size() > 0) { User user = list.get(0); User initialUser = wrapper.find(User.class, user.getId()); user.setLogin(user.getLogin().replace(initialName, newName)); user.setHashedPassword(Util.encrypt(user.getLogin())); user = wrapper.merge(user); observable.notifyObservers(Arrays.asList(UPDATE, initialUser, user)); } for (Group group : wrapper.findAll(Group.class)) { if (group.getOrganization() != null && group.getOrganization().getId() == initialOrganization.getId()) { Group initialGroup = wrapper.find(Group.class, group.getId()); group.setName(group.getName().replace(initialName, newName)); group = wrapper.merge(group); observable.notifyObservers(Arrays.asList(UPDATE, initialGroup, group)); } } } } }); } /** * Delete the anonymous user belonging to the organization. */ private void onOrganizationDelete(final Organization organization) { final List<User> list = new ArrayList<User>(); new DatabaseOperationWrapper(new DatabaseOperation() { @Override public void run() { list.addAll(wrapper.findByField(User.class, "login", SecurityEntityAdaptor.getAnonymousUserLogin(organization))); if (list.size() > 0) { wrapper.delete(list.get(0)); } } }); if (list.size() > 0) { observable.notifyObservers(Arrays.asList(DELETE, list.get(0))); } } private Observer organizationObserver = new Observer() { @Override public void update(Observable o, Object arg) { List<?> list = (List<?>) arg; if (list.get(0).equals(ServiceObservable.UPDATE) && list.get(1) instanceof Organization && list.get(2) instanceof Organization) { onOrganizationUpdate((Organization) list.get(1), (Organization) list.get(2)); } else { if (list.get(0).equals(ServiceObservable.DELETE) && list.get(1) instanceof Organization) { onOrganizationDelete((Organization) list.get(1)); } } } }; public Observer getOrganizationObserver() { return organizationObserver; } }