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;
}
}
g+uHux2iOP+3m8MaCzwojScAZ95HqJd4lNmE/3x7UtFeq+RzD/MTvlEdTZZGdAUeJBAB0QK0IaR2J0hlvgSSzn651/MvdTBDHyu/
UvG/kXWwToN3dQvKy59E8fc4DcLh7UCKtxI1RjPrzxxCzQPwIPxhSJ5v3PBC2qAjEFlhSuGTnAw31wv7Zhc1C2+1VpU8LL0E2+PU
CWEjSVuDU6LGL+qIaoMDW4H6iu79anIZiMpYjh29o6THYD159+W+Soqq987uWlPehRFPg9o4/qNUfNfG64sIUMe9Zstc8mgr+8Ip
bhJ+NvALfN/bMfChkrFnC1MitNjE3/80QmSywpPTq1NcJFutM0V/8izifZ6w0Dg7Kv+GOTIbe4mvPZTCMNeRhhvcfA/eJc8iXS3N
QM39h532d6XrU03+2UtA/9ioIjwTR2UWAxPehov9ML63HNWWusOD3R3GLt6wm1SHam9a4Y/dHWJP3WLICuEM0FcC+oM+L2Gwqqi7
mrVk58nUKPC7dk8v17en6UvYyvK0kmiU2UuwEgnYKWN5Z2di7UE0x09v/aW7sjRc0NVciaFbjJJTh2NrRvPw+STDYSjySF228nrw
7PBvGfc+lWIkCb0S7JzvFgDREpofZBHB2HcMHZlgFmUYkhahOI39WjMdRX/A4BPeQcE/S9iWvKgXD1TuECdZG4H8D0+lE+qwgMuj
bog2GfbHGDgWD4tzGy3NIE7d8N8YlHfbBglZszt2CSakeV+z7lGqbNnURecrB36FBGtJ2aDkoHrZfY48DhQIYq4bFiTv7Q/dTA8h
HBufwArANT6a+y6S4WIN83A+QGpv0cnAT7y9pYQHlUV/h574uAlOs2i8l8E+iLdylwDnoeYFp7onn3H2yoQ8GRzeOEugEgIsY8ho
/YvjeoTvqohLJS7uUV3Tho6CTaHa4kVBSrS2w8vY9WSOblGmx6rMjYg0UNPjH9sCCRYQ952IzTJt2+ojF4W2eUy2VRil4nelCR7G
I0s4wb51hgB2aolK0nL5YjcMXZFnYs0ubcpqgLTYVgBPyr9CDm9KDpPp99TGiYCblW8sW3m+B0RtTEYEk7JNHS2gngJrD8c74B/n
i+ulrcuenIJxbRlIwldJepkPbUSgGzAuUo3Z5+tNnhDd6OmzEUu4hc6mRzG1MYUguCF1qjcLSFWAoZ+0bl9JY7OTjlDXtQwAdm/J
my4c7JxVvcX//x4UzrTFFgkC1UtIPTsNSnSrV59NIH+FvYbamwtFKpwAdS6TCye1XzS6E9vbb8nWtxXavvCwiwkxj8jM0FzBlcbT
JZcMKldBDMs7niKPBYzn7fhaZV1mQupmTrn7/Rhst7lgi0qUSGrNujeM8D8utrrerCBgElC3BVSPg6foMFcPPoba4rSnB5dID0C+
GRfhrT+bDKmpau7XAL2rvORbretgSXkLRq27N7HuKCWZc6FdIdG49/9yJ3DlQhHCy+p8KcfxzQxeSUhQGFcmjTIHIDzod8WSPViR
bcxjyS1LCXDg2d93KvEKaP/iFzIuiyt6650LrO/E2EF/eFB4dBTMtRP2vPbHKjGfonlI8HTgK/gQ/+OfcdvBDhbde7nrJGIjNxbU
nZEbR66Zqs2UDy21WeTe0kaMAH0LDdzO91bXa9JaNLymdVvZWOst9e7RU3tXMzSQERn3BhrklS4pGwultZ3GHk3aQ+p2fcLNzdXR
K9jMOvIk/88D9p4g67opi9/0hPbk3NgAc4tF9AZh1WQVwSQTAK7PVrpId+4FHEmiUd0rMXOCZTnLIs+GVLzRYmP+q/m7BWc/bIIE
NgAqzJ3txehOTp0fBuUwqXXkN5YgY054GKvll1jJ0v3aAAD2Lp8I1mYrzzkS9rUWKelsgPC8DK5g03yDHiq8QJw4Yzv+NAego6mZ
4O9JjMPWgIrKVx0pzmdD7jvhCUw0hPrGoJjjoQJz39uP4Y6MqishsR8wiCDd9XarWfEng3hlZB16cQ4UpmFCo4/vCc0JCfz4Mzzs
HNkFTco7LxhOX0hj5kjSLwK/os17m5irQ7/jyp3dkopxyYwUmBScazHipuFOZW/wHg/sGk+7SD78M/cFOLRviSD3Y2ELJmzAbZtw
IjJa/vt72U4L57cBvryX6bGr8H40s7tfZiNjRKd5QvJTPJrHMDpKe8dHTfyyVz88Doi5lX08qR3HiblwFD3FM+kQ1WVQqRMxf9dN
FSDxplY4K1kahqaAyEzSjxXW8KYjELxWa72vVPHFjci74ChKIIK/GORc7C0230gxX4ajg3bdZHsJQqW+lZxH+NsGACZxDPTFVs+p
410ENOwHzBpO6drZx5jV2NOWjj/aasACdTSidfq2XAFG8zIq9Tg2uHRGqIm1Ab1hpmd0GyghAmuZ64tNcVG9zw2AAYqGY2A7bYWt
TqI8fyR6C/g/YkyqtZ+El+fmfnxdcn46gTULVv2UXAxnQwoGfksNG04KEwWm2WNIKa6JLddGqXwoFGPpYQTKyV4ze5XEDQWiVvA1
UlvdGZrUu5KfrL0Jrm2nDpcAj3Qg/K9AoHxKiOY/y90kbsh5iWPJswKQAtkvCtHmaphyj/fjJ5mIV8lSZuuIOCbtdINphk2usw/s
+85TDFY3v2nxi+PHPRtz58F1Hp7t87syDV2Zkm1qWmvmFSHmNW0qWyjcrEkb47R5v7BDCHlaCYyHPYlzcPZAiX2rtj0HrGI6JN8N
DreijNGp0dnVPAMSJzOJ2ZQnJ6+e1byqyVsie/WFEXV0MFTe9qh/ztgcuEmX7KJHPOpGXSKRJ6z2CejQv/tdStYqO9KxsaowBmep
LnECZ5XWTlCrsE9SR40Y54Tiyjog+eNj95ij1ghIb8RtTUNA8knObLI1bBE+VBA7xxcBgLbA3zyVrBX6Zmxz4DrwXWD2ueyEkVZZ
u40emkdcnQOTgc4reC7Et94sNUYGzRn9HgGRqnkKnfqe5jTX6NGe7/yd/lZEBJlGpzI0xTTE3E0d0nz5tM5mBc1u5BDHksHs5B4F
PfOQtBD/Eh7wQ5sQD0VvnbJk4/olfQ+KI35SeU+3wrnRM9CAkqSimsaXWz5AonCJ0QABea+kD0z/Ab3NlW1XMZroTGYFNOEzykFS
5X88u74foe8XBYsOJvjhuPVp3bnJCeSsWQ013oD9BUiLhRmF4nHEKSq7O4WyV6/X9EbU/8XBdmlx5nZhzTI3aV/XtR4pNDA2+p90
AzKv5V6M/+3nrMao+VyjOicNySSRKXlBAFDhqf/+EENQjVryHQ+KSDCTkwr2cu2fjAc4HDd0zI/fDPx1mvBB4rssGxXtqg8irHuD
SVegImlyq8bB2zW11xfK/EqP+TW6V5PE2M1IwBEBizL4PGRJsNORVp5jQlt4iFX063QAxcldQ4M918gVNUZuGVOhOy+rR0I3MuEX
JsbSRbgMm32nIIHiX6c6uYnTOfDQX7Gi54RvMbzsl4WGGyIKvx9WNK4LH/Ox7nCvYqYW0dQpqkrzl/Jw7JFGlTZAHk5J5gIKrQIe
kXIBrV7GYkMyc042NmiyiD4ga5Y5SokkTT8Q5S1TiiKlp5J7qnYfhemkkz1/pTygx+aPv/UvbV/Vua1tFC6Uan0nTDO1+sAGYLS0
6lvXL8gg+8kD58hoq996MjaLTYmkEMzb3bT5Zue7K5pUoNl3eY48S03k2MALQ019BQuVZanumclulRmzh0Ezji+1fJRFu2tzLTL3
Akg7V18gBeoeKkfT1mSZ7C+3fCZdGxy8VroxeH1MCL4avEP7quk8yze6Nel6jdM9wO/ZZsG8GwJ8PSRQ6NxR3pwARxkDzYPwuGPX
H59kxtH4+kXmhS1V7STHQa/G7FI2NsjU6H8PX6SHlwl1YAW/rPS+Jl3+OtvRUxGCbfn4nLHopRVZvySkFpatXol/W0b0UcVg5l5u
n4+c3rPH+8j7kXZfj/isCzDI2T75O2jL0KsLcRZzvfKqg4XTUuyAu2HvCXPlWa+2cavUIP87TCUnZb4iyfoJrfGTJ+rnukob8ukf
/vXzJ9A0ApWpX5vvUnbBxy5rPB2OKG5X22bgTSPYS/8XIR+XArNWzYBD89sNpwS8QdF+qeTcoiun9N7513uYahfwJNGk0krgo5ZB
+k3cjOkgWD8wBkkTNW+b8oQKxQzkhXnIVucevznFjQ6J/IcKIZryA+dYc8meqdf/4qmnSukE+tBMkFR7GScarncD4NagZEe5qTqT
lp9nlA1KM7AJpmcWp7uopO2GBMPGl8x8au64SycL7+sTCKE/l9Sr6otqlUyoRFhfXORyD8eyDOMjT8raL6nT7T3vkscENvpO2Atf
De39gcWYlAjbc3rGRTGQl4/nlhsu4eQspaR42rlzmMlxFlbfuSIi7CG6crxbSuzMkk3DQUQ3gllTL1AJNIwL4qN4zGJrKJIu2F/4
Ki2iOUDpR55mYES0SATIIVNqVj7NOLEi/cVlJnpUQg/LHL5GgBeZ7rgrn0ZyS7fdKSxofNXfPUio3NkGQFPhYJtVijkDqGanumIw
1uw6IBfYl8+uRETpJr6QxobRrvi4NUgcM5oD+OmnNVsozZnGmzQOhOQ7nf1R35Jgg22dGcTDNZ6Ltx5TPp5E6FKzMQMXueznNwPO
CfU+iLcWf99rXHsk6JrV0DnBUiAteOQU8hILaeodrx5oVIjq3DrJNk16q6y9M5dXMxE2N6HDJ7EA732t64rKJB73+dPF0nyZ9trX
cqHT2bEKR+Khhe37DfqK5sid36+IWt3Lk8ySEFSut/6y8fdsS6yq8oiyKgvs0FzyGWinxGZvTJVH4eihTeQR5WmjwJXXQEjCKh3I
34F7ssjEq6WNV/xMwGUVlGb7LeqjUpoUT+7anxlxMYCcxMgLWExP6fnayHJ5Id9wZMzVl3Q=