commit 2d00900f731f57723bf7418ffb484eed46bafb68
parent a0673eaa944125a46fb9d741d51389b54e1ab254
Author: Wim Dupont <wim@wimdupont.com>
Date: Sat, 23 Dec 2023 21:10:30 +0100
properties as singleton
Diffstat:
5 files changed, 89 insertions(+), 45 deletions(-)
diff --git a/pom.xml b/pom.xml
@@ -15,7 +15,7 @@
<bcpg-jdk18on.version>1.77</bcpg-jdk18on.version>
<otp-java.version>2.0.3</otp-java.version>
<junit-jupiter-engine.version>5.10.1</junit-jupiter-engine.version>
- <mockito-core.version>5.8.0</mockito-core.version>
+ <mockito.version>5.8.0</mockito.version>
<maven-surefire-plugin.version>3.2.3</maven-surefire-plugin.version>
</properties>
@@ -44,7 +44,13 @@
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
- <version>${mockito-core.version}</version>
+ <version>${mockito.version}</version>
+ <scope>test</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-junit-jupiter</artifactId>
+ <version>${mockito.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
diff --git a/src/main/java/com/wimdupont/Main.java b/src/main/java/com/wimdupont/Main.java
@@ -1,16 +1,15 @@
package com.wimdupont;
import com.bastiaanjansen.otp.TOTPGenerator;
+import com.wimdupont.service.ApplicationProperties;
import com.wimdupont.service.GpgService;
-import com.wimdupont.service.PropertiesLoader;
public class Main {
public static void main(String[] args) {
try {
- var gpgService = new GpgService(new PropertiesLoader().loadProperties());
System.out.println(TOTPGenerator
- .withDefaultValues(gpgService
+ .withDefaultValues(new GpgService(ApplicationProperties.getInstance())
.decrypt(getFileArg(args)))
.now());
} catch (Exception e) {
diff --git a/src/main/java/com/wimdupont/service/ApplicationProperties.java b/src/main/java/com/wimdupont/service/ApplicationProperties.java
@@ -5,9 +5,9 @@ import java.security.InvalidParameterException;
import java.util.MissingResourceException;
import java.util.Properties;
-public class PropertiesLoader {
+public class ApplicationProperties {
- private static PropertiesLoader instance;
+ private static ApplicationProperties instance;
private final String dirPath;
private final String secretFile;
@@ -19,15 +19,15 @@ public class PropertiesLoader {
return secretFile;
}
- public static PropertiesLoader getInstance() {
+ public static ApplicationProperties getInstance() {
if (instance == null) {
- instance = new PropertiesLoader();
+ instance = new ApplicationProperties();
}
return instance;
}
- private PropertiesLoader(){
+ private ApplicationProperties(){
var properties = loadProperties();
dirPath = properties.getProperty("dir.path");
secretFile = properties.getProperty("secret.file");
diff --git a/src/main/java/com/wimdupont/service/GpgService.java b/src/main/java/com/wimdupont/service/GpgService.java
@@ -23,23 +23,15 @@ import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
-import java.security.InvalidParameterException;
import java.security.Security;
import java.util.Iterator;
-import java.util.Properties;
public class GpgService {
- private final String dirPath;
- private final String secretFile;
+ private final ApplicationProperties applicationProperties;
- public GpgService(Properties properties) {
- dirPath = properties.getProperty("dir.path");
- secretFile = properties.getProperty("secret.file");
-
- if (dirPath == null | secretFile == null) {
- throw new InvalidParameterException("Properties missing.");
- }
+ public GpgService(ApplicationProperties applicationProperties){
+ this.applicationProperties = applicationProperties;
}
public byte[] decrypt(String fileName) throws IOException, PGPException {
@@ -58,7 +50,8 @@ public class GpgService {
}
private Iterator<PGPEncryptedData> getPgpEncryptedDataIterator(String fileName) throws IOException {
- var secretDecoderStream = PGPUtil.getDecoderStream(KeyRetriever.getSecretKeyInputStream(fileName, dirPath));
+ var secretDecoderStream = PGPUtil.getDecoderStream(KeyRetriever
+ .getSecretKeyInputStream(fileName, applicationProperties.getDirPath()));
var pgpObjectFactory = new PGPObjectFactory(secretDecoderStream, new BcKeyFingerprintCalculator());
var nextObject = pgpObjectFactory.nextObject();
@@ -68,7 +61,7 @@ public class GpgService {
}
private PGPPrivateKey retrievePgpPrivateKey(PGPPublicKeyEncryptedData pgpPublicKeyEncryptedData) throws IOException, PGPException {
- var pgpPrivateKey = readSecretKey(new FileInputStream(secretFile), pgpPublicKeyEncryptedData.getKeyID())
+ var pgpPrivateKey = readSecretKey(new FileInputStream(applicationProperties.getSecretFile()), pgpPublicKeyEncryptedData.getKeyID())
.extractPrivateKey(new BcPBESecretKeyDecryptorBuilder(new BcPGPDigestCalculatorProvider())
.build(PasswordReader.getPassword()));
diff --git a/src/test/java/com/wimdupont/service/GpgServiceTest.java b/src/test/java/com/wimdupont/service/GpgServiceTest.java
@@ -3,73 +3,119 @@ package com.wimdupont.service;
import org.bouncycastle.openpgp.PGPException;
import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.mockito.Mock;
import org.mockito.MockedStatic;
+import org.mockito.Mockito;
+import org.mockito.junit.jupiter.MockitoExtension;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Paths;
-import java.util.Properties;
import static org.mockito.Mockito.mockStatic;
+@ExtendWith(MockitoExtension.class)
public class GpgServiceTest {
+ private static final String TOTP_SECRET_FILE = "encrypted-totp-secret";
private static final char[] PASSWORD = {'s', 'e', 'c', 'r', 'e', 't', 'p', 'a', 's', 's', 'w', 'o', 'r', 'd'};
+ private static String dirPath;
+ private static String secretFile;
+
private GpgService gpgService;
+ @Mock
+ private ApplicationProperties applicationProperties;
- @BeforeEach
- public void setup() {
+ @BeforeAll
+ public static void setupClass() {
var resourceDirectory = Paths.get("src", "test", "resources")
.toAbsolutePath()
.toString();
- var properties = new Properties();
-
- properties.setProperty("dir.path", String.format("%s/totpfiles/", resourceDirectory));
- properties.setProperty("secret.file", String.format("%s/private-key.asc", resourceDirectory));
+ dirPath = String.format("%s/totpfiles/", resourceDirectory);
+ secretFile = String.format("%s/private-key.asc", resourceDirectory);
+ }
- gpgService = new GpgService(properties);
+ @BeforeEach
+ public void setup() {
+ gpgService = new GpgService(applicationProperties);
}
@Test
public void decryptWithCorrectPasswordShouldReturnCorrectSecret() throws PGPException, IOException {
- try (MockedStatic<PasswordReader> mocked = mockStatic(PasswordReader.class)) {
- mocked.when(PasswordReader::getPassword).thenReturn(PASSWORD);
+ Mockito.when(applicationProperties.getDirPath()).thenReturn(dirPath);
+ Mockito.when(applicationProperties.getSecretFile()).thenReturn(secretFile);
+ try (MockedStatic<PasswordReader> mockedPasswordReader = mockStatic(PasswordReader.class)) {
+ mockedPasswordReader.when(PasswordReader::getPassword).thenReturn(PASSWORD);
- var result = gpgService.decrypt("encrypted-totp-secret");
+ var result = gpgService.decrypt(TOTP_SECRET_FILE);
Assertions.assertNotNull(result);
- Assertions.assertEquals("This is the big secret key",
- new String(result, Charset.defaultCharset()));
+ Assertions.assertEquals("This is the big secret key", new String(result, Charset.defaultCharset()));
}
}
@Test
public void decryptWithWrongPasswordShouldThrowPgpException() {
- try (MockedStatic<PasswordReader> mocked = mockStatic(PasswordReader.class)) {
- mocked.when(PasswordReader::getPassword).thenReturn(new char[]{'w', 'r', 'o', 'n', 'g'});
+ Mockito.when(applicationProperties.getDirPath()).thenReturn(dirPath);
+ Mockito.when(applicationProperties.getSecretFile()).thenReturn(secretFile);
+ try (MockedStatic<PasswordReader> mockedPasswordReader = mockStatic(PasswordReader.class)) {
+ mockedPasswordReader.when(PasswordReader::getPassword).thenReturn(new char[]{'w', 'r', 'o', 'n', 'g'});
Exception exception = Assertions.assertThrows(PGPException.class, () ->
- gpgService.decrypt("encrypted-totp-secret")
+ gpgService.decrypt(TOTP_SECRET_FILE)
);
- Assertions.assertEquals(exception.getClass(), PGPException.class);
+ Assertions.assertEquals(PGPException.class, exception.getClass());
}
}
@Test
- public void decryptUnknownFileShouldThrowException() {
- try (MockedStatic<PasswordReader> mocked = mockStatic(PasswordReader.class)) {
- mocked.when(PasswordReader::getPassword).thenReturn(PASSWORD);
+ public void decryptUnknownFileNameArgShouldThrowException() {
+ Mockito.when(applicationProperties.getDirPath()).thenReturn(dirPath);
+ try (MockedStatic<PasswordReader> mockedPasswordReader = mockStatic(PasswordReader.class)) {
+ mockedPasswordReader.when(PasswordReader::getPassword).thenReturn(PASSWORD);
Exception exception = Assertions.assertThrows(FileNotFoundException.class, () ->
gpgService.decrypt("unexisting")
);
- Assertions.assertEquals(exception.getClass(), FileNotFoundException.class);
- Assertions.assertEquals(exception.getMessage(), "File \"unexisting.gpg\" not found");
+ Assertions.assertEquals(FileNotFoundException.class, exception.getClass());
+ Assertions.assertEquals("File \"unexisting.gpg\" not found", exception.getMessage());
+ }
+ }
+
+ @Test
+ public void decryptUnknownFileFromDirPathShouldThrowException() {
+ Mockito.when(applicationProperties.getDirPath()).thenReturn("/wrong/path/");
+ try (MockedStatic<PasswordReader> mockedPasswordReader = mockStatic(PasswordReader.class)) {
+ mockedPasswordReader.when(PasswordReader::getPassword).thenReturn(PASSWORD);
+
+ Exception exception = Assertions.assertThrows(FileNotFoundException.class, () ->
+ gpgService.decrypt(TOTP_SECRET_FILE)
+ );
+
+ Assertions.assertEquals(FileNotFoundException.class, exception.getClass());
+ Assertions.assertEquals("File \"encrypted-totp-secret.gpg\" not found", exception.getMessage());
+ }
+ }
+
+ @Test
+ public void decryptUnknownFileFromSecretFileShouldThrowException() {
+ Mockito.when(applicationProperties.getDirPath()).thenReturn(dirPath);
+ Mockito.when(applicationProperties.getSecretFile()).thenReturn("/wrong/unexisting.asc");
+ try (MockedStatic<PasswordReader> mockedPasswordReader = mockStatic(PasswordReader.class)) {
+ mockedPasswordReader.when(PasswordReader::getPassword).thenReturn(PASSWORD);
+
+ Exception exception = Assertions.assertThrows(FileNotFoundException.class, () ->
+ gpgService.decrypt(TOTP_SECRET_FILE)
+ );
+
+ Assertions.assertEquals(FileNotFoundException.class, exception.getClass());
}
}
}
\ No newline at end of file