package com.sequenceiq.cloudbreak.service.credential;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import javax.inject.Inject;
import javax.transaction.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.stereotype.Service;
import com.sequenceiq.cloudbreak.common.type.APIResourceType;
import com.sequenceiq.cloudbreak.common.type.CbUserRole;
import com.sequenceiq.cloudbreak.controller.BadRequestException;
import com.sequenceiq.cloudbreak.controller.NotFoundException;
import com.sequenceiq.cloudbreak.domain.CbUser;
import com.sequenceiq.cloudbreak.domain.Credential;
import com.sequenceiq.cloudbreak.domain.Stack;
import com.sequenceiq.cloudbreak.repository.CredentialRepository;
import com.sequenceiq.cloudbreak.repository.StackRepository;
import com.sequenceiq.cloudbreak.service.DuplicateKeyValueException;
import com.sequenceiq.cloudbreak.service.notification.Notification;
import com.sequenceiq.cloudbreak.service.notification.NotificationSender;
import com.sequenceiq.cloudbreak.service.stack.connector.adapter.ServiceProviderCredentialAdapter;
@Service
@Transactional
public class CredentialService {
private static final Logger LOGGER = LoggerFactory.getLogger(CredentialService.class);
@Inject
private CredentialRepository credentialRepository;
@Inject
private StackRepository stackRepository;
@Inject
private ServiceProviderCredentialAdapter credentialAdapter;
@Inject
private NotificationSender notificationSender;
public Set<Credential> retrievePrivateCredentials(CbUser user) {
return credentialRepository.findForUser(user.getUserId());
}
public Set<Credential> retrieveAccountCredentials(CbUser user) {
if (user.getRoles().contains(CbUserRole.ADMIN)) {
return credentialRepository.findAllInAccount(user.getAccount());
} else {
return credentialRepository.findPublicInAccountForUser(user.getUserId(), user.getAccount());
}
}
@PostAuthorize("hasPermission(returnObject,'read')")
public Credential get(Long id) {
Credential credential = credentialRepository.findOne(id);
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", id));
} else {
return credential;
}
}
@Transactional(Transactional.TxType.NEVER)
public Map<String, String> interactiveLogin(CbUser user, Credential credential) {
LOGGER.debug("Interactive login: [User: '{}', Account: '{}']", user.getUsername(), user.getAccount());
credential.setOwner(user.getUserId());
credential.setAccount(user.getAccount());
return credentialAdapter.interactiveLogin(credential);
}
@Transactional(Transactional.TxType.NEVER)
public Credential create(CbUser user, Credential credential) {
LOGGER.debug("Creating credential: [User: '{}', Account: '{}']", user.getUsername(), user.getAccount());
credential.setOwner(user.getUserId());
credential.setAccount(user.getAccount());
return saveCredential(credential);
}
@Transactional(Transactional.TxType.NEVER)
public Credential create(String userId, String account, Credential credential) {
LOGGER.debug("Creating credential: [UserId: '{}', Account: '{}']", userId, account);
credential.setOwner(userId);
credential.setAccount(account);
return saveCredential(credential);
}
@Transactional(Transactional.TxType.NEVER)
@Retryable(value = BadRequestException.class, maxAttempts = 10, backoff = @Backoff(delay = 2000))
public Credential createWithRetry(String userId, String account, Credential credential) {
return create(userId, account, credential);
}
public void sendErrorNotification(String owner, String account, String cloudPlatform, String errorMessage) {
Notification notification = new Notification();
notification.setEventType("CREDENTIAL_CREATE_FAILED");
notification.setEventTimestamp(new Date());
notification.setEventMessage(errorMessage);
notification.setOwner(owner);
notification.setAccount(account);
notification.setCloud(cloudPlatform);
notificationSender.send(notification);
}
private void sendNotification(Credential credential) {
Notification notification = new Notification();
notification.setEventType("CREDENTIAL_CREATED");
notification.setEventTimestamp(new Date());
notification.setEventMessage("Credential created");
notification.setOwner(credential.getOwner());
notification.setAccount(credential.getAccount());
notification.setCloud(credential.cloudPlatform());
notificationSender.send(notification);
}
private Credential saveCredential(Credential credential) {
credential = credentialAdapter.init(credential);
Credential savedCredential;
try {
savedCredential = credentialRepository.save(credential);
sendNotification(credential);
} catch (DataIntegrityViolationException ex) {
throw new DuplicateKeyValueException(APIResourceType.CREDENTIAL, credential.getName(), ex);
}
return savedCredential;
}
public Credential getPublicCredential(String name, CbUser user) {
Credential credential = credentialRepository.findOneByName(name, user.getAccount());
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", name));
} else {
return credential;
}
}
public Credential getPrivateCredential(String name, CbUser user) {
Credential credential = credentialRepository.findByNameInUser(name, user.getUserId());
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", name));
} else {
return credential;
}
}
@Transactional(Transactional.TxType.NEVER)
public void delete(Long id, CbUser user) {
Credential credential = credentialRepository.findByIdInAccount(id, user.getAccount());
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", id));
}
delete(credential, user);
}
@Transactional(Transactional.TxType.NEVER)
public void delete(String name, CbUser user) {
Credential credential = credentialRepository.findByNameInAccount(name, user.getAccount(), user.getUserId());
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", name));
}
delete(credential, user);
}
@Transactional(Transactional.TxType.NEVER)
public Credential update(Long id) {
Credential credential = get(id);
if (credential == null) {
throw new NotFoundException(String.format("Credential '%s' not found.", id));
} else {
return credentialAdapter.update(credential);
}
}
private void delete(Credential credential, CbUser user) {
if (!user.getUserId().equals(credential.getOwner()) && !user.getRoles().contains(CbUserRole.ADMIN)) {
throw new BadRequestException("Credentials can be deleted only by account admins or owners.");
}
List<Stack> stacks = stackRepository.findByCredential(credential.getId());
if (stacks.isEmpty()) {
delete(credential);
} else {
throw new BadRequestException(String.format("Credential '%d' is in use, cannot be deleted.", credential.getId()));
}
}
public void delete(Credential credential) {
archiveCredential(credential);
}
private String generateArchiveName(String name) {
//generate new name for the archived credential to by pass unique constraint
return new StringBuilder().append(name).append("_").append(UUID.randomUUID()).toString();
}
private void archiveCredential(Credential credential) {
credential.setName(generateArchiveName(credential.getName()));
credential.setArchived(true);
credential.setTopology(null);
credentialRepository.save(credential);
}
}