- package com.fosspowered.peist.service;
- import com.fosspowered.peist.model.dao.Paste;
- import com.fosspowered.peist.model.exceptions.PeistAccessDeniedException;
- import com.fosspowered.peist.model.exceptions.PeistInternalException;
- import com.fosspowered.peist.model.exceptions.PeistInvalidRequestException;
- import com.fosspowered.peist.model.exceptions.PeistNotFoundException;
- import com.fosspowered.peist.model.json.PasteRequest;
- import com.fosspowered.peist.model.json.PasteResponse;
- import com.fosspowered.peist.repository.PasteRepository;
- import java.nio.charset.StandardCharsets;
- import java.security.InvalidKeyException;
- import java.security.MessageDigest;
- import java.security.NoSuchAlgorithmException;
- import java.sql.Date;
- import java.util.ArrayList;
- import java.util.Base64;
- import java.util.Calendar;
- import java.util.List;
- import java.util.Optional;
- import java.util.UUID;
- import java.util.stream.Collectors;
- import javax.crypto.BadPaddingException;
- import javax.crypto.Cipher;
- import javax.crypto.IllegalBlockSizeException;
- import javax.crypto.NoSuchPaddingException;
- import javax.crypto.spec.SecretKeySpec;
- import lombok.extern.log4j.Log4j2;
- import org.apache.commons.lang3.StringUtils;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- @Service
- @Log4j2
- public class PasteService {
- private final PasteRepository pasteRepository;
- private final String systemKey;
- PasteService(PasteRepository pasteRepository, @Value("${system-key}") String systemKey) {
- this.pasteRepository = pasteRepository;
- this.systemKey = systemKey;
- }
- public PasteResponse addPaste(PasteRequest request) {
- Paste paste = buildPasteData(request);
- this.pasteRepository.save(paste);
- return buildPasteResponse(paste);
- }
- private String convertToCipher(Date creationDate, String key, String text) {
- SecretKeySpec keySpec = buildSpec(creationDate, key);
- try {
- Cipher cipher = Cipher.getInstance("AES");
- cipher.init(Cipher.ENCRYPT_MODE, keySpec);
- byte[] cipherBytes = cipher.doFinal(text.getBytes(StandardCharsets.UTF_8));
- return Base64.getEncoder().encodeToString(cipherBytes);
- } catch (InvalidKeyException
- | IllegalBlockSizeException
- | BadPaddingException
- | NoSuchAlgorithmException
- | NoSuchPaddingException e) {
- throw new RuntimeException(e);
- }
- }
- private String convertFromCipher(Date creationDate, String key, String cipherText) {
- SecretKeySpec keySpec = buildSpec(creationDate, key);
- try {
- Cipher cipher = Cipher.getInstance("AES");
- cipher.init(Cipher.DECRYPT_MODE, keySpec);
- return new String(cipher.doFinal(Base64.getDecoder().decode(cipherText)));
- } catch (BadPaddingException e) {
- throw new PeistAccessDeniedException("Access denied for paste.");
- } catch (NoSuchAlgorithmException
- | InvalidKeyException
- | NoSuchPaddingException
- | IllegalBlockSizeException e) {
- log.error("Error while processing", e);
- throw new PeistInternalException("Error while processing");
- }
- }
- private SecretKeySpec buildSpec(Date creationDate, String key) {
- String keyStr =
- String.format(
- "%s%s%s", systemKey, StringUtils.isNotBlank(key) ? key : "", creationDate.getTime());
- try {
- MessageDigest sha = MessageDigest.getInstance("SHA-256");
- return new SecretKeySpec(sha.digest(keyStr.getBytes(StandardCharsets.UTF_8)), "AES");
- } catch (NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- }
- }
- public PasteResponse fetchPaste(String urlId) {
- return fetchPaste(urlId, null);
- }
- public PasteResponse fetchPaste(String urlId, String key) {
- Paste paste = fetchPasteFromDB(urlId);
- String text = convertFromCipher(paste.getCreationDate(), key, paste.getPasteData());
- paste.setPasteData(text);
- return buildPasteResponse(paste);
- }
- private Paste fetchPasteFromDB(String urlId) {
- if (StringUtils.isBlank(urlId)) {
- throw new PeistInvalidRequestException("Invalid request as urlId is invalid: " + urlId);
- }
- Optional<Paste> paste = this.pasteRepository.findByUrlId(urlId);
- if (paste.isPresent()) {
- return paste.get();
- } else {
- throw new PeistNotFoundException("Paste not found for urlId: " + urlId);
- }
- }
- public List<PasteResponse> fetchRecentPastes() {
- List<Paste> pasteList = new ArrayList<>(this.pasteRepository.findAll());
- return pasteList.stream().map(this::buildPasteResponse).collect(Collectors.toList());
- }
- private PasteResponse buildPasteResponse(Paste paste) {
- return PasteResponse.builder()
- .urlId(paste.getUrlId())
- .creationDate(paste.getCreationDate())
- .author(paste.getAuthor())
- .language(paste.getLanguage())
- .title(paste.getTitle())
- .expiryDate(paste.getExpiryDate())
- .isVisible(paste.getIsVisible())
- .pasteData(paste.getPasteData())
- .build();
- }
- private Paste buildPasteData(PasteRequest pasteRequest) {
- Calendar now = Calendar.getInstance();
- Date creationDate = new Date(now.getTime().getTime());
- now.add(Calendar.SECOND, pasteRequest.getTtl());
- Date expiryDate = new Date(now.getTime().getTime());
- String secretKey = pasteRequest.getSecretKey();
- boolean isVisible = StringUtils.isNotBlank(pasteRequest.getSecretKey());
- String text = convertToCipher(creationDate, secretKey, pasteRequest.getPasteData());
- Paste paste = new Paste();
- paste.setUrlId(UUID.randomUUID().toString());
- paste.setCreationDate(creationDate);
- paste.setTitle(pasteRequest.getTitle());
- paste.setAuthor(pasteRequest.getAuthor());
- paste.setLanguage(pasteRequest.getLanguage());
- paste.setExpiryDate(expiryDate);
- paste.setIsVisible(isVisible);
- paste.setPasteData(text);
- return paste;
- }
- }