1
0

import-database-users-and-databases (#82)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/82
Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-08-02 10:40:15 +02:00
parent d6a0511d98
commit e4e1216a85
19 changed files with 388 additions and 41 deletions

View File

@ -27,6 +27,7 @@ public final class HashGenerator {
"abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789/.";
private static boolean couldBeHashEnabled; // TODO.impl: remove after legacy data is migrated
public enum Algorithm {
LINUX_SHA512(LinuxEtcShadowHashGenerator::hash, "6"),
@ -59,6 +60,14 @@ public final class HashGenerator {
this.algorithm = algorithm;
}
public static void enableChouldBeHash(final boolean enable) {
couldBeHashEnabled = enable;
}
public boolean couldBeHash(final String value) {
return couldBeHashEnabled && value.startsWith(algorithm.prefix);
}
public String hash(final String plaintextPassword) {
if (plaintextPassword == null) {
throw new IllegalStateException("no password given");
@ -67,6 +76,12 @@ public final class HashGenerator {
return algorithm.implementation.apply(this, plaintextPassword);
}
public String hashIfNotYetHashed(final String plaintextPasswordOrHash) {
return couldBeHash(plaintextPasswordOrHash)
? plaintextPasswordOrHash
: hash(plaintextPasswordOrHash);
}
public static void nextSalt(final String salt) {
predefinedSalts.add(salt);
}

View File

@ -50,6 +50,7 @@ public enum HsHostingAssetType implements Node {
inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)),
// TODO.spec: do we really want to keep email aliases or migrate to unix users with .forward?
EMAIL_ALIAS( // named e.g. xyz00-abc
inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)),

View File

@ -9,6 +9,8 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator {
final static String HEAD_REGEXP = "^MAD\\|";
public HsMariaDbDatabaseHostingAssetValidator() {
super(
MARIADB_DATABASE,
@ -20,6 +22,6 @@ class HsMariaDbDatabaseHostingAssetValidator extends HostingAssetEntityValidator
@Override
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$");
}
}

View File

@ -10,6 +10,8 @@ import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordP
class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator {
final static String HEAD_REGEXP = "^MAU\\|";
public HsMariaDbUserHostingAssetValidator() {
super(
MARIADB_USER,
@ -28,6 +30,6 @@ class HsMariaDbUserHostingAssetValidator extends HostingAssetEntityValidator {
@Override
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$");
}
}

View File

@ -9,6 +9,8 @@ import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringPrope
class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValidator {
final static String HEAD_REGEXP = "^PGD\\|";
public HsPostgreSqlDatabaseHostingAssetValidator() {
super(
PGSQL_DATABASE,
@ -23,6 +25,6 @@ class HsPostgreSqlDatabaseHostingAssetValidator extends HostingAssetEntityValida
@Override
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$");
}
}

View File

@ -5,7 +5,7 @@ import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import java.util.regex.Pattern;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_DATABASE;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.PGSQL_INSTANCE;
class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityValidator {
@ -13,7 +13,7 @@ class HsPostgreSqlDbInstanceHostingAssetValidator extends HostingAssetEntityVali
public HsPostgreSqlDbInstanceHostingAssetValidator() {
super(
PGSQL_DATABASE,
PGSQL_INSTANCE,
AlarmContact.isOptional(),
// TODO.spec: PostgreSQL extensions in database and here? also decide which. Free selection or booleans/checkboxes?

View File

@ -10,6 +10,8 @@ import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordP
class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator {
final static String HEAD_REGEXP = "^PGU\\|";
public HsPostgreSqlUserHostingAssetValidator() {
super(
PGSQL_USER,
@ -28,6 +30,6 @@ class HsPostgreSqlUserHostingAssetValidator extends HostingAssetEntityValidator
@Override
protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"_[a-z0-9_]+$");
return Pattern.compile(HEAD_REGEXP+webspaceIdentifier+"$|"+HEAD_REGEXP+webspaceIdentifier+"_[a-zA-Z0-9_]+$");
}
}

View File

@ -31,6 +31,12 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
@Override
protected void validate(final List<String> result, final String propValue, final PropertiesProvider propProvider) {
// TODO.impl: remove after legacy data is migrated
if (HashGenerator.using(hashedUsing).couldBeHash(propValue) && propValue.length() > this.maxLength()) {
// already hashed => do not validate
return;
}
super.validate(result, propValue, propProvider);
validatePassword(result, propValue);
}
@ -40,7 +46,7 @@ public class PasswordProperty extends StringProperty<PasswordProperty> {
computedBy(
ComputeMode.IN_PREP,
(em, entity) -> ofNullable(entity.getDirectValue(propertyName, String.class))
.map(password -> HashGenerator.using(algorithm).withRandomSalt().hash(password))
.map(password -> HashGenerator.using(algorithm).withRandomSalt().hashIfNotYetHashed(password))
.orElse(null));
return self();
}

View File

@ -41,11 +41,19 @@ public class StringProperty<P extends StringProperty<P>> extends ValidatableProp
return self();
}
public Integer minLength() {
return this.minLength;
}
public P maxLength(final int maxLength) {
this.maxLength = maxLength;
return self();
}
public Integer maxLength() {
return this.maxLength;
}
public P matchesRegEx(final String... regExPattern) {
this.matchesRegEx = stream(regExPattern).map(Pattern::compile).toArray(Pattern[]::new);
return self();