/*
* LinShare is an open source filesharing software, part of the LinPKI software
* suite, developed by Linagora.
*
* Copyright (C) 2015 LINAGORA
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version, provided you comply with the Additional Terms applicable for
* LinShare software by Linagora pursuant to Section 7 of the GNU Affero General
* Public License, subsections (b), (c), and (e), pursuant to which you must
* notably (i) retain the display of the “LinShare™” trademark/logo at the top
* of the interface window, the display of the “You are using the Open Source
* and free version of LinShare™, powered by Linagora © 2009–2015. Contribute to
* Linshare R&D by subscribing to an Enterprise offer!” infobox and in the
* e-mails sent with the Program, (ii) retain all hypertext links between
* LinShare and linshare.org, between linagora.com and Linagora, and (iii)
* refrain from infringing Linagora intellectual property rights over its
* trademarks and commercial brands. Other Additional Terms apply, see
* <http://www.linagora.com/licenses/> for more details.
*
* 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 Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License and
* its applicable Additional Terms for LinShare along with this program. If not,
* see <http://www.gnu.org/licenses/> for the GNU Affero General Public License
* version 3 and <http://www.linagora.com/licenses/> for the Additional Terms
* applicable to LinShare software.
*/
package org.linagora.linshare.core.business.service.impl;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import org.linagora.linshare.core.business.service.AbstractFunctionalityBusinessService;
import org.linagora.linshare.core.domain.constants.Policies;
import org.linagora.linshare.core.domain.entities.AbstractDomain;
import org.linagora.linshare.core.domain.entities.AbstractFunctionality;
import org.linagora.linshare.core.domain.entities.Policy;
import org.linagora.linshare.core.exception.BusinessErrorCode;
import org.linagora.linshare.core.exception.BusinessException;
import org.linagora.linshare.core.repository.AbstractDomainRepository;
import org.linagora.linshare.core.repository.AbstractFunctionalityRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.Assert;
public abstract class AbstractFunctionalityBusinessServiceImpl<T extends AbstractFunctionality> implements AbstractFunctionalityBusinessService<T> {
private final static Logger logger = LoggerFactory.getLogger(AbstractFunctionalityBusinessServiceImpl.class);
protected AbstractFunctionalityRepository<T> repository;
protected AbstractDomainRepository abstractDomainRepository;
public AbstractFunctionalityBusinessServiceImpl(AbstractFunctionalityRepository<T> functionalityRepository,
AbstractDomainRepository abstractDomainRepository) {
super();
this.repository = functionalityRepository;
this.abstractDomainRepository = abstractDomainRepository;
}
/**
* Helper : convert Functionality list to InnerFunctionality list.
*
* @param functionalities
* @return innerFunctionalities
*/
private Set<InnerFunctionality> convertToInnerFunctionality(Set<T> functionalities) {
Set<InnerFunctionality> res = new HashSet<InnerFunctionality>();
for (T f : functionalities) {
res.add(new InnerFunctionality(f));
}
return res;
}
/**
* Helper : convert InnerFunctionality list to Functionality list.
* @param functionalities
* @param exclude
* @param domain
* @return
*/
private Set<T> convertToFunctionality(Set<InnerFunctionality> functionalities, List<String> exclude, AbstractDomain domain) {
Set<T> res = new HashSet<T>();
if (exclude == null) {
exclude = new ArrayList<String>();
}
for (InnerFunctionality f : functionalities) {
if(!exclude.contains(f.getFunctionality().getIdentifier())) {
@SuppressWarnings("unchecked")
T functionality = (T) f.getFunctionality().clone();
functionality.setDomain(domain);
initUpdateRight(functionality, domain);
res.add(functionality);
}
}
return res;
}
/**
* get all functionalities from current domain, then ask parent domains to
* get missing ones, recursively until all root domain is reached. At this
* point all LinShare functionalities are collected in the result list.
*
* @param domain
* @return result contains unique list of InnerFunctionality.
* InnerFunctionality is an association of functionality identifier
* and a functionality.
*/
private Set<InnerFunctionality> getAllInnerFunctionalities(AbstractDomain domain) {
Assert.notNull(domain);
Set<InnerFunctionality> res = new HashSet<InnerFunctionality>();
// Copy all functionalities from this domain to result list
res.addAll(convertToInnerFunctionality(repository.findAll(domain)));
if (domain.getParentDomain() != null) {
// get parent functionalities using recursive call
for (InnerFunctionality innerFunctionality : this.getAllInnerFunctionalities(domain.getParentDomain())) {
// only functionality identifier is compared.
if (!res.contains(innerFunctionality)) {
res.add(innerFunctionality);
}
}
}
return res;
}
private T getParentFunctionality(AbstractDomain domain, String functionalityIdentifier) {
Assert.notNull(domain);
Assert.notNull(functionalityIdentifier);
T res = null;
AbstractDomain parentDomain = domain.getParentDomain();
if (parentDomain != null) {
res = repository.findByDomain(parentDomain, functionalityIdentifier);
// no functionality was found in the current parentDomain. Trying parentDomain of parentDomain, using recursive call.
if (res == null) {
res = getParentFunctionality(parentDomain, functionalityIdentifier);
}
}
return res;
}
@Override
public Set<T> getAllFunctionalities(AbstractDomain domain)
throws BusinessException {
return convertToFunctionality(this.getAllInnerFunctionalities(domain), null, domain);
}
@Override
public Set<T> getAllFunctionalities(AbstractDomain domain, List<String> exclude) {
return convertToFunctionality(this.getAllInnerFunctionalities(domain), exclude, domain);
}
@Override
public void initUpdateRight(T functionality, AbstractDomain domain) {
// ancestor functionality could be null
// It is loaded in advance for performance purpose
T ancestorFunc = getParentFunctionality(domain, functionality.getIdentifier());
boolean parentAllowAPUpdate = activationPolicyIsMutable(functionality, domain, ancestorFunc);
boolean parentAllowCPUpdate = false;
boolean parentAllowDPUpdate = false;
functionality.setDisplayable(false);
functionality.getActivationPolicy().setParentAllowUpdate(parentAllowAPUpdate);
Policy delegationPolicy = functionality.getDelegationPolicy();
if (!parentAllowAPUpdate && functionality.getActivationPolicy().isForbidden()) {
functionality.getConfigurationPolicy().setParentAllowUpdate(false);
if (delegationPolicy != null) {
delegationPolicy.setParentAllowUpdate(false);
}
functionality.setParentAllowParametersUpdate(false);
} else {
// CP update right
parentAllowCPUpdate = configurationPolicyIsMutable(functionality, domain, ancestorFunc);
functionality.getConfigurationPolicy().setParentAllowUpdate(parentAllowCPUpdate);
// DP update right
if (delegationPolicy != null) {
parentAllowDPUpdate = delegationPolicyIsMutable(functionality, domain, ancestorFunc);
delegationPolicy.setParentAllowUpdate(parentAllowDPUpdate);
}
// Parameters update right
boolean parentAllowParametersUpdate = parametersAreMutable(functionality, domain, ancestorFunc);
functionality.setParentAllowParametersUpdate(parentAllowParametersUpdate);
}
if (parentAllowAPUpdate) {
functionality.setDisplayable(true);
}
if(parentAllowCPUpdate) {
functionality.setDisplayable(true);
}
if(parentAllowDPUpdate) {
functionality.setDisplayable(true);
}
}
protected boolean activationPolicyIsMutable(T functionality, AbstractDomain domain, T ancestorFunc) throws BusinessException {
Assert.notNull(functionality);
Assert.notNull(domain);
// Check if the current functionality belong to the current domain.
if (functionality.getDomain().getUuid().equals(domain.getUuid())) {
// The current functionality belong to the current domain.
// We check if the parent domain allow the current domain to
// modify/override activation policy configuration.
if (ancestorFunc == null) {
if (functionality.getActivationPolicy().isSystem()) {
return false;
}
return true;
} else if (ancestorFunc.getActivationPolicy().isMutable()) {
return true;
}
} else {
// the current functionality belongs to the parent functionality, so it could be considered as an ancestor.
if (functionality.getActivationPolicy().isMutable()) {
return true;
}
}
return false;
}
protected boolean configurationPolicyIsMutable(T functionality, AbstractDomain domain, T ancestorFunc) throws BusinessException {
Assert.notNull(functionality);
Assert.notNull(domain);
// Check if the current functionality belong to the current domain.
if (functionality.getDomain().getUuid().equals(domain.getUuid())) {
// we have to check if we have the permission to modify the configuration status of this functionality
// We check if the parent domain allow the current domain to
// modify/override activation policy configuration.
if (ancestorFunc == null) {
if (functionality.getConfigurationPolicy().isSystem()) {
return false;
}
return true;
} else if (ancestorFunc.getConfigurationPolicy().isMutable()) {
return true;
}
} else {
// The current functionality belong to a parent domain.
if (functionality.getConfigurationPolicy().isMutable()) {
return true;
}
}
return false;
}
protected boolean parametersAreMutable(T functionality, AbstractDomain domain, T ancestorFunc) throws BusinessException {
Assert.notNull(functionality);
Assert.notNull(domain);
// No need to check every conditions if there is no parameters to manage
if (!functionality.hasSomeParam()) {
return false;
}
// Check if the current functionality belong to the current domain.
if (functionality.getDomain().getUuid().equals(domain.getUuid())) {
// we have to check if we have the permission to modify the configuration status of this functionality
// We check if the parent domain allow the current domain to
// modify/override activation policy configuration.
if (ancestorFunc == null) {
// This functionality belongs to the root domain.
if (functionality.isSystem()) {
return false;
}
return true;
} else {
if (ancestorFunc.isSystem()) {
return false;
}
if (ancestorFunc.getActivationPolicy().isForbidden()) {
return false;
}
if (ancestorFunc.getConfigurationPolicy().isForbidden()) {
return false;
}
return true;
}
} else {
// The current functionality belong to a parent domain.
if (functionality.isSystem()) {
return false;
}
if (functionality.getActivationPolicy().isForbidden()) {
return false;
}
return functionality.getConfigurationPolicy().getStatus();
}
}
protected boolean delegationPolicyIsMutable(T functionality, AbstractDomain domain, T ancestorFunc) {
Assert.notNull(functionality);
Assert.notNull(domain);
// Check if the current functionality belong to the current domain.
if (functionality.getDomain().getUuid().equals(domain.getUuid())) {
// we have to check if we have the permission to modify the configuration status of this functionality
// We check if the parent domain allow the current domain to
// modify/override activation policy configuration.
if (ancestorFunc == null) {
if (functionality.getDelegationPolicy() != null) {
if (!functionality.getDelegationPolicy().isSystem()) {
return true;
}
}
return false;
} else if (ancestorFunc.getDelegationPolicy() != null && ancestorFunc.getDelegationPolicy().isMutable()) {
return true;
}
} else {
// The current functionality belong to a parent domain.
if (functionality.getDelegationPolicy() != null && functionality.getDelegationPolicy().isMutable()) {
return true;
}
}
return false;
}
protected T getFunctionalityEntityByIdentifiers(AbstractDomain domain, String functionalityId) {
Assert.notNull(domain);
Assert.notNull(functionalityId);
T fonc = repository.findByDomain(domain, functionalityId);
if (fonc == null && domain.getParentDomain() != null) {
fonc = getFunctionalityEntityByIdentifiers(domain.getParentDomain(), functionalityId);
}
return fonc;
}
@Override
public T getFunctionality(AbstractDomain domain, String functionalityId) throws BusinessException {
Assert.notNull(domain);
Assert.notNull(functionalityId);
T functionality = getFunctionalityEntityByIdentifiers(domain, functionalityId);
if (functionality == null) {
throw new BusinessException(BusinessErrorCode.NO_SUCH_ELEMENT, "Functionality not found.");
}
// Never returns the entity when we try to modify the functionality.
// The current functionality returned could belong to a parent domain.
// In this case, the functionality will be clone, linked to the input domain.
@SuppressWarnings("unchecked")
T clone = (T)functionality.clone();
// Wet set the input domain to fake the out side world.
clone.setDomain(domain);
initUpdateRight(clone, domain);
return clone;
}
@Override
public T update(String domainId, T functionality) throws BusinessException {
AbstractDomain currentDomain = findDomain(domainId);
T entity = getFunctionalityEntityByIdentifiers(currentDomain, functionality.getIdentifier());
if (entity.getDomain().getUuid().equals(functionality.getDomain().getUuid())) {
// This functionality belongs to the current domain.
logger.debug("this functionality belongs to the current domain");
entity.updateFunctionalityFrom(functionality);
repository.update(entity);
permissionPropagationForActivationPolicy(entity);
permissionPropagationForConfigurationPolicy(entity);
if (entity.getDelegationPolicy() != null) {
permissionPropagationForDelegationPolicy(entity);
}
} else {
// This functionality does not belong to the current domain.
logger.debug("this functionality does not belong to the current domain");
if (!functionality.businessEquals(entity, true)) {
// This functionality is different, it needs to be persist.
functionality.setDomain(currentDomain);
if (repository.findByDomain(currentDomain, functionality.getIdentifier()) == null) {
repository.create(functionality);
logger.info("Update by creation of a new functionality for : " + functionality.getIdentifier() + " link to domain : " + currentDomain.getUuid());
} else {
// TODO : to be check : This could really happen ? odd !
logger.error("This should not happen ! You does not have the right to update the functionnality (All) '" + functionality +"' in domain '" + currentDomain +"'");
throw new BusinessException(BusinessErrorCode.UNAUTHORISED_FUNCTIONALITY_UPDATE_ATTEMPT, "You does not have the right to update this functionality");
}
} else { // no differences
logger.debug("functionality " + functionality.getIdentifier()+ " was not modified.");
}
}
return this.getFunctionality(currentDomain, entity.getIdentifier());
}
@Override
public void delete(String domainId, String functionalityId) throws IllegalArgumentException, BusinessException {
Assert.notNull(domainId);
Assert.notNull(functionalityId);
AbstractDomain domain = findDomain(domainId);
T functionality = getFunctionalityEntityByIdentifiers(domain, functionalityId);
// The functionality belong to the current domain. We can delete it.
if (functionality.getDomain().getUuid().equals(domainId)){
logger.debug("suppression of the functionality : " + domainId + " : " + functionalityId);
T rawFunc = repository.findByDomain(domain, functionalityId);
repository.delete(rawFunc);
domain.getFunctionalities().remove(rawFunc);
abstractDomainRepository.update(domain);
} else {
logger.warn("You are currently in the domain : " + domainId + ". The functionaliy : " + functionalityId + ", you are trying to delete, belongs to the domain : " + functionality.getDomain().getUuid() + ".");
}
}
private void deleteFunctionalityRecursivly(AbstractDomain domain, String functionalityIdentifier) throws IllegalArgumentException, BusinessException {
if(domain != null ) {
for (AbstractDomain subDomain : domain.getSubdomain()) {
Set<T> functionalities = repository.findAll(subDomain);
for (T functionality : functionalities) {
if(functionality.getIdentifier().equals(functionalityIdentifier)) {
repository.delete(functionality);
functionalities.remove(functionality);
break;
}
}
deleteFunctionalityRecursivly(subDomain, functionalityIdentifier);
}
}
}
private void updateActivationPolicyRecursivly(AbstractDomain domain, T functionality) throws IllegalArgumentException, BusinessException {
if(domain != null ) {
for (AbstractDomain subDomain : domain.getSubdomain()) {
for (T f : repository.findAll(subDomain)) {
if(f.getIdentifier().equals(functionality.getIdentifier())) {
f.getActivationPolicy().updatePolicyFrom(functionality.getActivationPolicy());
repository.update(f);
break;
}
}
updateActivationPolicyRecursivly(subDomain, functionality);
}
}
}
private void updateConfigurationPolicyRecursivly(AbstractDomain domain, T functionality, boolean copyContent) throws IllegalArgumentException, BusinessException {
if(domain != null ) {
for (AbstractDomain subDomain : domain.getSubdomain()) {
for (T f : repository.findAll(subDomain)) {
if(f.getIdentifier().equals(functionality.getIdentifier())) {
f.getConfigurationPolicy().updatePolicyFrom(functionality.getConfigurationPolicy());
if(copyContent) {
f.updateFunctionalityValuesOnlyFrom(functionality);
}
repository.update(f);
break;
}
}
updateConfigurationPolicyRecursivly(subDomain, functionality, copyContent);
}
}
}
private void updateDelegationPolicyRecursivly(AbstractDomain domain, T functionality) throws IllegalArgumentException, BusinessException {
if(domain != null ) {
for (AbstractDomain subDomain : domain.getSubdomain()) {
for (T f : repository.findAll(subDomain)) {
if(f.getIdentifier().equals(functionality.getIdentifier())) {
f.getDelegationPolicy().updatePolicyFrom(functionality.getDelegationPolicy());
repository.update(f);
break;
}
}
updateDelegationPolicyRecursivly(subDomain, functionality);
}
}
}
private void permissionPropagationForActivationPolicy(T functionalityEntity) throws IllegalArgumentException, BusinessException {
if(functionalityEntity.getActivationPolicy().getPolicy().equals(Policies.FORBIDDEN)) {
// We have to delete the activation policy of each functionality from all the sub domains
deleteFunctionalityRecursivly(functionalityEntity.getDomain(), functionalityEntity.getIdentifier());
} else if(functionalityEntity.getActivationPolicy().getPolicy().equals(Policies.MANDATORY)) {
// We have to update the activation policy of each functionality from all the sub domains
updateActivationPolicyRecursivly(functionalityEntity.getDomain(), functionalityEntity);
}
}
private void permissionPropagationForConfigurationPolicy(T functionalityEntity) throws IllegalArgumentException, BusinessException {
if(functionalityEntity.getConfigurationPolicy().getPolicy().equals(Policies.FORBIDDEN)) {
// We have to update the configuration policy of each functionality from all the sub domains
// The parameters of the current functionality are propagated to all sub functionalities
updateConfigurationPolicyRecursivly(functionalityEntity.getDomain(), functionalityEntity, true);
} else if(functionalityEntity.getConfigurationPolicy().getPolicy().equals(Policies.MANDATORY)) {
// We have to update the configuration policy of each functionality from all the sub domains
updateConfigurationPolicyRecursivly(functionalityEntity.getDomain(), functionalityEntity, false);
}
}
private void permissionPropagationForDelegationPolicy(T functionalityEntity) throws IllegalArgumentException, BusinessException {
if(functionalityEntity.getConfigurationPolicy().getPolicy().equals(Policies.FORBIDDEN)) {
// We have to update the delegation policy of each functionality from all the sub domains
updateDelegationPolicyRecursivly(functionalityEntity.getDomain(), functionalityEntity);
} else if(functionalityEntity.getConfigurationPolicy().getPolicy().equals(Policies.MANDATORY)) {
// We have to update the delegation policy of each functionality from all the sub domains
updateDelegationPolicyRecursivly(functionalityEntity.getDomain(), functionalityEntity);
}
}
private class InnerFunctionality {
private final T functionality;
private final String identifier;
public InnerFunctionality(T functionality) {
super();
this.functionality = functionality;
this.identifier = functionality.getIdentifier();
}
public T getFunctionality() {
return functionality;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((identifier == null) ? 0 : identifier.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
@SuppressWarnings("unchecked")
InnerFunctionality other = (InnerFunctionality) obj;
if (identifier == null) {
if (other.identifier != null)
return false;
} else if (!identifier.equals(other.identifier))
return false;
return true;
}
@Override
public String toString() {
return "InnerFunctionality : " + identifier + "(" + functionality.getDomain().getUuid() + ")";
}
}
private AbstractDomain findDomain(String domain) throws BusinessException {
AbstractDomain abstractDomain = abstractDomainRepository.findById(domain);
if (abstractDomain == null) {
throw new BusinessException(BusinessErrorCode.DOMAIN_DO_NOT_EXIST, "The input domain does not exist.");
}
return abstractDomain;
}
}