Skip to content

Commit

Permalink
Add tests
Browse files Browse the repository at this point in the history
  • Loading branch information
mkay1375 committed Aug 2, 2024
1 parent 4282cc9 commit 8f8ed0f
Show file tree
Hide file tree
Showing 13 changed files with 621 additions and 106 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@
<version>${junit-jupiter.version}</version>
<scope>test</scope>
</dependency>
<dependency> <!-- Used to mock environment variables -->
<groupId>uk.org.webcompere</groupId>
<artifactId>system-stubs-jupiter</artifactId>
<version>2.1.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/io/github/tap30/hiss/Encrypted.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
/**
* Fields annotated using this will be encrypted.
*/
// todo: add example
// Todo: add example
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Encrypted {
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/io/github/tap30/hiss/EncryptedInside.java
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

// todo: improve doc
// Todo: improve doc
/**
* Fields annotated with this will be scanned
*/
// todo: add example
// Todo: add example
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface EncryptedInside {
Expand Down
49 changes: 29 additions & 20 deletions src/main/java/io/github/tap30/hiss/Hiss.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.intellij.lang.annotations.Language;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;
import java.util.logging.Level;
import java.util.logging.Logger;

Expand All @@ -17,21 +18,13 @@ public class Hiss {
private final HissObjectEncryptor hissObjectEncryptor;

Hiss(HissProperties hissProperties, KeyHashGenerator keyHashGenerator) {
Objects.requireNonNull(hissProperties);
Objects.requireNonNull(keyHashGenerator);

this.hissEncryptor = new HissEncryptor(hissProperties);
this.hissObjectEncryptor = new HissObjectEncryptor(this.hissEncryptor);
logger.log(Level.INFO, "Hiss initialized:\n" +
" Loaded Keys: {0}\n" +
" Default Encryption Key ID: {1}\n" +
" Default Encryption Algorithm: {2}\n" +
" Default Hashing Key ID: {3}\n" +
" Default Hashing Algorithm: {4}",
new Object[]{
hissProperties.getKeys().keySet(),
hissProperties.getDefaultEncryptionKeyId(),
hissProperties.getDefaultEncryptionAlgorithm(),
hissProperties.getDefaultHashingKeyId(),
hissProperties.getDefaultHashingAlgorithm()
});

logHissInitialized(hissProperties);
if (hissProperties.isKeyHashGenerationEnabled()) {
keyHashGenerator.generateAndLogHashes(hissProperties.getKeys().values());
}
Expand All @@ -44,7 +37,7 @@ public class Hiss {
* @return encrypted content or null if the content is null.
*/
public String encrypt(@Nullable String content) {
return this.encrypt(content, "");
return encrypt(content, "");
}

/**
Expand All @@ -56,7 +49,7 @@ public String encrypt(@Nullable String content) {
* @return encrypted content or null if the content is null.
*/
public String encrypt(@Nullable String content, @Language("regexp") @Nullable String pattern) {
return this.hissEncryptor.encrypt(content, pattern);
return hissEncryptor.encrypt(content, pattern);
}

/**
Expand All @@ -69,7 +62,7 @@ public String encrypt(@Nullable String content, @Language("regexp") @Nullable St
* @throws IllegalArgumentException if key ID or algorithm is not loaded/supported.
*/
public String decrypt(@Nullable String content) {
return this.hissEncryptor.decrypt(content);
return hissEncryptor.decrypt(content);
}

/**
Expand All @@ -79,7 +72,7 @@ public String decrypt(@Nullable String content) {
* @return hashed content or null if the content is null.
*/
public String hash(@Nullable String content) {
return this.hash(content, "");
return hash(content, "");
}

/**
Expand All @@ -91,7 +84,7 @@ public String hash(@Nullable String content) {
* @return hashed content or null if the content is null.
*/
public String hash(@Nullable String content, @Language("regexp") @Nullable String pattern) {
return this.hissEncryptor.hash(content, pattern);
return hissEncryptor.hash(content, pattern);
}

/**
Expand Down Expand Up @@ -120,7 +113,7 @@ public boolean isHashed(@Nullable String content) {
* @param object the annotated object; no action is taken on null objects.
*/
public void encryptObject(@Nullable Object object) {
this.hissObjectEncryptor.encryptObject(object);
hissObjectEncryptor.encryptObject(object);
}

/**
Expand All @@ -129,7 +122,23 @@ public void encryptObject(@Nullable Object object) {
* @param object the annotated object; no action is taken on null objects.
*/
public void decryptObject(@Nullable Object object) {
this.hissObjectEncryptor.decryptObject(object);
hissObjectEncryptor.decryptObject(object);
}

private void logHissInitialized(HissProperties hissProperties) {
logger.log(Level.INFO, "Hiss initialized:\n" +
" Loaded Keys: {0}\n" +
" Default Encryption Key ID: {1}\n" +
" Default Encryption Algorithm: {2}\n" +
" Default Hashing Key ID: {3}\n" +
" Default Hashing Algorithm: {4}",
new Object[]{
hissProperties.getKeys().keySet(),
hissProperties.getDefaultEncryptionKeyId(),
hissProperties.getDefaultEncryptionAlgorithm(),
hissProperties.getDefaultHashingKeyId(),
hissProperties.getDefaultHashingAlgorithm()
});
}

}
22 changes: 11 additions & 11 deletions src/main/java/io/github/tap30/hiss/properties/HissProperties.java
Original file line number Diff line number Diff line change
@@ -1,26 +1,21 @@
package io.github.tap30.hiss.properties;

import io.github.tap30.hiss.key.Key;
import io.github.tap30.hiss.utils.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;

public abstract class HissProperties {

private final Map<String, Object> properties = new HashMap<>();

public Map<String, Key> getKeys() {
return this.getProperty("Keys", () -> {
var keys = loadKeys();
keys.forEach((k, v) -> {
if (v != null && !StringUtils.hasText(v.getId())) {
throw new RuntimeException("Key ID must not be empty");
}
});
return keys;
});
return this.getProperty("Keys", () -> loadKeys()
.stream()
.collect(Collectors.toMap(Key::getId, k -> k)));
}

public String getDefaultEncryptionKeyId() {
Expand All @@ -43,11 +38,16 @@ public boolean isKeyHashGenerationEnabled() {
return this.getProperty("KeyHashGenerationEnabled", this::loadKeyHashGenerationEnabled);
}

protected abstract Map<String, Key> loadKeys();
protected abstract Set<Key> loadKeys();

protected abstract String loadDefaultEncryptionKeyId();

protected abstract String loadDefaultEncryptionAlgorithm();

protected abstract String loadDefaultHashingKeyId();

protected abstract String loadDefaultHashingAlgorithm();

protected abstract boolean loadKeyHashGenerationEnabled();

@SuppressWarnings("unchecked")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package io.github.tap30.hiss.properties;

import io.github.tap30.hiss.key.Key;
import lombok.Setter;

import java.util.Base64;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;

/**
Expand All @@ -30,19 +30,17 @@ public class HissPropertiesFromEnv extends HissProperties {
private static final String KEY_ENV_PREFIX = "HISS_KEYS_";
private static final String KEY_HASH_ENV_POSTFIX = "___HASH";

@Setter // todo: make it package private
private Supplier<Map<String, String>> envProvider = System::getenv;
private static final Supplier<Map<String, String>> ENV_PROVIDER = System::getenv;

@Override
public Map<String, Key> loadKeys() {
var keys = new HashMap<String, Key>();
envProvider.get().forEach((k, v) -> {
public Set<Key> loadKeys() {
var keys = new HashSet<Key>();
ENV_PROVIDER.get().forEach((k, v) -> {
if (k.startsWith(KEY_ENV_PREFIX) && !k.endsWith(KEY_HASH_ENV_POSTFIX)) {
var id = k.replace(KEY_ENV_PREFIX, "").toLowerCase();
keys.put(id, Key.builder()
.id(id)
keys.add(Key.builder()
.id(k.replace(KEY_ENV_PREFIX, "").toLowerCase())
.key(Base64.getDecoder().decode(v))
.keyHash(envProvider.get().get(k + KEY_HASH_ENV_POSTFIX))
.keyHash(ENV_PROVIDER.get().get(k + KEY_HASH_ENV_POSTFIX))
.build());
}
});
Expand All @@ -51,26 +49,26 @@ public Map<String, Key> loadKeys() {

@Override
public String loadDefaultEncryptionKeyId() {
return envProvider.get().get("HISS_DEFAULT_ENCRYPTION_KEY_ID");
return ENV_PROVIDER.get().get("HISS_DEFAULT_ENCRYPTION_KEY_ID");
}

@Override
public String loadDefaultEncryptionAlgorithm() {
return envProvider.get().get("HISS_DEFAULT_ENCRYPTION_ALGORITHM");
return ENV_PROVIDER.get().get("HISS_DEFAULT_ENCRYPTION_ALGORITHM");
}

@Override
public String loadDefaultHashingKeyId() {
return envProvider.get().get("HISS_DEFAULT_HASHING_KEY_ID");
return ENV_PROVIDER.get().get("HISS_DEFAULT_HASHING_KEY_ID");
}

@Override
public String loadDefaultHashingAlgorithm() {
return envProvider.get().get("HISS_DEFAULT_HASHING_ALGORITHM");
return ENV_PROVIDER.get().get("HISS_DEFAULT_HASHING_ALGORITHM");
}

@Override
protected boolean loadKeyHashGenerationEnabled() {
return Boolean.parseBoolean(envProvider.get().get("HISS_KEY_HASH_GENERATION_ENABLED"));
return Boolean.parseBoolean(ENV_PROVIDER.get().get("HISS_KEY_HASH_GENERATION_ENABLED"));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,39 @@
import io.github.tap30.hiss.utils.StringUtils;

import java.util.ArrayList;
import java.util.Objects;
import java.util.Set;

public class HissPropertiesValidator {

private static final Set<String> SUPPORTED_ENCRYPTION_ALGORITHMS
= Set.of("aes-128-gcm", "aes-128-cbc"); // Todo: get these from algorithm spec
private static final Set<String> SUPPORTED_HASHING_ALGORITHMS
= Set.of("hmac-sha256"); // Todo: get this from algorithm spec

private final KeyHashGenerator keyHashGenerator;

public HissPropertiesValidator(KeyHashGenerator keyHashGenerator) {
Objects.requireNonNull(keyHashGenerator);
this.keyHashGenerator = keyHashGenerator;
}

public void validate(HissProperties hissProperties) {
var errors = new ArrayList<String>();
validateKeys(hissProperties, errors);
validateDefaultEncryptionKeyAndAlgorithm(hissProperties, errors);
validateDefaultHashingKeyAndAlgorithm(hissProperties, errors);
if (!errors.isEmpty()) {
throw new IllegalArgumentException("Hiss properties are not valid: " + String.join("; ", errors));
}
}

private void validateKeys(HissProperties hissProperties, ArrayList<String> errors) {
if (hissProperties.getKeys() == null || hissProperties.getKeys().isEmpty()) {
errors.add("Keys are empty");
} else {
hissProperties.getKeys().forEach((k, v) -> {
if (v == null || v.getKey() == null || v.getKey().length == 0) {
if (k == null || v == null || v.getKey() == null || v.getKey().length == 0) {
errors.add("Key " + k + " is empty");
}
});
Expand All @@ -28,20 +45,33 @@ public void validate(HissProperties hissProperties) {
errors.add("Key(s) " + mismatches + " did not match with their hashes");
}
}
}

private static void validateDefaultEncryptionKeyAndAlgorithm(HissProperties hissProperties, ArrayList<String> errors) {
if (!StringUtils.hasText(hissProperties.getDefaultEncryptionKeyId())) {
errors.add("Default encryption key ID is not defined");
}
if (!hissProperties.getKeys().containsKey(hissProperties.getDefaultEncryptionKeyId())) {
errors.add("Default encryption key ID is not among provided keys: " + hissProperties.getKeys().keySet());
}
if (!StringUtils.hasText(hissProperties.getDefaultEncryptionAlgorithm())) {
errors.add("Default encryption algorithm is not defined");
} else if (!SUPPORTED_ENCRYPTION_ALGORITHMS.contains(hissProperties.getDefaultEncryptionAlgorithm())) {
errors.add("Encryption algorithm " + hissProperties.getDefaultEncryptionAlgorithm() + " is not supported");
}
}

private static void validateDefaultHashingKeyAndAlgorithm(HissProperties hissProperties, ArrayList<String> errors) {
if (!StringUtils.hasText(hissProperties.getDefaultHashingKeyId())) {
errors.add("Default hashing key ID is not defined");
}
if (!hissProperties.getKeys().containsKey(hissProperties.getDefaultHashingKeyId())) {
errors.add("Default hashing key ID is not among provided keys: " + hissProperties.getKeys().keySet());
}
if (!StringUtils.hasText(hissProperties.getDefaultHashingAlgorithm())) {
errors.add("Default hashing algorithm is not defined");
}
if (!errors.isEmpty()) {
throw new IllegalArgumentException("Hiss properties are not valid: " + String.join("; ", errors));
} else if (!SUPPORTED_HASHING_ALGORITHMS.contains(hissProperties.getDefaultHashingAlgorithm())) {
errors.add("Hashing algorithm " + hissProperties.getDefaultHashingAlgorithm() + " is not supported");
}
}

Expand Down
27 changes: 16 additions & 11 deletions src/test/java/io/github/tap30/hiss/BaseHissTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,29 @@

import io.github.tap30.hiss.properties.HissPropertiesFromEnv;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.extension.ExtendWith;
import uk.org.webcompere.systemstubs.environment.EnvironmentVariables;
import uk.org.webcompere.systemstubs.jupiter.SystemStub;
import uk.org.webcompere.systemstubs.jupiter.SystemStubsExtension;

import java.util.Map;

@ExtendWith(SystemStubsExtension.class)
public class BaseHissTest {

@SystemStub
private EnvironmentVariables environment = new EnvironmentVariables(
"HISS_DEFAULT_ENCRYPTION_KEY_ID", "default_key",
"HISS_DEFAULT_ENCRYPTION_ALGORITHM", "aes-128-gcm",
"HISS_DEFAULT_HASHING_KEY_ID", "default_key",
"HISS_DEFAULT_HASHING_ALGORITHM", "hmac-sha256",
"HISS_KEYS_DEFAULT_KEY", "AAAAAAAAAAAAAAAAAAAAAA==",
"HISS_KEY_HASH_GENERATION_ENABLED", "false"
);

protected Hiss hiss;

@BeforeEach
void setUpHiss() {
var hissPropertiesFromEnv = new HissPropertiesFromEnv();
hissPropertiesFromEnv.setEnvProvider(() -> Map.of(
"HISS_DEFAULT_ENCRYPTION_KEY_ID", "default_key",
"HISS_DEFAULT_ENCRYPTION_ALGORITHM", "aes-128-gcm",
"HISS_DEFAULT_HASHING_KEY_ID", "default_key",
"HISS_DEFAULT_HASHING_ALGORITHM", "hmac-sha256",
"HISS_KEYS_DEFAULT_KEY", "AAAAAAAAAAAAAAAAAAAAAA==",
"HISS_KEYS_DEFAULT_KEY___HASH", "$2a$12$3T0VMnGMgvesehYomommnO02dbFOJuM/3elsmgmsB2/qlGSF3BIbe", // todo: remove from here and add it to separate test
"HISS_KEY_HASH_GENERATION_ENABLED", "true"
));
hiss = HissFactory.createHiss(hissPropertiesFromEnv);
}

Expand Down
Loading

0 comments on commit 8f8ed0f

Please sign in to comment.