/**
* Copyright (C) 2015 Orange
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.francetelecom.clara.cloud.core.service;
import com.francetelecom.clara.cloud.commons.AuthorizationException;
import com.francetelecom.clara.cloud.commons.TechnicalException;
import com.francetelecom.clara.cloud.commons.ValidatorUtil;
import com.francetelecom.clara.cloud.core.service.exception.*;
import com.francetelecom.clara.cloud.coremodel.*;
import com.francetelecom.clara.cloud.services.dto.ApplicationDTO;
import com.francetelecom.clara.cloud.services.dto.ConfigOverrideDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static com.francetelecom.clara.cloud.coremodel.ApplicationSpecifications.*;
import static org.springframework.data.jpa.domain.Specifications.where;
/**
* Business implementation for Application management.
*
* All methods are defined as transactional. If no transaction is in progress
* during method call, then it will start a new transaction.
*/
public class ManageApplicationImpl implements ManageApplication {
private static final Logger log = LoggerFactory.getLogger(ManageApplicationImpl.class);
@Autowired
private SecurityUtils securityUtils;
@Autowired(required = true)
private ApplicationRepository applicationRepository;
@Autowired(required = true)
private ApplicationReleaseRepository applicationReleaseRepository;
@Autowired(required = true)
private PaasUserRepository paasUserRepository;
@Autowired(required = true)
private ConfigRoleRepository configRoleRepository;
@Override
public List<Application> findApplications() {
List<Application> applications = applicationRepository.findAll(isActive(), new Sort(Sort.Direction.ASC,"label"));
for (Application application : applications) {
application.setEditable(hasWritePermissionFor(application));
}
return applications;
}
@Override
public List<Application> findAccessibleApplications() {
if (securityUtils.currentUserIsAdmin()) {
return findApplications();
} else {
List<Application> applications = applicationRepository.findAll(where(isActive()).and(isPublicOrHasForMember(securityUtils.currentUser())));
for (Application application : applications) {
application.setEditable(hasWritePermissionFor(application));
}
return applications;
}
}
@Override
public List<Application> findMyApplications() {
return applicationRepository.findAll(where(isActive()).and(hasForMember(securityUtils.currentUser())), new Sort(Sort.Direction.ASC, "label"));
}
@Override
public long countApplications() {
return applicationRepository.count(isActive());
}
@Override
public ApplicationDTO findApplicationByLabel(String label) throws ApplicationNotFoundException {
Application application = applicationRepository.findOne(hasLabel(label));
if (application == null) {
String message = "Application with label[" + label + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
return new ApplicationDTO(application.getUID(), application.getCode(), application.getLabel(), application.getDescription(),
application.getApplicationRegistryUrl());
}
@Override
public boolean isApplicationLabelUnique(String label) {
return applicationRepository.findOne(where(isActive()).and(hasLabel(label))) == null;
}
private boolean isApplicationCodeUnique(String code) {
return applicationRepository.findOne(where(isActive()).and(hasCode(code))) == null;
}
private boolean applicationHasNoActiveApplicationReleases(String applicationUID) {
return applicationReleaseRepository.countApplicationReleasesByApplicationUID(applicationUID) == 0;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackForClassName = { "BusinessException" })
public String createPublicApplication(String code, String label, String description, URL applicationRegistryUrl, SSOId... members)
throws DuplicateApplicationException, PaasUserNotFoundException {
Application application = createApplication(code, label, description, applicationRegistryUrl, members);
application.setAsPublic();
applicationRepository.save(application);
return application.getUID();
}
private Application createApplication(String code, String label, String description, URL applicationRegistryUrl, SSOId... members)
throws PaasUserNotFoundException, DuplicateApplicationException, AuthorizationException {
log.debug("/******* create application with label[" + label + "] and code [" + code + "]**********/");
Application application = new Application(label, code);
application.setDescription(description);
if (applicationRegistryUrl != null) {
application.setApplicationRegistryUrl(applicationRegistryUrl);
}
final Set<SSOId> candidates = Arrays.asList(members).stream().collect(Collectors.toSet());
assertMembersExist(candidates);
application.setMembers(candidates);
assertHasPermissionFor(application);
// Validate model
ValidatorUtil.validate(application);
// if label is not unique throw exception
if (!isApplicationLabelUnique(application.getLabel())) {
String message = "Application with label[" + label + "] already exists.";
log.info(message);
throw new DuplicateApplicationException(message);
}
// if code is not unique throw exception
if (!isApplicationCodeUnique(application.getCode())) {
String message = "Application with code[" + code + "] already exists.";
log.info(message);
throw new DuplicateApplicationException(message);
}
return application;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackForClassName = { "BusinessException" })
public String createPrivateApplication(String code, String label, String description, URL applicationRegistryUrl, SSOId... members)
throws DuplicateApplicationException, PaasUserNotFoundException {
Application application = createApplication(code, label, description, applicationRegistryUrl, members);
application.setAsPrivate();
applicationRepository.save(application);
return application.getUID();
}
private void assertMembersExist(Set<SSOId> candidates) throws PaasUserNotFoundException {
// assert users with ssoids exist
for (SSOId candidate : candidates) {
if (paasUserRepository.findBySsoId(candidate) == null) {
final String message = "Cannot create application with member list: " + candidates + ". Member " + candidate + " is unknown.";
log.debug(message);
throw new PaasUserNotFoundException(message, candidate);
}
;
}
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackForClassName = { "BusinessException" })
public void deleteApplication(String applicationUID) throws ApplicationNotFoundException, AuthorizationException {
Application application = applicationRepository.findByUid(applicationUID);
if (application == null) {
String message = "Application with UID[" + applicationUID + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
assertHasPermissionFor(application);
// TODO we may to need to enforce this rule in Application entity,
// perhaps in markAsRemoved() operation
if (!applicationHasNoActiveApplicationReleases(applicationUID)) {
String message = "Application with UID[" + applicationUID + "] has active releases.";
log.info(message);
throw new IllegalStateException(message);
}
application.markAsRemoved();
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackForClassName = { "BusinessException" })
public boolean canBeDeleted(String applicationUID) throws ApplicationNotFoundException {
log.debug("/******* find application by UID[" + applicationUID + "] **********/");
Application application = applicationRepository.findByUid(applicationUID);
if (application == null) {
String message = "Application with UID[" + applicationUID + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
return applicationHasNoActiveApplicationReleases(applicationUID) && hasWritePermissionFor(application);
}
@Override
public Application findApplicationByUID(String applicationUID) throws ApplicationNotFoundException {
log.debug("/******* find application by UID[" + applicationUID + "] **********/");
Application application = applicationRepository.findByUid(applicationUID);
if (application == null) {
String message = "Application with UID[" + applicationUID + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
application.setEditable(hasWritePermissionFor(application));
return application;
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT, rollbackForClassName = { "BusinessException" })
public Application updateApplication(Application application) throws ApplicationNotFoundException, DuplicateApplicationException,
PaasUserNotFoundException {
log.debug("/******* update application with label[" + application.getLabel() + "] **********/");
Application persisted = applicationRepository.findByUid(application.getUID());
if (persisted == null) {
String message = "Application with UID[" + application.getUID() + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
// assert members exist
assertMembersExist(application.listMembers().stream().collect(Collectors.toSet()));
assertHasPermissionFor(persisted);
// if label has changed but new label is not unique -> throw
// exception
if (labelHasChanged(persisted, application) && !isApplicationLabelUnique(application.getLabel())) {
String message = "Application with label[" + application.getLabel() + "] already exists.";
log.info(message);
throw new DuplicateApplicationException(message);
}
// if code has changed but code is not unique throw exception
if (codeHasChanged(persisted, application) && !isApplicationCodeUnique(application.getCode())) {
String message = "Application with code[" + application.getCode() + "] already exists.";
log.info(message);
throw new DuplicateApplicationException(message);
}
return applicationRepository.save(application);
}
private void assertHasPermissionFor(Application application) throws AuthorizationException {
if (!hasWritePermissionFor(application))
throw new AuthorizationException();
}
private boolean hasWritePermissionFor(Application application) {
return securityUtils.hasWritePermissionFor(application);
}
private boolean labelHasChanged(Application persisted, Application updated) {
return !updated.getLabel().equals(persisted.getLabel());
}
private boolean codeHasChanged(Application persisted, Application updated) {
return !updated.getCode().equals(persisted.getCode());
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void purgeOldRemovedApplications() {
log.info("*** purge old application");
// find removed applications
List<Application> toPurgeApplications = applicationRepository.findAll(isRemoved());
// hard remove them
applicationRepository.delete(toPurgeApplications);
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public void purgeApplication(String uid) throws ApplicationNotFoundException {
deleteApplication(uid);
Application application = applicationRepository.findByUid(uid);
applicationRepository.delete(application);
}
@Override
public long countMyApplications() {
return applicationRepository.count(where(isActive()).and(hasForMember(securityUtils.currentUser())));
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public String createConfigRole(String applicationUID, String configRoleLabel, List<ConfigOverrideDTO> overrideConfigs) throws ApplicationNotFoundException, InvalidConfigOverrideException {
for (ConfigOverrideDTO overrideConfig : overrideConfigs) {
try {
ValidatorUtil.validate(overrideConfig);
} catch (TechnicalException e) {
throw new InvalidConfigOverrideException(e.getMessage(), e, overrideConfig);
}
}
Application application = applicationRepository.findByUid(applicationUID);
if (application == null) {
String message = "Application with UID[" + applicationUID + "] does not exist.";
log.info(message);
throw new ApplicationNotFoundException(message);
}
assertHasPermissionFor(application);
List<ConfigValue> configValues = new ArrayList<>();
for (ConfigOverrideDTO configOverrideDTO : overrideConfigs) {
configValues.add(new ConfigValue(configOverrideDTO.getConfigSet(), configOverrideDTO.getKey(), configOverrideDTO.getValue(),
configOverrideDTO.getComment()));
}
ConfigRole configRole = new ConfigRole(applicationUID);
configRole.setLastModificationComment(configRoleLabel);
configRole.setValues(configValues);
application.addConfigRole(configRole);
return configRole.getUID();
}
@Override
@Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.DEFAULT)
public ConfigRole findConfigRole(String configRoleUID) throws ConfigRoleNotFoundException {
log.debug("/******* find configrole by UID[" + configRoleUID + "] **********/");
ConfigRole configRole = configRoleRepository.findByUid(configRoleUID);
if (configRole == null) {
String message = "configrole with UID[" + configRoleUID + "] does not exist.";
log.info(message);
throw new ConfigRoleNotFoundException(message);
}
return configRole;
}
}