implement prepareProperties for converting plaintext password to hash before saving
This commit is contained in:
parent
c53b2bdd62
commit
52fa265a3d
@ -2,6 +2,9 @@ package net.hostsharing.hsadminng.hash;
|
|||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.random.RandomGenerator;
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
import lombok.SneakyThrows;
|
||||||
import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
|
import org.bouncycastle.crypto.generators.OpenBSDBCrypt;
|
||||||
@ -10,7 +13,9 @@ import static net.hostsharing.hsadminng.hash.HashProcessor.Algorithm.SHA512;
|
|||||||
|
|
||||||
public class HashProcessor {
|
public class HashProcessor {
|
||||||
|
|
||||||
private static final SecureRandom secureRandom = new SecureRandom();
|
private static final RandomGenerator random = new SecureRandom();
|
||||||
|
private static final Queue<String> predefinedSalts = new PriorityQueue<>();
|
||||||
|
|
||||||
public static final int SALT_LENGTH = 16;
|
public static final int SALT_LENGTH = 16;
|
||||||
public static final int COST_FACTOR = 13;
|
public static final int COST_FACTOR = 13;
|
||||||
|
|
||||||
@ -60,15 +65,22 @@ public class HashProcessor {
|
|||||||
return "$6$" + salt + "$" + hashedPassword;
|
return "$6$" + salt + "$" + hashedPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void nextSalt(final String salt) {
|
||||||
|
predefinedSalts.add(salt);
|
||||||
|
}
|
||||||
|
|
||||||
public HashProcessor withSalt(final String salt) {
|
public HashProcessor withSalt(final String salt) {
|
||||||
this.salt = salt;
|
this.salt = salt;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashProcessor withRandomSalt() {
|
public HashProcessor withRandomSalt() {
|
||||||
|
if (!predefinedSalts.isEmpty()) {
|
||||||
|
return withSalt(predefinedSalts.poll());
|
||||||
|
}
|
||||||
final var stringBuilder = new StringBuilder(SALT_LENGTH);
|
final var stringBuilder = new StringBuilder(SALT_LENGTH);
|
||||||
for (int i = 0; i < SALT_LENGTH; ++i) {
|
for (int i = 0; i < SALT_LENGTH; ++i) {
|
||||||
int randomIndex = secureRandom.nextInt(SALT_CHARACTERS.length());
|
int randomIndex = random.nextInt(SALT_CHARACTERS.length());
|
||||||
stringBuilder.append(SALT_CHARACTERS.charAt(randomIndex));
|
stringBuilder.append(SALT_CHARACTERS.charAt(randomIndex));
|
||||||
}
|
}
|
||||||
return withSalt(stringBuilder.toString());
|
return withSalt(stringBuilder.toString());
|
||||||
|
@ -29,8 +29,9 @@ public class HsHostingAssetEntityProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// hashing passwords etc.
|
/// hashing passwords etc.
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
public HsHostingAssetEntityProcessor prepareForSave() {
|
public HsHostingAssetEntityProcessor prepareForSave() {
|
||||||
// FIXME: add computed properties
|
validator.prepareProperties(entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,12 +90,20 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
|||||||
throw new IllegalArgumentException("Integer value (or null) expected, but got " + value);
|
throw new IllegalArgumentException("Integer value (or null) expected, but got " + value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void prepareProperties(final E entity) {
|
||||||
|
stream(propertyValidators).forEach(p -> {
|
||||||
|
if ( p.isWriteOnly() && p.isComputed()) {
|
||||||
|
entity.directProps().put(p.propertyName, p.compute(entity));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public Map<String, Object> revampProperties(final E entity, final Map<String, Object> config) {
|
public Map<String, Object> revampProperties(final E entity, final Map<String, Object> config) {
|
||||||
final var copy = new HashMap<>(config);
|
final var copy = new HashMap<>(config);
|
||||||
stream(propertyValidators).forEach(p -> {
|
stream(propertyValidators).forEach(p -> {
|
||||||
if (p.isWriteOnly()) {
|
if (p.isWriteOnly()) {
|
||||||
copy.remove(p.propertyName);
|
copy.remove(p.propertyName);
|
||||||
} else if (p.isComputed()) {
|
} else if (p.isReadOnly() && p.isComputed()) {
|
||||||
copy.put(p.propertyName, p.compute(entity));
|
copy.put(p.propertyName, p.compute(entity));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -34,7 +34,6 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
|
|||||||
|
|
||||||
public PasswordProperty hashedUsing(final Algorithm algorithm) {
|
public PasswordProperty hashedUsing(final Algorithm algorithm) {
|
||||||
this.hashedUsing = algorithm;
|
this.hashedUsing = algorithm;
|
||||||
// FIXME: computedBy is too late, we need preprocess
|
|
||||||
computedBy((entity)
|
computedBy((entity)
|
||||||
-> ofNullable(entity.getDirectValue(propertyName, String.class))
|
-> ofNullable(entity.getDirectValue(propertyName, String.class))
|
||||||
.map(password -> hashAlgorithm(algorithm).withRandomSalt().generate(password).forLinux())
|
.map(password -> hashAlgorithm(algorithm).withRandomSalt().generate(password).forLinux())
|
||||||
|
@ -3,6 +3,7 @@ package net.hostsharing.hsadminng.hs.hosting.asset;
|
|||||||
import io.restassured.RestAssured;
|
import io.restassured.RestAssured;
|
||||||
import io.restassured.http.ContentType;
|
import io.restassured.http.ContentType;
|
||||||
import net.hostsharing.hsadminng.HsadminNgApplication;
|
import net.hostsharing.hsadminng.HsadminNgApplication;
|
||||||
|
import net.hostsharing.hsadminng.hash.HashProcessor;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRepository;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemRepository;
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||||
@ -523,6 +524,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.identifier("fir01-temp")
|
.identifier("fir01-temp")
|
||||||
.caption("some test-unix-user")
|
.caption("some test-unix-user")
|
||||||
.build());
|
.build());
|
||||||
|
HashProcessor.nextSalt("Jr5w/Y8zo8pCkqg7");
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
@ -575,7 +577,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
assertThat(asset.getCaption()).isEqualTo("some patched test-unix-user");
|
assertThat(asset.getCaption()).isEqualTo("some patched test-unix-user");
|
||||||
assertThat(asset.getConfig().toString()).isEqualTo("""
|
assertThat(asset.getConfig().toString()).isEqualTo("""
|
||||||
{
|
{
|
||||||
"password": "Ein Passwort mit 4 Zeichengruppen!",
|
"password": "$6$Jr5w/Y8zo8pCkqg7$JDJ5JDEzJFFsR3pidzdYTUZudE1GL0JZMURsTHVkZUpwTmVZYlM4ZzRqeC95WUo3Y2c5OUpTdUZpbWFx",
|
||||||
"shell": "/bin/bash",
|
"shell": "/bin/bash",
|
||||||
"totpKey": "0x1234567890abcdef0123456789abcdef"
|
"totpKey": "0x1234567890abcdef0123456789abcdef"
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user