package org.openstack.atlas.service.domain.services.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.openstack.atlas.service.domain.entities.AccountLimitType; import org.openstack.atlas.service.domain.entities.CertificateMapping; import org.openstack.atlas.service.domain.entities.LoadBalancer; import org.openstack.atlas.service.domain.entities.LoadBalancerStatus; import org.openstack.atlas.service.domain.exceptions.*; import org.openstack.atlas.service.domain.pojos.SslDetails; import org.openstack.atlas.service.domain.services.CertificateMappingService; import org.openstack.atlas.service.domain.services.LoadBalancerStatusHistoryService; import org.openstack.atlas.service.domain.services.helpers.SslTerminationHelper; import org.openstack.atlas.service.domain.services.helpers.StringHelper; import org.openstack.atlas.service.domain.util.Constants; import org.openstack.atlas.util.ca.zeus.ZeusCrtFile; import org.openstack.atlas.util.ca.zeus.ZeusUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.Collection; import java.util.List; import java.util.Set; @Service public class CertificateMappingServiceImpl extends BaseService implements CertificateMappingService { private final Log LOG = LogFactory.getLog(CertificateMappingServiceImpl.class); private static final ZeusUtils zeusUtils; @Autowired private LoadBalancerStatusHistoryService loadBalancerStatusHistoryService; @Autowired private AccountLimitServiceImpl accountLimitService; static { zeusUtils = new ZeusUtils(); } @Override @Transactional public CertificateMapping create(LoadBalancer messengerLb) throws UnprocessableEntityException, EntityNotFoundException, BadRequestException, ImmutableEntityException, LimitReachedException { ensureSslTerminationConfigIsAvailable(messengerLb.getId()); List<CertificateMapping> dbCertificateMappings = certificateMappingRepository.getAllForLoadBalancerId(messengerLb.getId()); CertificateMapping newMapping = messengerLb.getCertificateMappings().iterator().next(); int certMappingLimit = accountLimitService.getLimit(messengerLb.getAccountId(), AccountLimitType.CERTIFICATE_MAPPING_LIMIT); if (dbCertificateMappings.size() >= certMappingLimit) { throw new LimitReachedException(String.format("Certificate mapping limit reached. Limit is set to '%d'. Please contact support if you would like to increase your limit.", certMappingLimit)); } detectDuplicateHostName(dbCertificateMappings, newMapping); validateCertificateMapping(newMapping); if (newMapping.getIntermediateCertificate() != null && newMapping.getIntermediateCertificate().trim().isEmpty()) { newMapping.setIntermediateCertificate(null); } setLbToPendingUpdate(messengerLb); return certificateMappingRepository.save(newMapping, messengerLb.getId()); } @Override public List<CertificateMapping> getAllForLoadBalancerId(Integer lbId) throws EntityNotFoundException { ensureSslTerminationConfigIsAvailable(lbId); return certificateMappingRepository.getAllForLoadBalancerId(lbId); } @Override public CertificateMapping getByIdAndLoadBalancerId(Integer id, Integer lbId) throws EntityNotFoundException { ensureSslTerminationConfigIsAvailable(lbId); return certificateMappingRepository.getByIdAndLoadBalancerId(id, lbId); } @Override @Transactional public void update(LoadBalancer messengerLb) throws EntityNotFoundException, UnprocessableEntityException, BadRequestException, ImmutableEntityException { ensureSslTerminationConfigIsAvailable(messengerLb.getId()); LoadBalancer dbLb = loadBalancerRepository.getByIdAndAccountId(messengerLb.getId(), messengerLb.getAccountId()); Set<CertificateMapping> dbCertMappings = dbLb.getCertificateMappings(); CertificateMapping certificateMappingToUpdate = messengerLb.getCertificateMappings().iterator().next(); if (!loadBalancerContainsMapping(dbLb, certificateMappingToUpdate)) { LOG.debug("Certificate mapping to update not found. Sending response to client..."); throw new EntityNotFoundException(Constants.CertificateMappingNotFound); } detectDuplicateHostName(dbCertMappings, certificateMappingToUpdate); LOG.debug("Certificate mappings on dbLoadBalancer: " + dbCertMappings.size()); for (CertificateMapping dbCertMapping : dbCertMappings) { if (dbCertMapping.getId().equals(certificateMappingToUpdate.getId())) { LOG.info("Certificate mapping to be update found: " + dbCertMapping.getId()); if (certificateMappingToUpdate.getPrivateKey() != null) { dbCertMapping.setPrivateKey(certificateMappingToUpdate.getPrivateKey()); } if (certificateMappingToUpdate.getCertificate() != null) { dbCertMapping.setCertificate(certificateMappingToUpdate.getCertificate()); } if (certificateMappingToUpdate.getIntermediateCertificate() != null) { if (certificateMappingToUpdate.getIntermediateCertificate().trim().isEmpty()) { dbCertMapping.setIntermediateCertificate(null); } else { dbCertMapping.setIntermediateCertificate(certificateMappingToUpdate.getIntermediateCertificate()); } } if (certificateMappingToUpdate.getHostName() != null) { dbCertMapping.setHostName(certificateMappingToUpdate.getHostName()); } validateCertificateMapping(dbCertMapping); break; } } setLbToPendingUpdate(messengerLb); certificateMappingRepository.update(dbLb); } @Override @Transactional public void prepareForDelete(LoadBalancer messengerLb) throws EntityNotFoundException, ImmutableEntityException, UnprocessableEntityException { ensureSslTerminationConfigIsAvailable(messengerLb.getId()); certificateMappingRepository.getByIdAndLoadBalancerId(messengerLb.getCertificateMappings().iterator().next().getId(), messengerLb.getId()); setLbToPendingUpdate(messengerLb); } @Override @Transactional public void deleteByIdAndLoadBalancerId(Integer id, Integer lbId) throws EntityNotFoundException { ensureSslTerminationConfigIsAvailable(lbId); LoadBalancer dbLoadBalancer = loadBalancerRepository.getById(lbId); certificateMappingRepository.delete(dbLoadBalancer, id); } private void setLbToPendingUpdate(LoadBalancer lb) throws EntityNotFoundException, UnprocessableEntityException, ImmutableEntityException { lb = loadBalancerRepository.getById(lb.getId()); if (!loadBalancerRepository.testAndSetStatus(lb.getAccountId(), lb.getId(), LoadBalancerStatus.PENDING_UPDATE, false)) { String message = StringHelper.immutableLoadBalancer(lb); LOG.warn(message); throw new ImmutableEntityException(message); } else { loadBalancerStatusHistoryService.save(lb.getAccountId(), lb.getId(), LoadBalancerStatus.PENDING_UPDATE); } } private void ensureSslTerminationConfigIsAvailable(Integer lbId) throws EntityNotFoundException { try { sslTerminationRepository.getSslTerminationByLbId(lbId); } catch (EntityNotFoundException e) { throw new EntityNotFoundException(Constants.SslTerminationNotFound); } } private void detectDuplicateHostName(Collection<CertificateMapping> dbCertificateMappings, CertificateMapping mappingToCheck) throws UnprocessableEntityException { CertificateMapping dbMappingWithHostName = getMappingWithDuplicateHostName(mappingToCheck, dbCertificateMappings); if (dbMappingWithHostName != null && !dbMappingWithHostName.getId().equals(mappingToCheck.getId())) { String message = String.format("Duplicate host name detected. Certificate mapping with id '%d' has already configured the host name provided.", dbMappingWithHostName.getId()); throw new UnprocessableEntityException(message); } } private void validateCertificateMapping(CertificateMapping mapping) throws BadRequestException { SslDetails sslDetails = new SslDetails(mapping.getPrivateKey(), mapping.getCertificate(), mapping.getIntermediateCertificate()); sslDetails = SslDetails.sanitize(sslDetails); ZeusCrtFile zeusCrtFile = zeusUtils.buildZeusCrtFileLbassValidation(sslDetails.getPrivateKey(), sslDetails.getCertificate(), sslDetails.getIntermediateCertificate()); SslTerminationHelper.verifyCertificationCredentials(zeusCrtFile); } private CertificateMapping getMappingWithDuplicateHostName(CertificateMapping newMapping, Collection<CertificateMapping> dbCertificateMappings) { for (CertificateMapping dbCertificateMapping : dbCertificateMappings) { if (newMapping.getHostName().equals(dbCertificateMapping.getHostName())) { return dbCertificateMapping; } } return null; } private boolean loadBalancerContainsMapping(LoadBalancer lb, CertificateMapping certificateMapping) { for (CertificateMapping m : lb.getCertificateMappings()) { if (m.getId().equals(certificateMapping.getId())) { return true; } } return false; } }