HsUnixUserHostingAssetValidator.java

package net.hostsharing.hsadminng.hs.hosting.asset.validators;

import net.hostsharing.hsadminng.hash.HashGenerator;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import net.hostsharing.hsadminng.hs.validation.PropertiesProvider;

import jakarta.persistence.EntityManager;
import java.util.regex.Pattern;

import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
import static net.hostsharing.hsadminng.hs.validation.PasswordProperty.passwordProperty;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;

class HsUnixUserHostingAssetValidator extends HostingAssetEntityValidator {

    private static final int DASH_LENGTH = "-".length();

    HsUnixUserHostingAssetValidator() {
        super(
                HsHostingAssetType.UNIX_USER,
                AlarmContact.isOptional(),

                booleanProperty("locked").readOnly(),
                integerProperty("userid").readOnly().initializedBy(HsUnixUserHostingAssetValidator::computeUserId),

                integerProperty("SSD hard quota").unit("MB").maxFrom("SSD").withFactor(1024).optional(),
                integerProperty("SSD soft quota").unit("MB").maxFrom("SSD hard quota").optional(),
                integerProperty("HDD hard quota").unit("MB").maxFrom("HDD").withFactor(1024).optional(),
                integerProperty("HDD soft quota").unit("MB").maxFrom("HDD hard quota").optional(),
                stringProperty("shell")
                        // TODO.spec: do we want to change them all to /usr/bin/, also in import?
                        .provided("/bin/false", "/bin/bash", "/bin/csh", "/bin/dash", "/usr/bin/tcsh", "/usr/bin/zsh", "/usr/bin/passwd")
                        .withDefault("/bin/false"),
                stringProperty("homedir").readOnly().renderedBy(HsUnixUserHostingAssetValidator::computeHomedir),
                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());
                // TODO.spec: public SSH keys? (only if hsadmin-ng is only accessible with 2FA)
    }

    @Override
    protected Pattern identifierPattern(final HsHostingAsset assetEntity) {
        final var webspaceIdentifier = assetEntity.getParentAsset().getIdentifier();
        return Pattern.compile("^"+webspaceIdentifier+"$|^"+webspaceIdentifier+"-[a-z0-9\\._-]+$");
    }

    private static String computeHomedir(final EntityManager em, final PropertiesProvider propertiesProvider) {
        final var entity = (HsHostingAsset) propertiesProvider;
        final var webspaceName = entity.getParentAsset().getIdentifier();
        return "/home/pacs/" + webspaceName
                + "/users/" + entity.getIdentifier().substring(webspaceName.length()+DASH_LENGTH);
    }

    private static Integer computeUserId(final EntityManager em, final PropertiesProvider propertiesProvider) {
        final Object result = em.createNativeQuery("SELECT nextval('hs_hosting.asset_unixuser_system_id_seq')", Integer.class)
                .getSingleResult();
        return (Integer) result;
    }
}