package com.feisystems.polrep.service;
import static com.feisystems.polrep.service.xacml.XACMLXPath.XPATH_POLICY_SET_ID;
import static com.feisystems.polrep.service.xacml.XACMLXPath.XPATH_POLICY_SET_POLICY_COMBINING_ALG_ID;
import static com.feisystems.polrep.util.AssertionUtils.assertNotExistsInPolicyRepository;
import static com.feisystems.polrep.util.AssertionUtils.assertPoliciesNotEmpty;
import static com.feisystems.polrep.util.AssertionUtils.assertPolicyId;
import static com.feisystems.polrep.util.AssertionUtils.assertPolicyIdConsistency;
import static com.feisystems.polrep.util.AssertionUtils.assertPolicyNotNull;
import static com.feisystems.polrep.util.AssertionUtils.assertUniqueIds;
import static java.util.stream.Collectors.toList;
import java.io.IOException;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Supplier;
import org.apache.commons.io.IOUtils;
import org.modelmapper.ModelMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.w3c.dom.Document;
import com.feisystems.polrep.domain.Policy;
import com.feisystems.polrep.infrastructure.PolicyRepository;
import com.feisystems.polrep.service.dto.PolicyContainerDto;
import com.feisystems.polrep.service.dto.PolicyContentContainerDto;
import com.feisystems.polrep.service.dto.PolicyContentDto;
import com.feisystems.polrep.service.dto.PolicyDto;
import com.feisystems.polrep.service.dto.PolicyMetadataContainerDto;
import com.feisystems.polrep.service.dto.PolicyMetadataDto;
import com.feisystems.polrep.service.exception.PolicyNotFoundException;
import com.feisystems.polrep.util.DOMUtils;
import com.feisystems.polrep.util.MappingUtils;
import com.feisystems.polrep.util.PolicyValidationUtils;
@Service
public class PolicyServiceImpl implements PolicyService {
private static final String DEFAULT_WILDCARD = "*";
private static final String LIKE = "%";
private static final String POLICY_SET_XML_TEMPLATE_FILE_NAME = "PolicySetTemplate.xml";
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private PolicyRepository policyRepository;
@Autowired
private ModelMapper modelMapper;
@Autowired
private PolicyCombiningAlgIdValidator policyCombiningAlgIdValidator;
@Override
@Transactional
public PolicyMetadataContainerDto addPolicies(
PolicyContentContainerDto policyContentContainerDto, boolean force) {
// Map to PolicyDto
final List<PolicyDto> requestPolicyDtos = policyContentContainerDto
.getPolicies().stream().map(MappingUtils::toPolicyDto)
.collect(toList());
// Assert unique ids
assertUniqueIds(requestPolicyDtos);
// Assert existence in policy repository if not forced
if (force == false) {
assertNotExistsInPolicyRepository(requestPolicyDtos,
policyRepository::findOne);
}
// Validate and map to domain objects
final List<Policy> policies = requestPolicyDtos.stream()
.map(PolicyValidationUtils::validateAndReturn)
.map(policyDto -> modelMapper.map(policyDto, Policy.class))
.collect(toList());
// Prepare response dtos from domain objects
final List<PolicyMetadataDto> responsePolicyDtos = policies
.stream()
.map(policy -> modelMapper.map(policy, PolicyMetadataDto.class))
.collect(toList());
// Save to repository
policyRepository.save(policies);
// Return response
return new PolicyMetadataContainerDto(responsePolicyDtos);
}
@Override
@Transactional
public void deletePolicy(String policyId) {
// Assert policy id
assertPolicyId(policyId);
// Delete policy
try {
policyRepository.delete(policyId);
} catch (final EmptyResultDataAccessException e) {
final StringBuilder errorBuilder = new StringBuilder();
errorBuilder.append(
"Policy/PolicySet cannot be found with given ID: ").append(
policyId);
throw new PolicyNotFoundException(errorBuilder.toString());
}
}
@Override
@Transactional(readOnly = true)
public PolicyMetadataContainerDto getAllPolicyMetadata() {
final List<Policy> allPolicies = policyRepository.findAll();
if (allPolicies == null || allPolicies.size() == 0) {
throw new PolicyNotFoundException(
"There are no policies in this policy repository.");
}
final List<PolicyMetadataDto> policyMetadataDtos = allPolicies
.stream()
.map(policy -> modelMapper.map(policy, PolicyMetadataDto.class))
.collect(toList());
return new PolicyMetadataContainerDto(policyMetadataDtos);
}
@Override
@Transactional(readOnly = true)
public PolicyContainerDto getPolicies(String policyId, String wildcard) {
// Assert policy id
assertPolicyId(policyId);
// if no wildcard is used (looking for exact match)
if (wildcard == null) {
// Try to find exact match for the policy
final Policy policy = policyRepository.findOne(policyId);
// Assert that the policy is found
assertPolicyNotNull(policy, policyId);
// Map policy to response object
final PolicyDto policyDto = modelMapper
.map(policy, PolicyDto.class);
// Return the response
return new PolicyContainerDto(policyDto);
}
// wildcard is used (looking for partial match)
else {
// Set default wildcard value if no value is specified
wildcard = "".equals(wildcard) ? DEFAULT_WILDCARD : wildcard;
// Setup partial match id format
final String policyIdLike = policyId.replace(wildcard, LIKE);
// Try to find partial match for the policies
final List<Policy> policies = policyRepository
.findAllByIdLike(policyIdLike);
// Assert that at least one policy is found
assertPoliciesNotEmpty(policies, policyId, wildcard);
// Map policies to response object
final List<PolicyDto> policyDtos = policies.stream()
.map(p -> modelMapper.map(p, PolicyDto.class))
.collect(toList());
// Return the response
return new PolicyContainerDto(policyDtos);
}
}
@Override
@Transactional(readOnly = true)
public PolicyDto getPoliciesCombinedAsPolicySet(String policyId,
String wildcard, String policySetId, String policyCombiningAlgId) {
// Validate policyCombiningAlgId
policyCombiningAlgId = policyCombiningAlgIdValidator
.validateAndReturn(policyCombiningAlgId);
// Get all policies
final PolicyContainerDto policies = getPolicies(policyId, wildcard);
// Construct a policy set template
final Document policySet = initPolicySetTemplate();
// Set policySetId and policyCombiningAlgId
if (!StringUtils.hasText(policySetId)) {
policySetId = UUID.randomUUID().toString();
}
DOMUtils.getNode(policySet, XPATH_POLICY_SET_ID).get()
.setNodeValue(policySetId);
DOMUtils.getNode(policySet, XPATH_POLICY_SET_POLICY_COMBINING_ALG_ID)
.get().setNodeValue(policyCombiningAlgId);
// Append all policies to the policy set template
policies.getPolicies()
.stream()
.map(PolicyDto::getPolicy)
.map(DOMUtils::bytesToDocument)
.map(Document::getDocumentElement)
.map(policy -> policy.cloneNode(true))
.map(clonedPolicy -> policySet.importNode(clonedPolicy, true))
.forEach(
importedPolicy -> policySet.getDocumentElement()
.appendChild(importedPolicy));
// Construct and return the response
final PolicyDto response = new PolicyDto();
response.setId(policySetId);
response.setPolicy(DOMUtils.documentToBytes(policySet));
response.setValid(PolicyValidationUtils.validate(response.getPolicy()));
debug(() -> new String(response.getPolicy()));
return response;
}
@Override
public Map<String, String> getPolicyCombiningAlgIds() {
return policyCombiningAlgIdValidator.getCombiningAlgs();
}
@Override
@Transactional
public PolicyMetadataDto updatePolicy(
PolicyContentDto updatePolicyRequestDto, String policyId) {
// Assert policy id
assertPolicyId(policyId);
// Map to policyDto
final PolicyDto policyDto = MappingUtils
.toPolicyDto(updatePolicyRequestDto.getPolicy());
// Assert that the policy ID in the request parameter matches with the
// ID in the policy content
assertPolicyIdConsistency(policyId, policyDto.getId());
// Try to find the policy
final Policy policy = policyRepository.findOne(policyId);
// Assert that the policy is found
assertPolicyNotNull(policy, policyId);
// Update the policy with new values
policy.setPolicy(updatePolicyRequestDto.getPolicy());
policy.setValid(PolicyValidationUtils.validate(updatePolicyRequestDto
.getPolicy()));
policyRepository.save(policy);
// Map policy to response object
final PolicyMetadataDto updatePolicyResponseDto = modelMapper.map(
policy, PolicyMetadataDto.class);
// Return response
return updatePolicyResponseDto;
}
private void debug(Supplier<String> logMsgSupplier) {
if (logger.isDebugEnabled()) {
logger.debug(logMsgSupplier.get());
}
}
private Document initPolicySetTemplate() {
byte[] policySetTemplateBytes = null;
try {
policySetTemplateBytes = IOUtils.toByteArray(getClass()
.getClassLoader().getResourceAsStream(
POLICY_SET_XML_TEMPLATE_FILE_NAME));
} catch (final IOException e) {
throw new RuntimeException(e);
}
return DOMUtils.bytesToDocument(policySetTemplateBytes);
}
}