Compare commits
No commits in common. "447e6cbc857974fc4e23c1c0959a6c9a33c919cf" and "94bda1df8a1122dc05ff43cf0a7cce2e8563a162" have entirely different histories.
447e6cbc85
...
94bda1df8a
@ -1,6 +1,5 @@
|
|||||||
## HostingAsset Type Structure
|
## HostingAsset Type Structure
|
||||||
|
|
||||||
|
|
||||||
### Domain
|
### Domain
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
@ -12,6 +11,8 @@ package Booking #feb28c {
|
|||||||
entity BI_CLOUD_SERVER
|
entity BI_CLOUD_SERVER
|
||||||
entity BI_MANAGED_SERVER
|
entity BI_MANAGED_SERVER
|
||||||
entity BI_MANAGED_WEBSPACE
|
entity BI_MANAGED_WEBSPACE
|
||||||
|
entity BI_DOMAIN_DNS_SETUP
|
||||||
|
entity BI_DOMAIN_SMTP_SETUP
|
||||||
}
|
}
|
||||||
|
|
||||||
package Hosting #feb28c{
|
package Hosting #feb28c{
|
||||||
@ -42,21 +43,20 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
|||||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||||
|
|
||||||
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
|
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||||
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
|
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||||
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
|
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
|
||||||
HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
|
HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
|
||||||
HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP
|
HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP
|
||||||
HA_DOMAIN_DNS_SETUP o..> HA_MANAGED_WEBSPACE
|
|
||||||
HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP
|
HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP
|
||||||
HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER
|
HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER
|
||||||
HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP
|
HA_DOMAIN_SMTP_SETUP *==> HA_DOMAIN_SETUP
|
||||||
HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE
|
HA_DOMAIN_SMTP_SETUP o..> HA_MANAGED_WEBSPACE
|
||||||
HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP
|
HA_DOMAIN_MBOX_SETUP *==> HA_DOMAIN_SETUP
|
||||||
HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE
|
HA_DOMAIN_MBOX_SETUP o..> HA_MANAGED_WEBSPACE
|
||||||
|
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_SETUP
|
HA_EMAIL_ADDRESS *==> HA_DOMAIN_MBOX_SETUP
|
||||||
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
||||||
@ -70,7 +70,6 @@ package Legend #white {
|
|||||||
}
|
}
|
||||||
Booking -down[hidden]->Legend
|
Booking -down[hidden]->Legend
|
||||||
```
|
```
|
||||||
|
|
||||||
### MariaDB
|
### MariaDB
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
@ -82,6 +81,8 @@ package Booking #feb28c {
|
|||||||
entity BI_CLOUD_SERVER
|
entity BI_CLOUD_SERVER
|
||||||
entity BI_MANAGED_SERVER
|
entity BI_MANAGED_SERVER
|
||||||
entity BI_MANAGED_WEBSPACE
|
entity BI_MANAGED_WEBSPACE
|
||||||
|
entity BI_DOMAIN_DNS_SETUP
|
||||||
|
entity BI_DOMAIN_SMTP_SETUP
|
||||||
}
|
}
|
||||||
|
|
||||||
package Hosting #feb28c{
|
package Hosting #feb28c{
|
||||||
@ -109,16 +110,16 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
|||||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||||
|
|
||||||
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
|
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||||
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
|
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||||
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
|
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||||
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
|
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
|
||||||
HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE
|
HA_MARIADB_USER *==> HA_MARIADB_INSTANCE
|
||||||
HA_MARIADB_USER o..> HA_MARIADB_INSTANCE
|
HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE
|
||||||
HA_MARIADB_DATABASE *==> HA_MARIADB_USER
|
HA_MARIADB_DATABASE *==> HA_MANAGED_WEBSPACE
|
||||||
HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE
|
HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE
|
||||||
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
||||||
@ -132,7 +133,6 @@ package Legend #white {
|
|||||||
}
|
}
|
||||||
Booking -down[hidden]->Legend
|
Booking -down[hidden]->Legend
|
||||||
```
|
```
|
||||||
|
|
||||||
### PostgreSQL
|
### PostgreSQL
|
||||||
|
|
||||||
```plantuml
|
```plantuml
|
||||||
@ -144,6 +144,8 @@ package Booking #feb28c {
|
|||||||
entity BI_CLOUD_SERVER
|
entity BI_CLOUD_SERVER
|
||||||
entity BI_MANAGED_SERVER
|
entity BI_MANAGED_SERVER
|
||||||
entity BI_MANAGED_WEBSPACE
|
entity BI_MANAGED_WEBSPACE
|
||||||
|
entity BI_DOMAIN_DNS_SETUP
|
||||||
|
entity BI_DOMAIN_SMTP_SETUP
|
||||||
}
|
}
|
||||||
|
|
||||||
package Hosting #feb28c{
|
package Hosting #feb28c{
|
||||||
@ -171,9 +173,9 @@ BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
|||||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||||
|
|
||||||
HA_CLOUD_SERVER *==> BI_CLOUD_SERVER
|
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||||
HA_MANAGED_SERVER *==> BI_MANAGED_SERVER
|
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||||
HA_MANAGED_WEBSPACE *==> BI_MANAGED_WEBSPACE
|
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||||
@ -194,5 +196,4 @@ package Legend #white {
|
|||||||
}
|
}
|
||||||
Booking -down[hidden]->Legend
|
Booking -down[hidden]->Legend
|
||||||
```
|
```
|
||||||
|
This code generated was by HsHostingAssetType.main, do not amend manually.
|
||||||
This code generated was by HsHostingAssetType.main, do not amend manually.
|
|
||||||
|
@ -1,90 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hash;
|
|
||||||
|
|
||||||
import java.security.SecureRandom;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.PriorityQueue;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.function.BiFunction;
|
|
||||||
import java.util.random.RandomGenerator;
|
|
||||||
|
|
||||||
import lombok.Getter;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Usage-example to generate hash:
|
|
||||||
* HashGenerator.using(LINUX_SHA512).withRandomSalt().hash("plaintext password");
|
|
||||||
*
|
|
||||||
* Usage-example to verify hash:
|
|
||||||
* HashGenerator.fromHash("hashed password).verify("plaintext password");
|
|
||||||
*/
|
|
||||||
@Getter
|
|
||||||
public final class HashGenerator {
|
|
||||||
|
|
||||||
private static final RandomGenerator random = new SecureRandom();
|
|
||||||
private static final Queue<String> predefinedSalts = new PriorityQueue<>();
|
|
||||||
|
|
||||||
public static final int RANDOM_SALT_LENGTH = 16;
|
|
||||||
private static final String RANDOM_SALT_CHARACTERS =
|
|
||||||
"abcdefghijklmnopqrstuvwxyz" +
|
|
||||||
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
|
||||||
"0123456789/.";
|
|
||||||
|
|
||||||
public enum Algorithm {
|
|
||||||
LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"),
|
|
||||||
LINUX_YESCRYPT(LinuxEtcShadowHashGenerator::hash, "y"),
|
|
||||||
MYSQL_NATIVE(MySQLNativePasswordHashGenerator::hash, "*"),
|
|
||||||
SCRAM_SHA256(PostgreSQLScramSHA256::hash, "SCRAM-SHA-256");
|
|
||||||
|
|
||||||
final BiFunction<HashGenerator, String, String> implementation;
|
|
||||||
final String prefix;
|
|
||||||
|
|
||||||
Algorithm(BiFunction<HashGenerator, String, String> implementation, final String prefix) {
|
|
||||||
this.implementation = implementation;
|
|
||||||
this.prefix = prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Algorithm byPrefix(final String prefix) {
|
|
||||||
return Arrays.stream(Algorithm.values()).filter(a -> a.prefix.equals(prefix)).findAny()
|
|
||||||
.orElseThrow(() -> new IllegalArgumentException("unknown hash algorithm: '" + prefix + "'"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Algorithm algorithm;
|
|
||||||
private String salt;
|
|
||||||
|
|
||||||
public static HashGenerator using(final Algorithm algorithm) {
|
|
||||||
return new HashGenerator(algorithm);
|
|
||||||
}
|
|
||||||
|
|
||||||
private HashGenerator(final Algorithm algorithm) {
|
|
||||||
this.algorithm = algorithm;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String hash(final String plaintextPassword) {
|
|
||||||
if (plaintextPassword == null) {
|
|
||||||
throw new IllegalStateException("no password given");
|
|
||||||
}
|
|
||||||
|
|
||||||
return algorithm.implementation.apply(this, plaintextPassword);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void nextSalt(final String salt) {
|
|
||||||
predefinedSalts.add(salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashGenerator withSalt(final String salt) {
|
|
||||||
this.salt = salt;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public HashGenerator withRandomSalt() {
|
|
||||||
if (!predefinedSalts.isEmpty()) {
|
|
||||||
return withSalt(predefinedSalts.poll());
|
|
||||||
}
|
|
||||||
final var stringBuilder = new StringBuilder(RANDOM_SALT_LENGTH);
|
|
||||||
for (int i = 0; i < RANDOM_SALT_LENGTH; ++i) {
|
|
||||||
int randomIndex = random.nextInt(RANDOM_SALT_CHARACTERS.length());
|
|
||||||
stringBuilder.append(RANDOM_SALT_CHARACTERS.charAt(randomIndex));
|
|
||||||
}
|
|
||||||
return withSalt(stringBuilder.toString());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +1,109 @@
|
|||||||
package net.hostsharing.hsadminng.hash;
|
package net.hostsharing.hsadminng.hash;
|
||||||
|
|
||||||
|
import java.security.SecureRandom;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.PriorityQueue;
|
||||||
|
import java.util.Queue;
|
||||||
|
import java.util.random.RandomGenerator;
|
||||||
|
|
||||||
import com.sun.jna.Library;
|
import com.sun.jna.Library;
|
||||||
import com.sun.jna.Native;
|
import com.sun.jna.Native;
|
||||||
|
|
||||||
public class LinuxEtcShadowHashGenerator {
|
public class LinuxEtcShadowHashGenerator {
|
||||||
|
|
||||||
public static String hash(final HashGenerator generator, final String payload) {
|
private static final RandomGenerator random = new SecureRandom();
|
||||||
if (generator.getSalt() == null) {
|
private static final Queue<String> predefinedSalts = new PriorityQueue<>();
|
||||||
throw new IllegalStateException("no salt given");
|
|
||||||
|
public static final int SALT_LENGTH = 16;
|
||||||
|
|
||||||
|
private final String plaintextPassword;
|
||||||
|
private Algorithm algorithm;
|
||||||
|
|
||||||
|
public enum Algorithm {
|
||||||
|
SHA512("6"),
|
||||||
|
YESCRYPT("y");
|
||||||
|
|
||||||
|
final String prefix;
|
||||||
|
|
||||||
|
Algorithm(final String prefix) {
|
||||||
|
this.prefix = prefix;
|
||||||
}
|
}
|
||||||
|
|
||||||
return NativeCryptLibrary.INSTANCE.crypt(payload, "$" + generator.getAlgorithm().prefix + "$" + generator.getSalt());
|
static Algorithm byPrefix(final String prefix) {
|
||||||
|
return Arrays.stream(Algorithm.values()).filter(a -> a.prefix.equals(prefix)).findAny()
|
||||||
|
.orElseThrow(() -> new IllegalArgumentException("unknown hash algorithm: '" + prefix + "'"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void verify(final String givenHash, final String payload) {
|
private static final String SALT_CHARACTERS =
|
||||||
|
"abcdefghijklmnopqrstuvwxyz" +
|
||||||
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
|
||||||
|
"0123456789/.";
|
||||||
|
|
||||||
|
private String salt;
|
||||||
|
|
||||||
|
public static LinuxEtcShadowHashGenerator hash(final String plaintextPassword) {
|
||||||
|
return new LinuxEtcShadowHashGenerator(plaintextPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private LinuxEtcShadowHashGenerator(final String plaintextPassword) {
|
||||||
|
this.plaintextPassword = plaintextPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinuxEtcShadowHashGenerator using(final Algorithm algorithm) {
|
||||||
|
this.algorithm = algorithm;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
void verify(final String givenHash) {
|
||||||
final var parts = givenHash.split("\\$");
|
final var parts = givenHash.split("\\$");
|
||||||
if (parts.length < 3 || parts.length > 5) {
|
if (parts.length < 3 || parts.length > 5) {
|
||||||
throw new IllegalArgumentException("hash with unknown hash method: " + givenHash);
|
throw new IllegalArgumentException("not a " + algorithm.name() + " Linux hash: " + givenHash);
|
||||||
}
|
}
|
||||||
|
|
||||||
final var algorithm = HashGenerator.Algorithm.byPrefix(parts[1]);
|
algorithm = Algorithm.byPrefix(parts[1]);
|
||||||
final var salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3];
|
salt = parts.length == 4 ? parts[2] : parts[2] + "$" + parts[3];
|
||||||
final var calcualatedHash = HashGenerator.using(algorithm).withSalt(salt).hash(payload);
|
|
||||||
if (!calcualatedHash.equals(givenHash)) {
|
if (!generate().equals(givenHash)) {
|
||||||
throw new IllegalArgumentException("invalid password");
|
throw new IllegalArgumentException("invalid password");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String generate() {
|
||||||
|
if (salt == null) {
|
||||||
|
throw new IllegalStateException("no salt given");
|
||||||
|
}
|
||||||
|
if (plaintextPassword == null) {
|
||||||
|
throw new IllegalStateException("no password given");
|
||||||
|
}
|
||||||
|
|
||||||
|
return NativeCryptLibrary.INSTANCE.crypt(plaintextPassword, "$" + algorithm.prefix + "$" + salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void nextSalt(final String salt) {
|
||||||
|
predefinedSalts.add(salt);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinuxEtcShadowHashGenerator withSalt(final String salt) {
|
||||||
|
this.salt = salt;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LinuxEtcShadowHashGenerator withRandomSalt() {
|
||||||
|
if (!predefinedSalts.isEmpty()) {
|
||||||
|
return withSalt(predefinedSalts.poll());
|
||||||
|
}
|
||||||
|
final var stringBuilder = new StringBuilder(SALT_LENGTH);
|
||||||
|
for (int i = 0; i < SALT_LENGTH; ++i) {
|
||||||
|
int randomIndex = random.nextInt(SALT_CHARACTERS.length());
|
||||||
|
stringBuilder.append(SALT_CHARACTERS.charAt(randomIndex));
|
||||||
|
}
|
||||||
|
return withSalt(stringBuilder.toString());
|
||||||
|
}
|
||||||
|
public static void main(String[] args) {
|
||||||
|
System.out.println(NativeCryptLibrary.INSTANCE.crypt("given password", "$6$abcdefghijklmno"));
|
||||||
|
}
|
||||||
|
|
||||||
public interface NativeCryptLibrary extends Library {
|
public interface NativeCryptLibrary extends Library {
|
||||||
NativeCryptLibrary INSTANCE = Native.load("crypt", NativeCryptLibrary.class);
|
NativeCryptLibrary INSTANCE = Native.load("crypt", NativeCryptLibrary.class);
|
||||||
|
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hash;
|
|
||||||
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
public class MySQLNativePasswordHashGenerator {
|
|
||||||
|
|
||||||
public static String hash(final HashGenerator generator, final String password) {
|
|
||||||
// TODO.impl: if a random salt is generated or not should be part of the algorithm definition
|
|
||||||
// if (generator.getSalt() != null) {
|
|
||||||
// throw new IllegalStateException("salt not supported");
|
|
||||||
// }
|
|
||||||
|
|
||||||
try {
|
|
||||||
final var sha1 = MessageDigest.getInstance("SHA-1");
|
|
||||||
final var firstHash = sha1.digest(password.getBytes());
|
|
||||||
final var secondHash = sha1.digest(firstHash);
|
|
||||||
return "*" + bytesToHex(secondHash).toUpperCase();
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("SHA-1 algorithm not found", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String bytesToHex(byte[] bytes) {
|
|
||||||
final var hexString = new StringBuilder();
|
|
||||||
for (byte b : bytes) {
|
|
||||||
final var hex = Integer.toHexString(0xff & b);
|
|
||||||
if (hex.length() == 1) {
|
|
||||||
hexString.append('0');
|
|
||||||
}
|
|
||||||
hexString.append(hex);
|
|
||||||
}
|
|
||||||
return hexString.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,61 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hash;
|
|
||||||
|
|
||||||
import lombok.SneakyThrows;
|
|
||||||
|
|
||||||
import javax.crypto.Mac;
|
|
||||||
import javax.crypto.SecretKeyFactory;
|
|
||||||
import javax.crypto.spec.PBEKeySpec;
|
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
public class PostgreSQLScramSHA256 {
|
|
||||||
|
|
||||||
private static final String PBKDF_2_WITH_HMAC_SHA256 = "PBKDF2WithHmacSHA256";
|
|
||||||
private static final String HMAC_SHA256 = "HmacSHA256";
|
|
||||||
private static final String SHA256 = "SHA-256";
|
|
||||||
private static final int ITERATIONS = 4096;
|
|
||||||
public static final int KEY_LENGTH_IN_BITS = 256;
|
|
||||||
|
|
||||||
private static final PostgreSQLScramSHA256 scram = new PostgreSQLScramSHA256();
|
|
||||||
|
|
||||||
@SneakyThrows
|
|
||||||
public static String hash(final HashGenerator generator, final String password) {
|
|
||||||
if (generator.getSalt() == null) {
|
|
||||||
throw new IllegalStateException("no salt given");
|
|
||||||
}
|
|
||||||
|
|
||||||
final byte[] salt = generator.getSalt().getBytes(Charset.forName("latin1")); // Base64.getEncoder().encode(generator.getSalt().getBytes());
|
|
||||||
final byte[] saltedPassword = scram.generateSaltedPassword(password, salt);
|
|
||||||
final byte[] clientKey = scram.hmacSHA256(saltedPassword, "Client Key".getBytes());
|
|
||||||
final byte[] storedKey = MessageDigest.getInstance(SHA256).digest(clientKey);
|
|
||||||
final byte[] serverKey = scram.hmacSHA256(saltedPassword, "Server Key".getBytes());
|
|
||||||
|
|
||||||
return "SCRAM-SHA-256${iterations}:{base64EncodedSalt}${base64EncodedStoredKey}:{base64EncodedServerKey}"
|
|
||||||
.replace("{iterations}", Integer.toString(ITERATIONS))
|
|
||||||
.replace("{base64EncodedSalt}", base64(salt))
|
|
||||||
.replace("{base64EncodedStoredKey}", base64(storedKey))
|
|
||||||
.replace("{base64EncodedServerKey}", base64(serverKey));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String base64(final byte[] salt) {
|
|
||||||
return Base64.getEncoder().encodeToString(salt);
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] generateSaltedPassword(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
||||||
final var spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_LENGTH_IN_BITS);
|
|
||||||
return SecretKeyFactory.getInstance(PBKDF_2_WITH_HMAC_SHA256).generateSecret(spec).getEncoded();
|
|
||||||
}
|
|
||||||
|
|
||||||
private byte[] hmacSHA256(byte[] key, byte[] message)
|
|
||||||
throws NoSuchAlgorithmException, InvalidKeyException {
|
|
||||||
final var mac = Mac.getInstance(HMAC_SHA256);
|
|
||||||
mac.init(new SecretKeySpec(key, HMAC_SHA256));
|
|
||||||
return mac.doFinal(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -82,16 +82,17 @@ public enum HsHostingAssetType implements Node {
|
|||||||
|
|
||||||
PGSQL_INSTANCE( // TODO.spec: identifier to be specified
|
PGSQL_INSTANCE( // TODO.spec: identifier to be specified
|
||||||
inGroup("PostgreSQL"),
|
inGroup("PostgreSQL"),
|
||||||
requiredParent(MANAGED_SERVER)), // TODO.spec: or MANAGED_WEBSPACE?
|
requiredParent(MANAGED_SERVER)),
|
||||||
|
|
||||||
PGSQL_USER( // named e.g. xyz00_abc
|
PGSQL_USER( // named e.g. xyz00_abc
|
||||||
inGroup("PostgreSQL"),
|
inGroup("PostgreSQL"),
|
||||||
requiredParent(MANAGED_WEBSPACE), // thus, the MANAGED_WEBSPACE:Agent becomes RBAC owner
|
requiredParent(PGSQL_INSTANCE),
|
||||||
assignedTo(PGSQL_INSTANCE)), // keep in mind: no RBAC grants implied
|
assignedTo(MANAGED_WEBSPACE)),
|
||||||
|
|
||||||
PGSQL_DATABASE( // named e.g. xyz00_abc
|
PGSQL_DATABASE( // named e.g. xyz00_abc
|
||||||
inGroup("PostgreSQL"),
|
inGroup("PostgreSQL"),
|
||||||
requiredParent(PGSQL_USER)), // thus, the PGSQL_USER_USER:Agent becomes RBAC owner
|
requiredParent(MANAGED_WEBSPACE), // TODO.spec: or PGSQL_USER?
|
||||||
|
assignedTo(PGSQL_INSTANCE)), // TODO.spec: or swapping parent+assignedTo?
|
||||||
|
|
||||||
MARIADB_INSTANCE( // TODO.spec: identifier to be specified
|
MARIADB_INSTANCE( // TODO.spec: identifier to be specified
|
||||||
inGroup("MariaDB"),
|
inGroup("MariaDB"),
|
||||||
@ -99,12 +100,13 @@ public enum HsHostingAssetType implements Node {
|
|||||||
|
|
||||||
MARIADB_USER( // named e.g. xyz00_abc
|
MARIADB_USER( // named e.g. xyz00_abc
|
||||||
inGroup("MariaDB"),
|
inGroup("MariaDB"),
|
||||||
requiredParent(MANAGED_WEBSPACE), // thus, the MANAGED_WEBSPACE:Agent becomes RBAC owner
|
requiredParent(MARIADB_INSTANCE),
|
||||||
assignedTo(MARIADB_INSTANCE)),
|
assignedTo(MANAGED_WEBSPACE)),
|
||||||
|
|
||||||
MARIADB_DATABASE( // named e.g. xyz00_abc
|
MARIADB_DATABASE( // named e.g. xyz00_abc
|
||||||
inGroup("MariaDB"),
|
inGroup("MariaDB"),
|
||||||
requiredParent(MARIADB_USER)), // thus, the MARIADB_USER:Agent becomes RBAC owner
|
requiredParent(MANAGED_WEBSPACE), // TODO.spec: or MARIADB_USER?
|
||||||
|
assignedTo(MARIADB_INSTANCE)), // TODO.spec: or swapping parent+assignedTo?
|
||||||
|
|
||||||
IP_NUMBER(
|
IP_NUMBER(
|
||||||
inGroup("Server"),
|
inGroup("Server"),
|
||||||
|
@ -32,7 +32,7 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
|
|||||||
|
|
||||||
HostingAssetEntityValidator(
|
HostingAssetEntityValidator(
|
||||||
final HsHostingAssetType assetType,
|
final HsHostingAssetType assetType,
|
||||||
final AlarmContact alarmContactValidation, // hostmaster alert address is implicitly added where needed
|
final AlarmContact alarmContactValidation,
|
||||||
final ValidatableProperty<?, ?>... properties) {
|
final ValidatableProperty<?, ?>... properties) {
|
||||||
super(properties);
|
super(properties);
|
||||||
this.bookingItemReferenceValidation = new ReferenceValidator<>(
|
this.bookingItemReferenceValidation = new ReferenceValidator<>(
|
||||||
@ -213,7 +213,6 @@ public abstract class HostingAssetEntityValidator extends HsEntityValidator<HsHo
|
|||||||
super(policy, HsHostingAssetEntity::getAlarmContact);
|
super(policy, HsHostingAssetEntity::getAlarmContact);
|
||||||
}
|
}
|
||||||
|
|
||||||
// hostmaster alert address is implicitly added where neccessary
|
|
||||||
static AlarmContact isOptional() {
|
static AlarmContact isOptional() {
|
||||||
return new AlarmContact(HsHostingAssetType.RelationPolicy.OPTIONAL);
|
return new AlarmContact(HsHostingAssetType.RelationPolicy.OPTIONAL);
|
||||||
}
|
}
|
||||||
|
@ -26,12 +26,6 @@ public class HostingAssetEntityValidatorRegistry {
|
|||||||
register(DOMAIN_SMTP_SETUP, new HsDomainSmtpSetupHostingAssetValidator());
|
register(DOMAIN_SMTP_SETUP, new HsDomainSmtpSetupHostingAssetValidator());
|
||||||
register(DOMAIN_MBOX_SETUP, new HsDomainMboxSetupHostingAssetValidator());
|
register(DOMAIN_MBOX_SETUP, new HsDomainMboxSetupHostingAssetValidator());
|
||||||
register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator());
|
register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator());
|
||||||
register(MARIADB_INSTANCE, new HsMariaDbInstanceHostingAssetValidator());
|
|
||||||
register(MARIADB_USER, new HsMariaDbUserHostingAssetValidator());
|
|
||||||
register(MARIADB_DATABASE, new HsMariaDbDatabaseHostingAssetValidator());
|
|
||||||
register(PGSQL_INSTANCE, new HsPostgreSqlDbInstanceHostingAssetValidator());
|
|
||||||
register(PGSQL_USER, new HsPostgreSqlUserHostingAssetValidator());
|
|
||||||
register(PGSQL_DATABASE, new HsPostgreSqlDatabaseHostingAssetValidator());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {
|
||||||
|
@ -14,7 +14,7 @@ class HsManagedServerHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
public HsManagedServerHostingAssetValidator() {
|
public HsManagedServerHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
MANAGED_SERVER,
|
MANAGED_SERVER,
|
||||||
AlarmContact.isOptional(),
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
|
|
||||||
// monitoring
|
// monitoring
|
||||||
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
integerProperty("monit_max_cpu_usage").unit("%").min(10).max(100).withDefault(92),
|
||||||
|
@ -10,7 +10,7 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator
|
|||||||
public HsManagedWebspaceHostingAssetValidator() {
|
public HsManagedWebspaceHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
MANAGED_WEBSPACE,
|
MANAGED_WEBSPACE,
|
||||||
AlarmContact.isOptional(),
|
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||||
NO_EXTRA_PROPERTIES);
|
NO_EXTRA_PROPERTIES);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
|
||||||
|
|
||||||
class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
public HsMariaDbDatabaseHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
MARIADB_DATABASE,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
|
|
||||||
stringProperty("encoding").matchesRegEx("[a-z0-9_]+").maxLength(24).provided("latin1", "utf8").withDefault("utf8"));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
|
|
||||||
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE;
|
|
||||||
|
|
||||||
class HsMariaDbInstanceHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
final static String DEFAULT_INSTANCE_IDENTIFIER_SUFFIX = "|MariaDB.default"; // TODO.spec: specify instance naming
|
|
||||||
|
|
||||||
public HsMariaDbInstanceHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
MARIADB_INSTANCE,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
NO_EXTRA_PROPERTIES); // TODO.spec: specify instance properties, e.g. installed extensions
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
return Pattern.compile(
|
|
||||||
"^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier()
|
|
||||||
+ DEFAULT_INSTANCE_IDENTIFIER_SUFFIX)
|
|
||||||
+ "$");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preprocessEntity(final HsHostingAssetEntity entity) {
|
|
||||||
super.preprocessEntity(entity);
|
|
||||||
if (entity.getIdentifier() == null) {
|
|
||||||
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(
|
|
||||||
pa.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
|
|
||||||
|
|
||||||
class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
public HsMariaDbUserHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
MARIADB_USER,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
|
|
||||||
// TODO.impl: we need to be able to suppress updating of fields etc., something like this:
|
|
||||||
// withFieldValidation(
|
|
||||||
// referenceProperty(alarmContact).isOptional(),
|
|
||||||
// referenceProperty(parentAsset).isWriteOnce(),
|
|
||||||
// referenceProperty(assignedToAsset).isWriteOnce(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.MYSQL_NATIVE).writeOnly());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
|
|
||||||
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
|
|
||||||
|
|
||||||
class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
public HsPostgreSqlDatabaseHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
PGSQL_DATABASE,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
|
|
||||||
stringProperty("encoding").matchesRegEx("[A-Z0-9_]+").maxLength(24).provided("LATIN1", "UTF8").withDefault("UTF8")
|
|
||||||
|
|
||||||
// TODO.spec: PostgreSQL extensions in instance and here? also decide which. Free selection or booleans/checkboxes?
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
|
|
||||||
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,39 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE;
|
|
||||||
|
|
||||||
class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
final static String DEFAULT_INSTANCE_IDENTIFIER_SUFFIX = "|PgSql.default"; // TODO.spec: specify instance naming
|
|
||||||
|
|
||||||
public HsPostgreSqlDbInstanceHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
PGSQL_DATABASE,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
|
|
||||||
// TODO.spec: PostgreSQL extensions in database and here? also decide which. Free selection or booleans/checkboxes?
|
|
||||||
NO_EXTRA_PROPERTIES); // TODO.spec: specify instance properties, e.g. installed extensions
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
return Pattern.compile(
|
|
||||||
"^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier()
|
|
||||||
+ DEFAULT_INSTANCE_IDENTIFIER_SUFFIX)
|
|
||||||
+ "$");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void preprocessEntity(final HsHostingAssetEntity entity) {
|
|
||||||
super.preprocessEntity(entity);
|
|
||||||
if (entity.getIdentifier() == null) {
|
|
||||||
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(
|
|
||||||
pa.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
|
|
||||||
import java.util.regex.Pattern;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
|
|
||||||
|
|
||||||
class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator {
|
|
||||||
|
|
||||||
public HsPostgreSqlUserHostingAssetValidator() {
|
|
||||||
super(
|
|
||||||
PGSQL_USER,
|
|
||||||
AlarmContact.isOptional(),
|
|
||||||
|
|
||||||
// TODO.impl: we need to be able to suppress updating of fields etc., something like this:
|
|
||||||
// withFieldValidation(
|
|
||||||
// referenceProperty(alarmContact).isOptional(),
|
|
||||||
// referenceProperty(parentAsset).isWriteOnce(),
|
|
||||||
// referenceProperty(assignedToAsset).isWriteOnce(),
|
|
||||||
// );
|
|
||||||
|
|
||||||
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.SCRAM_SHA256).writeOnly());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
|
||||||
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
|
|
||||||
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;
|
||||||
@ -30,8 +30,7 @@ class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator {
|
|||||||
.withDefault("/bin/false"),
|
.withDefault("/bin/false"),
|
||||||
stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir),
|
stringProperty("homedir").readOnly().computedBy(HsUnixUserHostingAssetValidator::computeHomedir),
|
||||||
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(),
|
stringProperty("totpKey").matchesRegEx("^0x([0-9A-Fa-f]{2})+$").minLength(20).maxLength(256).undisclosed().writeOnly().optional(),
|
||||||
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(HashGenerator.Algorithm.LINUX_SHA512).writeOnly());
|
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LinuxEtcShadowHashGenerator.Algorithm.SHA512).writeOnly());
|
||||||
// TODO.spec: public SSH keys?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1,13 +1,13 @@
|
|||||||
package net.hostsharing.hsadminng.hs.validation;
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm;
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator.Algorithm;
|
|
||||||
import lombok.Setter;
|
import lombok.Setter;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import static java.util.Optional.ofNullable;
|
import static java.util.Optional.ofNullable;
|
||||||
|
import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash;
|
||||||
import static net.hostsharing.hsadminng.mapper.Array.insertNewEntriesAfterExistingEntry;
|
import static net.hostsharing.hsadminng.mapper.Array.insertNewEntriesAfterExistingEntry;
|
||||||
|
|
||||||
@Setter
|
@Setter
|
||||||
@ -36,7 +36,7 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
|
|||||||
this.hashedUsing = algorithm;
|
this.hashedUsing = algorithm;
|
||||||
computedBy((entity)
|
computedBy((entity)
|
||||||
-> ofNullable(entity.getDirectValue(propertyName, String.class))
|
-> ofNullable(entity.getDirectValue(propertyName, String.class))
|
||||||
.map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password))
|
.map(password -> hash(password).using(algorithm).withRandomSalt().generate())
|
||||||
.orElse(null));
|
.orElse(null));
|
||||||
return self();
|
return self();
|
||||||
}
|
}
|
||||||
|
@ -17,10 +17,8 @@ components:
|
|||||||
- DOMAIN_MBOX_SETUP
|
- DOMAIN_MBOX_SETUP
|
||||||
- EMAIL_ALIAS
|
- EMAIL_ALIAS
|
||||||
- EMAIL_ADDRESS
|
- EMAIL_ADDRESS
|
||||||
- PGSQL_INSTANCE
|
|
||||||
- PGSQL_USER
|
- PGSQL_USER
|
||||||
- PGSQL_DATABASE
|
- PGSQL_DATABASE
|
||||||
- MARIADB_INSTANCE
|
|
||||||
- MARIADB_USER
|
- MARIADB_USER
|
||||||
- MARIADB_DATABASE
|
- MARIADB_DATABASE
|
||||||
|
|
||||||
|
@ -16,10 +16,8 @@ create type HsHostingAssetType as enum (
|
|||||||
'DOMAIN_MBOX_SETUP',
|
'DOMAIN_MBOX_SETUP',
|
||||||
'EMAIL_ALIAS',
|
'EMAIL_ALIAS',
|
||||||
'EMAIL_ADDRESS',
|
'EMAIL_ADDRESS',
|
||||||
'PGSQL_INSTANCE',
|
|
||||||
'PGSQL_USER',
|
'PGSQL_USER',
|
||||||
'PGSQL_DATABASE',
|
'PGSQL_DATABASE',
|
||||||
'MARIADB_INSTANCE',
|
|
||||||
'MARIADB_USER',
|
'MARIADB_USER',
|
||||||
'MARIADB_DATABASE'
|
'MARIADB_DATABASE'
|
||||||
);
|
);
|
||||||
@ -49,9 +47,6 @@ create table if not exists hs_hosting_asset
|
|||||||
--changeset hosting-asset-TYPE-HIERARCHY-CHECK:1 endDelimiter:--//
|
--changeset hosting-asset-TYPE-HIERARCHY-CHECK:1 endDelimiter:--//
|
||||||
-- ----------------------------------------------------------------------------
|
-- ----------------------------------------------------------------------------
|
||||||
|
|
||||||
-- TODO.impl: this could be generated from HsHostingAssetType
|
|
||||||
-- also including a check for assignedToAssetUuud
|
|
||||||
|
|
||||||
create or replace function hs_hosting_asset_type_hierarchy_check_tf()
|
create or replace function hs_hosting_asset_type_hierarchy_check_tf()
|
||||||
returns trigger
|
returns trigger
|
||||||
language plpgsql as $$
|
language plpgsql as $$
|
||||||
@ -77,14 +72,10 @@ begin
|
|||||||
when 'DOMAIN_SMTP_SETUP' then 'DOMAIN_SETUP'
|
when 'DOMAIN_SMTP_SETUP' then 'DOMAIN_SETUP'
|
||||||
when 'DOMAIN_MBOX_SETUP' then 'DOMAIN_SETUP'
|
when 'DOMAIN_MBOX_SETUP' then 'DOMAIN_SETUP'
|
||||||
when 'EMAIL_ADDRESS' then 'DOMAIN_MBOX_SETUP'
|
when 'EMAIL_ADDRESS' then 'DOMAIN_MBOX_SETUP'
|
||||||
|
|
||||||
when 'PGSQL_INSTANCE' then 'MANAGED_SERVER'
|
|
||||||
when 'PGSQL_USER' then 'MANAGED_WEBSPACE'
|
when 'PGSQL_USER' then 'MANAGED_WEBSPACE'
|
||||||
when 'PGSQL_DATABASE' then 'PGSQL_USER'
|
when 'PGSQL_DATABASE' then 'MANAGED_WEBSPACE'
|
||||||
|
|
||||||
when 'MARIADB_INSTANCE' then 'MANAGED_SERVER'
|
|
||||||
when 'MARIADB_USER' then 'MANAGED_WEBSPACE'
|
when 'MARIADB_USER' then 'MANAGED_WEBSPACE'
|
||||||
when 'MARIADB_DATABASE' then 'MARIADB_USER'
|
when 'MARIADB_DATABASE' then 'MANAGED_WEBSPACE'
|
||||||
else raiseException(format('[400] unknown asset type %s', NEW.type::text))
|
else raiseException(format('[400] unknown asset type %s', NEW.type::text))
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
@ -25,10 +25,6 @@ declare
|
|||||||
webUnixUserUuid uuid;
|
webUnixUserUuid uuid;
|
||||||
domainSetupUuid uuid;
|
domainSetupUuid uuid;
|
||||||
domainMBoxSetupUuid uuid;
|
domainMBoxSetupUuid uuid;
|
||||||
mariaDbInstanceUuid uuid;
|
|
||||||
mariaDbUserUuid uuid;
|
|
||||||
pgSqlInstanceUuid uuid;
|
|
||||||
PgSqlUserUuid uuid;
|
|
||||||
begin
|
begin
|
||||||
currentTask := 'creating hosting-asset test-data ' || givenProjectCaption;
|
currentTask := 'creating hosting-asset test-data ' || givenProjectCaption;
|
||||||
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
|
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
|
||||||
@ -73,25 +69,14 @@ begin
|
|||||||
select uuid_generate_v4() into webUnixUserUuid;
|
select uuid_generate_v4() into webUnixUserUuid;
|
||||||
select uuid_generate_v4() into domainSetupUuid;
|
select uuid_generate_v4() into domainSetupUuid;
|
||||||
select uuid_generate_v4() into domainMBoxSetupUuid;
|
select uuid_generate_v4() into domainMBoxSetupUuid;
|
||||||
select uuid_generate_v4() into mariaDbInstanceUuid;
|
|
||||||
select uuid_generate_v4() into mariaDbUserUuid;
|
|
||||||
select uuid_generate_v4() into pgSqlInstanceUuid;
|
|
||||||
select uuid_generate_v4() into pgSqlUserUuid;
|
|
||||||
debitorNumberSuffix := relatedDebitor.debitorNumberSuffix;
|
debitorNumberSuffix := relatedDebitor.debitorNumberSuffix;
|
||||||
defaultPrefix := relatedDebitor.defaultPrefix;
|
defaultPrefix := relatedDebitor.defaultPrefix;
|
||||||
|
|
||||||
insert into hs_hosting_asset
|
insert into hs_hosting_asset
|
||||||
(uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config)
|
(uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config)
|
||||||
values
|
values (managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb),
|
||||||
(managedServerUuid, managedServerBI.uuid, 'MANAGED_SERVER', null, null, 'vm10' || debitorNumberSuffix, 'some ManagedServer', '{ "monit_max_cpu_usage": 90, "monit_max_ram_usage": 80, "monit_max_ssd_usage": 70 }'::jsonb),
|
|
||||||
(uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb),
|
(uuid_generate_v4(), cloudServerBI.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb),
|
||||||
(managedWebspaceUuid, managedWebspaceBI.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::jsonb),
|
(managedWebspaceUuid, managedWebspaceBI.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::jsonb),
|
||||||
(mariaDbInstanceUuid, null, 'MARIADB_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.MariaDB.default', 'some default MariaDB instance','{}'::jsonb),
|
|
||||||
(mariaDbUserUuid, null, 'MARIADB_USER', managedWebspaceUuid, mariaDbInstanceUuid, defaultPrefix || '01_web', 'some default MariaDB user', '{ "password": "<TODO:replace-by-encrypted-mariadb-password"}'::jsonb ),
|
|
||||||
(uuid_generate_v4(), null, 'MARIADB_DATABASE', mariaDbUserUuid, mariaDbInstanceUuid, defaultPrefix || '01_web', 'some default MariaDB database','{ "encryption": "utf8", "collation": "utf8"}'::jsonb ),
|
|
||||||
(pgSqlInstanceUuid, null, 'PGSQL_INSTANCE', managedServerUuid, null, 'vm10' || debitorNumberSuffix || '.Postgresql.default', 'some default Postgresql instance','{}'::jsonb),
|
|
||||||
(PgSqlUserUuid, null, 'PGSQL_USER', managedWebspaceUuid, pgSqlInstanceUuid, defaultPrefix || '01_web', 'some default Postgresql user', '{ "password": "<TODO:replace-by-encrypted-postgresql-password"}'::jsonb ),
|
|
||||||
(uuid_generate_v4(), null, 'PGSQL_DATABASE', pgSqlUserUuid, pgSqlInstanceUuid, defaultPrefix || '01_web', 'some default Postgresql database','{ "encryption": "utf8", "collation": "utf8"}'::jsonb ),
|
|
||||||
(uuid_generate_v4(), null, 'EMAIL_ALIAS', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some E-Mail-Alias', '{ "target": [ "office@example.org", "archive@example.com" ] }'::jsonb),
|
(uuid_generate_v4(), null, 'EMAIL_ALIAS', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some E-Mail-Alias', '{ "target": [ "office@example.org", "archive@example.com" ] }'::jsonb),
|
||||||
(webUnixUserUuid, null, 'UNIX_USER', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some UnixUser for Website', '{ "SSD-soft-quota": "128", "SSD-hard-quota": "256", "HDD-soft-quota": "512", "HDD-hard-quota": "1024"}'::jsonb),
|
(webUnixUserUuid, null, 'UNIX_USER', managedWebspaceUuid, null, defaultPrefix || '01-web', 'some UnixUser for Website', '{ "SSD-soft-quota": "128", "SSD-hard-quota": "256", "HDD-soft-quota": "512", "HDD-hard-quota": "1024"}'::jsonb),
|
||||||
(domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb),
|
(domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb),
|
||||||
|
@ -1,77 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hash;
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_SHA512;
|
|
||||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.MYSQL_NATIVE;
|
|
||||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.SCRAM_SHA256;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
import static org.assertj.core.api.Assertions.catchThrowable;
|
|
||||||
|
|
||||||
class HashGeneratorUnitTest {
|
|
||||||
|
|
||||||
final String GIVEN_PASSWORD = "given password";
|
|
||||||
final String WRONG_PASSWORD = "wrong password";
|
|
||||||
final String GIVEN_SALT = "0123456789abcdef";
|
|
||||||
|
|
||||||
// generated via mkpasswd for plaintext password GIVEN_PASSWORD (see above)
|
|
||||||
final String GIVEN_LINUX_GENERATED_SHA512_HASH = "$6$ooei1HK6JXVaI7KC$sY5d9fEOr36hjh4CYwIKLMfRKL1539bEmbVCZ.zPiH0sv7jJVnoIXb5YEefEtoSM2WWgDi9hr7vXRe3Nw8zJP/";
|
|
||||||
final String GIVEN_LINUX_GENERATED_YESCRYPT_HASH = "$y$j9T$wgYACPmBXvlMg2MzeZA0p1$KXUzd28nG.67GhPnBZ3aZsNNA5bWFdL/dyG4wS0iRw7";
|
|
||||||
|
|
||||||
// generated in PostgreSQL using:
|
|
||||||
// CREATE USER test WITH PASSWORD 'given password';
|
|
||||||
// SELECT rolname, rolpassword FROM pg_authid WHERE rolname = 'test';
|
|
||||||
final String GIVEN_POSTGRESQL_GENERATED_SCRAM_SHA256_HASH = "SCRAM-SHA-256$4096:m8M12fdSTsKH+ywthTx1Zw==$4vsB1OddRNdsej9NPAFh91MPdtbOPjkQ85LQZS5lV0Q=:NsVpQNx4Ic/8Sqj1dxfBzUAxyF4FCTMpIsI+bOZCTfA=";
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void verifiesLinuxPasswordAgainstSha512HashFromMkpasswd() {
|
|
||||||
LinuxEtcShadowHashGenerator.verify(GIVEN_LINUX_GENERATED_SHA512_HASH, GIVEN_PASSWORD); // throws exception if wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void verifiesLinuxPasswordAgainstYescryptHashFromMkpasswd() {
|
|
||||||
LinuxEtcShadowHashGenerator.verify(GIVEN_LINUX_GENERATED_YESCRYPT_HASH, GIVEN_PASSWORD); // throws exception if wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void verifiesHashedLinuxPasswordWithRandomSalt() {
|
|
||||||
final var hash = HashGenerator.using(LINUX_SHA512).withRandomSalt().hash(GIVEN_PASSWORD);
|
|
||||||
LinuxEtcShadowHashGenerator.verify(hash, GIVEN_PASSWORD); // throws exception if wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void verifiesLinuxHashedPasswordWithGivenSalt() {
|
|
||||||
final var hash = HashGenerator.using(LINUX_SHA512).withSalt(GIVEN_SALT).hash(GIVEN_PASSWORD);
|
|
||||||
LinuxEtcShadowHashGenerator.verify(hash, GIVEN_PASSWORD); // throws exception if wrong
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void throwsExceptionForInvalidLinuxPassword() {
|
|
||||||
final var hash = HashGenerator.using(LINUX_SHA512).withRandomSalt().hash(GIVEN_PASSWORD);
|
|
||||||
final var throwable = catchThrowable(() ->
|
|
||||||
LinuxEtcShadowHashGenerator.verify(hash, WRONG_PASSWORD)
|
|
||||||
);
|
|
||||||
assertThat(throwable).hasMessage("invalid password");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generatesMySqlNativePasswordHash() {
|
|
||||||
final var hash = HashGenerator.using(MYSQL_NATIVE).hash("Test1234");
|
|
||||||
assertThat(hash).isEqualTo("*14F1A8C42F8B6D4662BB3ED290FD37BF135FE45C");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void generatePostgreSqlScramPasswordHash() {
|
|
||||||
// given the same salt, extracted from the hash as generated by PostgreSQL
|
|
||||||
final var postgresBase64Salt = Base64.getDecoder().decode(GIVEN_POSTGRESQL_GENERATED_SCRAM_SHA256_HASH.split("\\$")[1].split(":")[1]);
|
|
||||||
|
|
||||||
// when the hash is re-generated via Java
|
|
||||||
final var hash = HashGenerator.using(SCRAM_SHA256).withSalt(new String(postgresBase64Salt, Charset.forName("latin1"))).hash(GIVEN_PASSWORD);
|
|
||||||
|
|
||||||
// then we are getting the same hash
|
|
||||||
assertThat(hash).isEqualTo(GIVEN_POSTGRESQL_GENERATED_SCRAM_SHA256_HASH);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,51 @@
|
|||||||
|
package net.hostsharing.hsadminng.hash;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm.SHA512;
|
||||||
|
import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash;
|
||||||
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
|
import static org.assertj.core.api.Assertions.catchThrowable;
|
||||||
|
|
||||||
|
class LinuxEtcShadowHashGeneratorUnitTest {
|
||||||
|
|
||||||
|
final String GIVEN_PASSWORD = "given password";
|
||||||
|
final String WRONG_PASSWORD = "wrong password";
|
||||||
|
final String GIVEN_SALT = "0123456789abcdef";
|
||||||
|
|
||||||
|
// generated via mkpasswd for plaintext password GIVEN_PASSWORD (see above)
|
||||||
|
final String GIVEN_SHA512_HASH = "$6$ooei1HK6JXVaI7KC$sY5d9fEOr36hjh4CYwIKLMfRKL1539bEmbVCZ.zPiH0sv7jJVnoIXb5YEefEtoSM2WWgDi9hr7vXRe3Nw8zJP/";
|
||||||
|
final String GIVEN_YESCRYPT_HASH = "$y$j9T$wgYACPmBXvlMg2MzeZA0p1$KXUzd28nG.67GhPnBZ3aZsNNA5bWFdL/dyG4wS0iRw7";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifiesPasswordAgainstSha512HashFromMkpasswd() {
|
||||||
|
hash(GIVEN_PASSWORD).verify(GIVEN_SHA512_HASH); // throws exception if wrong
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifiesPasswordAgainstYescryptHashFromMkpasswd() {
|
||||||
|
hash(GIVEN_PASSWORD).verify(GIVEN_YESCRYPT_HASH); // throws exception if wrong
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifiesHashedPasswordWithRandomSalt() {
|
||||||
|
final var hash = hash(GIVEN_PASSWORD).using(SHA512).withRandomSalt().generate();
|
||||||
|
hash(GIVEN_PASSWORD).verify(hash); // throws exception if wrong
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void verifiesHashedPasswordWithGivenSalt() {
|
||||||
|
final var givenPasswordHash =hash(GIVEN_PASSWORD).using(SHA512).withSalt(GIVEN_SALT).generate();
|
||||||
|
hash(GIVEN_PASSWORD).verify(givenPasswordHash); // throws exception if wrong
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void throwsExceptionForInvalidPassword() {
|
||||||
|
final var givenPasswordHash = hash(GIVEN_PASSWORD).using(SHA512).withRandomSalt().generate();
|
||||||
|
|
||||||
|
final var throwable = catchThrowable(() ->
|
||||||
|
hash(WRONG_PASSWORD).verify(givenPasswordHash) // throws exception if wrong);
|
||||||
|
);
|
||||||
|
assertThat(throwable).hasMessage("invalid password");
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +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.HashGenerator;
|
import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator;
|
||||||
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;
|
||||||
@ -537,7 +537,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
.identifier("fir01-temp")
|
.identifier("fir01-temp")
|
||||||
.caption("some test-unix-user")
|
.caption("some test-unix-user")
|
||||||
.build());
|
.build());
|
||||||
HashGenerator.nextSalt("Jr5w/Y8zo8pCkqg7");
|
LinuxEtcShadowHashGenerator.nextSalt("Jr5w/Y8zo8pCkqg7");
|
||||||
|
|
||||||
RestAssured // @formatter:off
|
RestAssured // @formatter:off
|
||||||
.given()
|
.given()
|
||||||
|
@ -30,7 +30,6 @@ import java.util.Map;
|
|||||||
import static java.util.Map.entry;
|
import static java.util.Map.entry;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_CLOUD_SERVER_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM;
|
import static net.hostsharing.hsadminng.hs.booking.item.TestHsBookingItem.TEST_MANAGED_SERVER_BOOKING_ITEM;
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
||||||
import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT;
|
import static net.hostsharing.hsadminng.hs.office.contact.TestHsOfficeContact.TEST_CONTACT;
|
||||||
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
import static net.hostsharing.hsadminng.rbac.test.JsonMatcher.lenientlyEquals;
|
||||||
@ -364,130 +363,6 @@ public class HsHostingAssetControllerRestTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
"""),
|
|
||||||
MARIADB_INSTANCE(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.MARIADB_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|MariaDB.default")
|
|
||||||
.caption("some fake MariaDB instance")
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "MARIADB_INSTANCE",
|
|
||||||
"identifier": "vm1234|MariaDB.default",
|
|
||||||
"caption": "some fake MariaDB instance",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""),
|
|
||||||
MARIADB_USER(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.MARIADB_USER)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some fake MariaDB user")
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "MARIADB_USER",
|
|
||||||
"identifier": "xyz00_temp",
|
|
||||||
"caption": "some fake MariaDB user",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""),
|
|
||||||
MARIADB_DATABASE(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.MARIADB_DATABASE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some fake MariaDB database")
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("encoding", "latin1"),
|
|
||||||
entry("collation", "latin2")
|
|
||||||
))
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "MARIADB_DATABASE",
|
|
||||||
"identifier": "xyz00_temp",
|
|
||||||
"caption": "some fake MariaDB database",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {
|
|
||||||
"encoding": "latin1",
|
|
||||||
"collation": "latin2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""),
|
|
||||||
PGSQL_INSTANCE(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.PGSQL_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|PgSql.default")
|
|
||||||
.caption("some fake PgSql instance")
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "PGSQL_INSTANCE",
|
|
||||||
"identifier": "vm1234|PgSql.default",
|
|
||||||
"caption": "some fake PgSql instance",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""),
|
|
||||||
PGSQL_USER(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.PGSQL_USER)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some fake PgSql user")
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "PGSQL_USER",
|
|
||||||
"identifier": "xyz00_temp",
|
|
||||||
"caption": "some fake PgSql user",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
"""),
|
|
||||||
PGSQL_DATABASE(
|
|
||||||
List.of(
|
|
||||||
HsHostingAssetEntity.builder()
|
|
||||||
.type(HsHostingAssetType.PGSQL_DATABASE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some fake PgSql database")
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("encoding", "latin1"),
|
|
||||||
entry("collation", "latin2")
|
|
||||||
))
|
|
||||||
.build()),
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"type": "PGSQL_DATABASE",
|
|
||||||
"identifier": "xyz00_temp",
|
|
||||||
"caption": "some fake PgSql database",
|
|
||||||
"alarmContact": null,
|
|
||||||
"config": {
|
|
||||||
"encoding": "latin1",
|
|
||||||
"collation": "latin2"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
""");
|
""");
|
||||||
|
|
||||||
final HsHostingAssetType assetType;
|
final HsHostingAssetType assetType;
|
||||||
|
@ -41,13 +41,7 @@ class HsHostingAssetPropsControllerAcceptanceTest {
|
|||||||
"DOMAIN_HTTP_SETUP",
|
"DOMAIN_HTTP_SETUP",
|
||||||
"DOMAIN_SMTP_SETUP",
|
"DOMAIN_SMTP_SETUP",
|
||||||
"DOMAIN_MBOX_SETUP",
|
"DOMAIN_MBOX_SETUP",
|
||||||
"EMAIL_ADDRESS",
|
"EMAIL_ADDRESS"
|
||||||
"MARIADB_INSTANCE",
|
|
||||||
"MARIADB_USER",
|
|
||||||
"MARIADB_DATABASE",
|
|
||||||
"PGSQL_INSTANCE",
|
|
||||||
"PGSQL_USER",
|
|
||||||
"PGSQL_DATABASE"
|
|
||||||
]
|
]
|
||||||
"""));
|
"""));
|
||||||
// @formatter:on
|
// @formatter:on
|
||||||
|
@ -245,9 +245,7 @@ class HsHostingAssetRepositoryIntegrationTest extends ContextBasedTestWithCleanu
|
|||||||
// then
|
// then
|
||||||
exactlyTheseAssetsAreReturned(
|
exactlyTheseAssetsAreReturned(
|
||||||
result,
|
result,
|
||||||
"HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)",
|
"HsHostingAssetEntity(MANAGED_WEBSPACE, sec01, some Webspace, MANAGED_SERVER:vm1012, D-1000212:D-1000212 default project:separate ManagedWebspace)");
|
||||||
"HsHostingAssetEntity(MARIADB_INSTANCE, vm1012.MariaDB.default, some default MariaDB instance, MANAGED_SERVER:vm1012)",
|
|
||||||
"HsHostingAssetEntity(PGSQL_INSTANCE, vm1012.Postgresql.default, some default Postgresql instance, MANAGED_SERVER:vm1012)");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,9 +129,10 @@ class HsHostingAssetTypeUnitTest {
|
|||||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||||
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
|
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
|
||||||
HA_MARIADB_USER *==> HA_MANAGED_WEBSPACE
|
HA_MARIADB_USER *==> HA_MARIADB_INSTANCE
|
||||||
HA_MARIADB_USER o..> HA_MARIADB_INSTANCE
|
HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE
|
||||||
HA_MARIADB_DATABASE *==> HA_MARIADB_USER
|
HA_MARIADB_DATABASE *==> HA_MANAGED_WEBSPACE
|
||||||
|
HA_MARIADB_DATABASE o..> HA_MARIADB_INSTANCE
|
||||||
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
||||||
@ -190,9 +191,10 @@ class HsHostingAssetTypeUnitTest {
|
|||||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||||
HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER
|
HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER
|
||||||
HA_PGSQL_USER *==> HA_MANAGED_WEBSPACE
|
HA_PGSQL_USER *==> HA_PGSQL_INSTANCE
|
||||||
HA_PGSQL_USER o..> HA_PGSQL_INSTANCE
|
HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE
|
||||||
HA_PGSQL_DATABASE *==> HA_PGSQL_USER
|
HA_PGSQL_DATABASE *==> HA_MANAGED_WEBSPACE
|
||||||
|
HA_PGSQL_DATABASE o..> HA_PGSQL_INSTANCE
|
||||||
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
||||||
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
||||||
|
@ -39,13 +39,7 @@ class HostingAssetEntityValidatorRegistryUnitTest {
|
|||||||
HsHostingAssetType.DOMAIN_HTTP_SETUP,
|
HsHostingAssetType.DOMAIN_HTTP_SETUP,
|
||||||
HsHostingAssetType.DOMAIN_SMTP_SETUP,
|
HsHostingAssetType.DOMAIN_SMTP_SETUP,
|
||||||
HsHostingAssetType.DOMAIN_MBOX_SETUP,
|
HsHostingAssetType.DOMAIN_MBOX_SETUP,
|
||||||
HsHostingAssetType.EMAIL_ADDRESS,
|
HsHostingAssetType.EMAIL_ADDRESS
|
||||||
HsHostingAssetType.MARIADB_INSTANCE,
|
|
||||||
HsHostingAssetType.MARIADB_USER,
|
|
||||||
HsHostingAssetType.MARIADB_DATABASE,
|
|
||||||
HsHostingAssetType.PGSQL_INSTANCE,
|
|
||||||
HsHostingAssetType.PGSQL_USER,
|
|
||||||
HsHostingAssetType.PGSQL_DATABASE
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,116 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static java.util.Map.ofEntries;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsMariaDbDatabaseHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_MARIADB_INSTANCE = HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|MariaDB.default")
|
|
||||||
.caption("some valid test MariaDB-Instance")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_MARIADB_USER = HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_USER)
|
|
||||||
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
|
||||||
.assignedToAsset(GIVEN_MARIADB_INSTANCE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test MariaDB-User")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("password", "Hallo Datenbank, lass mich rein!")
|
|
||||||
)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static HsHostingAssetEntityBuilder givenValidMariaDbDatabaseBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_DATABASE)
|
|
||||||
.parentAsset(GIVEN_MARIADB_USER)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test MariaDB-Database")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("encoding", "latin1")
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void describesItsProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidMariaDbDatabaseBuilder().build().getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var props = validator.properties();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
|
|
||||||
"{type=string, propertyName=encoding, matchesRegEx=[[a-z0-9_]+], maxLength=24, provided=[latin1, utf8], defaultValue=utf8}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = Stream.concat(
|
|
||||||
validator.validateEntity(givenMariaDbUserHostingAsset).stream(),
|
|
||||||
validator.validateContext(givenMariaDbUserHostingAsset).stream()
|
|
||||||
).toList();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder()
|
|
||||||
.config(ofEntries(
|
|
||||||
entry("unknown", "wrong"),
|
|
||||||
entry("encoding", 10)
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'",
|
|
||||||
"'MARIADB_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbDatabaseBuilder()
|
|
||||||
.identifier("xyz99-temp")
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsMariaDbInstanceHostingAssetValidator.DEFAULT_INSTANCE_IDENTIFIER_SUFFIX;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsMariaDbInstanceHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
static HsHostingAssetEntityBuilder validEntityBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier(TEST_MANAGED_SERVER_HOSTING_ASSET.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void containsExpectedProperties() {
|
|
||||||
// when
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SMTP_SETUP);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(validator.properties()).map(Map::toString).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void preprocessesTakesIdentifierFromParent() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().build();
|
|
||||||
assertThat(givenEntity.getParentAsset().getIdentifier()).as("precondition failed").isEqualTo("vm1234");
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
validator.preprocessEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(givenEntity.getIdentifier()).isEqualTo("vm1234|MariaDB.default");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void acceptsValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().identifier("example.org").build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^\\Qvm1234|MariaDB.default\\E$', but is 'example.org'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidReferencedEntities() {
|
|
||||||
// given
|
|
||||||
final var mangedServerHostingAssetEntity = validEntityBuilder()
|
|
||||||
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
|
||||||
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build())
|
|
||||||
.assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build())
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(mangedServerHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.bookingItem' must be null but is of type CLOUD_SERVER",
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.parentAsset' must be of type MANAGED_SERVER but is of type MANAGED_WEBSPACE",
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.assignedToAsset' must be null but is of type MANAGED_WEBSPACE");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var mangedServerHostingAssetEntity = validEntityBuilder()
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("any", "false")
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(mangedServerHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.config.any' is not expected but is set to 'false'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static java.util.Map.ofEntries;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsMariaDbUserHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_MARIADB_INSTANCE = HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|MariaDB.default")
|
|
||||||
.caption("some valid test MariaDB-Instance")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static HsHostingAssetEntityBuilder givenValidMariaDbUserBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_USER)
|
|
||||||
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
|
||||||
.assignedToAsset(GIVEN_MARIADB_INSTANCE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test MariaDB-User")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("password", "Test1234")
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void describesItsProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidMariaDbUserBuilder().build().getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var props = validator.properties();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
|
|
||||||
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=MYSQL_NATIVE, undisclosed=true}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void preparesEntity() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
// HashGenerator.nextSalt("Ly3LbsArtL5u4EVt"); // not needed for mysql_native_password
|
|
||||||
validator.prepareProperties(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries(
|
|
||||||
entry("password", "*14F1A8C42F8B6D4662BB3ED290FD37BF135FE45C")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = Stream.concat(
|
|
||||||
validator.validateEntity(givenMariaDbUserHostingAsset).stream(),
|
|
||||||
validator.validateContext(givenMariaDbUserHostingAsset).stream()
|
|
||||||
).toList();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder()
|
|
||||||
.config(ofEntries(
|
|
||||||
entry("unknown", 100),
|
|
||||||
entry("password", "short")
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_USER:xyz00_temp.config.unknown' is not expected but is set to '100'",
|
|
||||||
"'MARIADB_USER:xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5",
|
|
||||||
"'MARIADB_USER:xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder()
|
|
||||||
.identifier("xyz99-temp")
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,138 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static java.util.Map.ofEntries;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsPostgreSqlDatabaseHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_PGSQL_INSTANCE = HsHostingAssetEntity.builder()
|
|
||||||
.type(PGSQL_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|PgSql.default")
|
|
||||||
.caption("some valid test PgSql-Instance")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_PGSQL_USER = HsHostingAssetEntity.builder()
|
|
||||||
.type(PGSQL_USER)
|
|
||||||
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
|
||||||
.assignedToAsset(GIVEN_PGSQL_INSTANCE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test PgSql-User")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("password", "Hallo Datenbank, lass mich rein!")
|
|
||||||
)))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static HsHostingAssetEntityBuilder givenValidPgSqlDatabaseBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(PGSQL_DATABASE)
|
|
||||||
.parentAsset(GIVEN_PGSQL_USER)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test PgSql-Database")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("encoding", "LATIN1")
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void describesItsProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidPgSqlDatabaseBuilder().build().getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var props = validator.properties();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
|
|
||||||
"{type=string, propertyName=encoding, matchesRegEx=[[A-Z0-9_]+], maxLength=24, provided=[LATIN1, UTF8], defaultValue=UTF8}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenPgSqlUserHostingAsset = givenValidPgSqlDatabaseBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenPgSqlUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = Stream.concat(
|
|
||||||
validator.validateEntity(givenPgSqlUserHostingAsset).stream(),
|
|
||||||
validator.validateContext(givenPgSqlUserHostingAsset).stream()
|
|
||||||
).toList();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidReferences() {
|
|
||||||
// given
|
|
||||||
final var givenPgSqlUserHostingAsset = givenValidPgSqlDatabaseBuilder()
|
|
||||||
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
|
||||||
.parentAsset(HsHostingAssetEntity.builder().type(PGSQL_INSTANCE).build())
|
|
||||||
.assignedToAsset(HsHostingAssetEntity.builder().type(PGSQL_INSTANCE).build())
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenPgSqlUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenPgSqlUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'",
|
|
||||||
"'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var givenPgSqlUserHostingAsset = givenValidPgSqlDatabaseBuilder()
|
|
||||||
.config(ofEntries(
|
|
||||||
entry("unknown", "wrong"),
|
|
||||||
entry("encoding", 10)
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenPgSqlUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenPgSqlUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'PGSQL_DATABASE:xyz00_temp.config.unknown' is not expected but is set to 'wrong'",
|
|
||||||
"'PGSQL_DATABASE:xyz00_temp.config.encoding' is expected to be of type String, but is of type Integer"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenPgSqlUserHostingAsset = givenValidPgSqlDatabaseBuilder()
|
|
||||||
.identifier("xyz99-temp")
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenPgSqlUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenPgSqlUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,116 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import static java.util.Map.entry;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SMTP_SETUP;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.validators.HsMariaDbInstanceHostingAssetValidator.DEFAULT_INSTANCE_IDENTIFIER_SUFFIX;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsPostgreSqlInstanceHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
static HsHostingAssetEntityBuilder validEntityBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(MARIADB_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier(TEST_MANAGED_SERVER_HOSTING_ASSET.getIdentifier() + DEFAULT_INSTANCE_IDENTIFIER_SUFFIX);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void containsExpectedProperties() {
|
|
||||||
// when
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(DOMAIN_SMTP_SETUP);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(validator.properties()).map(Map::toString).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void preprocessesTakesIdentifierFromParent() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().build();
|
|
||||||
assertThat(givenEntity.getParentAsset().getIdentifier()).as("precondition failed").isEqualTo("vm1234");
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
validator.preprocessEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(givenEntity.getIdentifier()).isEqualTo("vm1234|MariaDB.default");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void acceptsValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenEntity = validEntityBuilder().identifier("example.org").build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^\\Qvm1234|MariaDB.default\\E$', but is 'example.org'"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidReferencedEntities() {
|
|
||||||
// given
|
|
||||||
final var mangedServerHostingAssetEntity = validEntityBuilder()
|
|
||||||
.bookingItem(HsBookingItemEntity.builder().type(HsBookingItemType.CLOUD_SERVER).build())
|
|
||||||
.parentAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build())
|
|
||||||
.assignedToAsset(HsHostingAssetEntity.builder().type(MANAGED_WEBSPACE).build())
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(mangedServerHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.bookingItem' must be null but is of type CLOUD_SERVER",
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.parentAsset' must be of type MANAGED_SERVER but is of type MANAGED_WEBSPACE",
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.assignedToAsset' must be null but is of type MANAGED_WEBSPACE");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var mangedServerHostingAssetEntity = validEntityBuilder()
|
|
||||||
.config(Map.ofEntries(
|
|
||||||
entry("any", "false")
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(mangedServerHostingAssetEntity.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(mangedServerHostingAssetEntity);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'MARIADB_INSTANCE:vm1234|MariaDB.default.config.any' is not expected but is set to 'false'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,125 +0,0 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity.HsHostingAssetEntityBuilder;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
|
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.util.Base64;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.stream.Stream;
|
|
||||||
|
|
||||||
import static java.util.Map.ofEntries;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_INSTANCE;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_USER;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_SERVER_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.TestHsHostingAssetEntities.TEST_MANAGED_WEBSPACE_HOSTING_ASSET;
|
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchMap.entry;
|
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
|
||||||
|
|
||||||
class HsPostgreSqlUserHostingAssetValidatorUnitTest {
|
|
||||||
|
|
||||||
private static final HsHostingAssetEntity GIVEN_PGSQL_INSTANCE = HsHostingAssetEntity.builder()
|
|
||||||
.type(PGSQL_INSTANCE)
|
|
||||||
.parentAsset(TEST_MANAGED_SERVER_HOSTING_ASSET)
|
|
||||||
.identifier("vm1234|PgSql.default")
|
|
||||||
.caption("some valid test PgSql-Instance")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
private static HsHostingAssetEntityBuilder givenValidMariaDbUserBuilder() {
|
|
||||||
return HsHostingAssetEntity.builder()
|
|
||||||
.type(PGSQL_USER)
|
|
||||||
.parentAsset(TEST_MANAGED_WEBSPACE_HOSTING_ASSET)
|
|
||||||
.assignedToAsset(GIVEN_PGSQL_INSTANCE)
|
|
||||||
.identifier("xyz00_temp")
|
|
||||||
.caption("some valid test PgSql-User")
|
|
||||||
.config(new HashMap<>(ofEntries(
|
|
||||||
entry("password", "Test1234")
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void describesItsProperties() {
|
|
||||||
// given
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenValidMariaDbUserBuilder().build().getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var props = validator.properties();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(props).extracting(Object::toString).containsExactlyInAnyOrder(
|
|
||||||
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SCRAM_SHA256, undisclosed=true}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void preparesEntity() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
HashGenerator.nextSalt(new String(Base64.getDecoder().decode("L1QxSVNyTU81b3NZS1djNg=="), Charset.forName("latin1")));
|
|
||||||
validator.prepareProperties(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(givenMariaDbUserHostingAsset.getConfig()).containsExactlyInAnyOrderEntriesOf(ofEntries(
|
|
||||||
entry("password", "SCRAM-SHA-256$4096:L1QxSVNyTU81b3NZS1djNg==$bB4PEqHpnkoB9FwYfOjh+8yJvLsCnrwxom3TGK0CVJM=:ACRgTfhJwIZLrzhVRbJ3Qif5YhErYWAfkBThvtouW+8=")
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void validatesValidEntity() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder().build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = Stream.concat(
|
|
||||||
validator.validateEntity(givenMariaDbUserHostingAsset).stream(),
|
|
||||||
validator.validateContext(givenMariaDbUserHostingAsset).stream()
|
|
||||||
).toList();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidProperties() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder()
|
|
||||||
.config(ofEntries(
|
|
||||||
entry("unknown", 100),
|
|
||||||
entry("password", "short")
|
|
||||||
))
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactlyInAnyOrder(
|
|
||||||
"'PGSQL_USER:xyz00_temp.config.unknown' is not expected but is set to '100'",
|
|
||||||
"'PGSQL_USER:xyz00_temp.config.password' length is expected to be at min 8 but length of provided value is 5",
|
|
||||||
"'PGSQL_USER:xyz00_temp.config.password' must contain at least one character of at least 3 of the following groups: upper case letters, lower case letters, digits, special characters"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
void rejectsInvalidIdentifier() {
|
|
||||||
// given
|
|
||||||
final var givenMariaDbUserHostingAsset = givenValidMariaDbUserBuilder()
|
|
||||||
.identifier("xyz99-temp")
|
|
||||||
.build();
|
|
||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(givenMariaDbUserHostingAsset.getType());
|
|
||||||
|
|
||||||
// when
|
|
||||||
final var result = validator.validateEntity(givenMariaDbUserHostingAsset);
|
|
||||||
|
|
||||||
// then
|
|
||||||
assertThat(result).containsExactly(
|
|
||||||
"'identifier' expected to match '^xyz00$|^xyz00_[a-z0-9_]+$', but is 'xyz99-temp'");
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,6 +1,6 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.HashGenerator;
|
import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
@ -50,7 +50,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
HashGenerator.nextSalt("Ly3LbsArtL5u4EVt");
|
LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt");
|
||||||
validator.prepareProperties(unixUserHostingAsset);
|
validator.prepareProperties(unixUserHostingAsset);
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -141,7 +141,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
|||||||
final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
final var validator = HostingAssetEntityValidatorRegistry.forType(unixUserHostingAsset.getType());
|
||||||
|
|
||||||
// when
|
// when
|
||||||
HashGenerator.nextSalt("Ly3LbsArtL5u4EVt");
|
LinuxEtcShadowHashGenerator.nextSalt("Ly3LbsArtL5u4EVt");
|
||||||
final var result = validator.revampProperties(unixUserHostingAsset, unixUserHostingAsset.getConfig());
|
final var result = validator.revampProperties(unixUserHostingAsset, unixUserHostingAsset.getConfig());
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -169,7 +169,7 @@ class HsUnixUserHostingAssetValidatorUnitTest {
|
|||||||
"{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}",
|
"{type=enumeration, propertyName=shell, values=[/bin/false, /bin/bash, /bin/csh, /bin/dash, /usr/bin/tcsh, /usr/bin/zsh, /usr/bin/passwd], defaultValue=/bin/false}",
|
||||||
"{type=string, propertyName=homedir, readOnly=true, computed=true}",
|
"{type=string, propertyName=homedir, readOnly=true, computed=true}",
|
||||||
"{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}",
|
"{type=string, propertyName=totpKey, matchesRegEx=[^0x([0-9A-Fa-f]{2})+$], minLength=20, maxLength=256, writeOnly=true, undisclosed=true}",
|
||||||
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=LINUX_SHA512, undisclosed=true}"
|
"{type=password, propertyName=password, minLength=8, maxLength=40, writeOnly=true, computed=true, hashedUsing=SHA512, undisclosed=true}"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -131,7 +131,6 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
|
|
||||||
// at least as the number of lines in business-partners.csv from test-data, but less than real data partner count
|
// at least as the number of lines in business-partners.csv from test-data, but less than real data partner count
|
||||||
public static final int MAX_NUMBER_OF_TEST_DATA_PARTNERS = 100;
|
public static final int MAX_NUMBER_OF_TEST_DATA_PARTNERS = 100;
|
||||||
public static final String MIGRATION_DATA_PATH = ofNullable(System.getenv("HSADMINNG_MIGRATION_DATA_PATH")).orElse("migration") + "/";
|
|
||||||
|
|
||||||
static int relationId = 2000000;
|
static int relationId = 2000000;
|
||||||
|
|
||||||
@ -183,7 +182,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
@Order(1010)
|
@Order(1010)
|
||||||
void importBusinessPartners() {
|
void importBusinessPartners() {
|
||||||
|
|
||||||
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "business-partners.csv")) {
|
try (Reader reader = resourceReader("migration.data/business-partners.csv")) {
|
||||||
final var lines = readAllLines(reader);
|
final var lines = readAllLines(reader);
|
||||||
importBusinessPartners(justHeader(lines), withoutHeader(lines));
|
importBusinessPartners(justHeader(lines), withoutHeader(lines));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -229,7 +228,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
@Order(1020)
|
@Order(1020)
|
||||||
void importContacts() {
|
void importContacts() {
|
||||||
|
|
||||||
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "contacts.csv")) {
|
try (Reader reader = resourceReader("migration.data/contacts.csv")) {
|
||||||
final var lines = readAllLines(reader);
|
final var lines = readAllLines(reader);
|
||||||
importContacts(justHeader(lines), withoutHeader(lines));
|
importContacts(justHeader(lines), withoutHeader(lines));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -329,7 +328,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
@Order(1030)
|
@Order(1030)
|
||||||
void importSepaMandates() {
|
void importSepaMandates() {
|
||||||
|
|
||||||
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "sepa-mandates.csv")) {
|
try (Reader reader = resourceReader("migration.data/sepa-mandates.csv")) {
|
||||||
final var lines = readAllLines(reader);
|
final var lines = readAllLines(reader);
|
||||||
importSepaMandates(justHeader(lines), withoutHeader(lines));
|
importSepaMandates(justHeader(lines), withoutHeader(lines));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -361,7 +360,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
@Test
|
@Test
|
||||||
@Order(1040)
|
@Order(1040)
|
||||||
void importCoopShares() {
|
void importCoopShares() {
|
||||||
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "share-transactions.csv")) {
|
try (Reader reader = resourceReader("migration.data/share-transactions.csv")) {
|
||||||
final var lines = readAllLines(reader);
|
final var lines = readAllLines(reader);
|
||||||
importCoopShares(justHeader(lines), withoutHeader(lines));
|
importCoopShares(justHeader(lines), withoutHeader(lines));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
@ -388,7 +387,7 @@ public class ImportOfficeData extends ContextBasedTest {
|
|||||||
@Order(1050)
|
@Order(1050)
|
||||||
void importCoopAssets() {
|
void importCoopAssets() {
|
||||||
|
|
||||||
try (Reader reader = resourceReader(MIGRATION_DATA_PATH + "asset-transactions.csv")) {
|
try (Reader reader = resourceReader("migration.data/asset-transactions.csv")) {
|
||||||
final var lines = readAllLines(reader);
|
final var lines = readAllLines(reader);
|
||||||
importCoopAssets(justHeader(lines), withoutHeader(lines));
|
importCoopAssets(justHeader(lines), withoutHeader(lines));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package net.hostsharing.hsadminng.hs.validation;
|
package net.hostsharing.hsadminng.hs.validation;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.junit.jupiter.params.ParameterizedTest;
|
import org.junit.jupiter.params.ParameterizedTest;
|
||||||
import org.junit.jupiter.params.provider.ValueSource;
|
import org.junit.jupiter.params.provider.ValueSource;
|
||||||
@ -9,7 +8,8 @@ import java.util.ArrayList;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hash.HashGenerator.Algorithm.LINUX_SHA512;
|
import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.Algorithm.SHA512;
|
||||||
|
import static net.hostsharing.hsadminng.hash.LinuxEtcShadowHashGenerator.hash;
|
||||||
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
|
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
|
||||||
import static net.hostsharing.hsadminng.mapper.PatchableMapWrapper.entry;
|
import static net.hostsharing.hsadminng.mapper.PatchableMapWrapper.entry;
|
||||||
import static org.assertj.core.api.Assertions.assertThat;
|
import static org.assertj.core.api.Assertions.assertThat;
|
||||||
@ -17,7 +17,7 @@ import static org.assertj.core.api.Assertions.assertThat;
|
|||||||
class PasswordPropertyUnitTest {
|
class PasswordPropertyUnitTest {
|
||||||
|
|
||||||
private final ValidatableProperty<PasswordProperty, String> passwordProp =
|
private final ValidatableProperty<PasswordProperty, String> passwordProp =
|
||||||
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(LINUX_SHA512).writeOnly();
|
passwordProperty("password").minLength(8).maxLength(40).hashedUsing(SHA512).writeOnly();
|
||||||
private final List<String> violations = new ArrayList<>();
|
private final List<String> violations = new ArrayList<>();
|
||||||
|
|
||||||
@ParameterizedTest
|
@ParameterizedTest
|
||||||
@ -115,6 +115,6 @@ class PasswordPropertyUnitTest {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// then
|
// then
|
||||||
LinuxEtcShadowHashGenerator.verify(result, "some password"); // throws exception if wrong
|
hash("some password").using(SHA512).withRandomSalt().generate(); // throws exception if wrong
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user