1
0

add-domain-email-setup-validation (#74)

Co-authored-by: Michael Hoennig <michael@hoennig.de>
Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/74
Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
Michael Hoennig
2024-07-11 10:43:47 +02:00
parent 0af389d7c6
commit 9d2692add3
24 changed files with 653 additions and 78 deletions

View File

@@ -8,9 +8,7 @@ public enum HsBookingItemType implements Node {
PRIVATE_CLOUD,
CLOUD_SERVER(PRIVATE_CLOUD),
MANAGED_SERVER(PRIVATE_CLOUD),
MANAGED_WEBSPACE(MANAGED_SERVER),
DOMAIN_DNS_SETUP, // TODO.spec: experimental
DOMAIN_EMAIL_SUBMISSION_SETUP; // TODO.spec: experimental
MANAGED_WEBSPACE(MANAGED_SERVER);
private final HsBookingItemType parentItemType;

View File

@@ -8,7 +8,7 @@ import java.util.List;
import static java.util.Collections.emptyList;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_MBOX_SETUP;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.EMAIL_ADDRESS;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_DATABASE;
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MARIADB_USER;
@@ -88,7 +88,7 @@ class HsManagedWebspaceBookingItemValidator extends HsBookingItemEntityValidator
return (final HsBookingItemEntity entity, final IntegerProperty prop, final Integer factor) -> {
final var unixUserCount = ofNullable(entity.getRelatedHostingAsset())
.map(ha -> ha.getSubHostingAssets().stream()
.filter(bi -> bi.getType() == DOMAIN_EMAIL_MAILBOX_SETUP)
.filter(bi -> bi.getType() == DOMAIN_MBOX_SETUP)
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
.filter(subAsset -> subAsset.getType()==EMAIL_ADDRESS))
.count())

View File

@@ -45,6 +45,10 @@ public enum HsHostingAssetType implements Node {
inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)),
EMAIL_ALIAS( // named e.g. xyz00-abc
inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)),
DOMAIN_SETUP( // named e.g. example.org
inGroup("Domain"),
optionalParent(SAME_TYPE)
@@ -52,32 +56,29 @@ public enum HsHostingAssetType implements Node {
DOMAIN_DNS_SETUP( // named e.g. example.org
inGroup("Domain"),
requiredParent(DOMAIN_SETUP)),
requiredParent(DOMAIN_SETUP),
assignedTo(MANAGED_WEBSPACE)),
DOMAIN_HTTP_SETUP( // named e.g. example.org
inGroup("Domain"),
requiredParent(DOMAIN_SETUP),
assignedTo(UNIX_USER)),
DOMAIN_EMAIL_SUBMISSION_SETUP( // named e.g. example.org
DOMAIN_SMTP_SETUP( // named e.g. example.org
inGroup("Domain"),
requiredParent(DOMAIN_SETUP),
assignedTo(MANAGED_WEBSPACE)),
DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org
DOMAIN_MBOX_SETUP( // named e.g. example.org
inGroup("Domain"),
requiredParent(DOMAIN_SETUP),
assignedTo(MANAGED_WEBSPACE)),
// TODO.spec: SECURE_MX
EMAIL_ALIAS( // named e.g. xyz00-abc
inGroup("Webspace"),
requiredParent(MANAGED_WEBSPACE)),
EMAIL_ADDRESS( // named e.g. sample@example.org
inGroup("Domain"),
requiredParent(DOMAIN_EMAIL_MAILBOX_SETUP)),
requiredParent(DOMAIN_MBOX_SETUP)),
PGSQL_INSTANCE( // TODO.spec: identifier to be specified
inGroup("PostgreSQL"),

View File

@@ -23,6 +23,9 @@ public class HostingAssetEntityValidatorRegistry {
register(DOMAIN_SETUP, new HsDomainSetupHostingAssetValidator());
register(DOMAIN_DNS_SETUP, new HsDomainDnsSetupHostingAssetValidator());
register(DOMAIN_HTTP_SETUP, new HsDomainHttpSetupHostingAssetValidator());
register(DOMAIN_SMTP_SETUP, new HsDomainSmtpSetupHostingAssetValidator());
register(DOMAIN_MBOX_SETUP, new HsDomainMboxSetupHostingAssetValidator());
register(EMAIL_ADDRESS, new HsEMailAddressHostingAssetValidator());
}
private static void register(final Enum<HsHostingAssetType> type, final HsEntityValidator<HsHostingAssetEntity> validator) {

View File

@@ -60,7 +60,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HostingAssetEntityValidator
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + Pattern.quote(IDENTIFIER_SUFFIX) + "$");
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
}
@Override

View File

@@ -43,7 +43,7 @@ class HsDomainHttpSetupHostingAssetValidator extends HostingAssetEntityValidator
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + Pattern.quote(IDENTIFIER_SUFFIX) + "$");
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + IDENTIFIER_SUFFIX) + "$");
}
@Override

View File

@@ -0,0 +1,34 @@
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.DOMAIN_MBOX_SETUP;
class HsDomainMboxSetupHostingAssetValidator extends HostingAssetEntityValidator {
public static final String IDENTIFIER_SUFFIX = "|MBOX";
HsDomainMboxSetupHostingAssetValidator() {
super(
DOMAIN_MBOX_SETUP,
AlarmContact.isOptional(),
NO_EXTRA_PROPERTIES);
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + 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() + IDENTIFIER_SUFFIX));
}
}
}

View File

@@ -43,7 +43,6 @@ class HsDomainSetupHostingAssetValidator extends HostingAssetEntityValidator {
// - user has Admin/Agent-role for all its sub-domains and the direct parent-Domain which are set up at at Hostsharing
// - domain has DNS zone with TXT record approval
// - parent-domain has DNS zone with TXT record approval
// - dom
//
// TXT-Record check:
// new InitialDirContext().getAttributes("dns:_netblocks.google.com", new String[] { "TXT"}).get("TXT").getAll();

View File

@@ -0,0 +1,34 @@
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.DOMAIN_SMTP_SETUP;
class HsDomainSmtpSetupHostingAssetValidator extends HostingAssetEntityValidator {
public static final String IDENTIFIER_SUFFIX = "|SMTP";
HsDomainSmtpSetupHostingAssetValidator() {
super(
DOMAIN_SMTP_SETUP,
AlarmContact.isOptional(),
NO_EXTRA_PROPERTIES);
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^" + Pattern.quote(assetEntity.getParentAsset().getIdentifier() + 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() + IDENTIFIER_SUFFIX));
}
}
}

View File

@@ -0,0 +1,51 @@
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
import java.util.regex.Pattern;
import static java.util.Optional.ofNullable;
import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf;
import static net.hostsharing.hsadminng.hs.validation.StringProperty.stringProperty;
class HsEMailAddressHostingAssetValidator extends HostingAssetEntityValidator {
private static final String UNIX_USER_REGEX = "^[a-z][a-z0-9]{2}[0-9]{2}(-[a-z0-9]+)?$"; // also accepts legacy pac-names
private static final String EMAIL_ADDRESS_LOCAL_PART_REGEX = "[a-zA-Z0-9_!#$%&'*+/=?`{|}~^.-]+"; // RFC 5322
private static final String EMAIL_ADDRESS_DOMAIN_PART_REGEX = "[a-zA-Z0-9.-]+";
private static final String EMAIL_ADDRESS_FULL_REGEX = "^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "@" + EMAIL_ADDRESS_DOMAIN_PART_REGEX + "$";
public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322
HsEMailAddressHostingAssetValidator() {
super( HsHostingAssetType.EMAIL_ADDRESS,
AlarmContact.isOptional(),
stringProperty("local-part").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").required(),
stringProperty("sub-domain").matchesRegEx("^" + EMAIL_ADDRESS_LOCAL_PART_REGEX + "$").optional(),
arrayOf(
stringProperty("target").maxLength(EMAIL_ADDRESS_MAX_LENGTH).matchesRegEx(UNIX_USER_REGEX, EMAIL_ADDRESS_FULL_REGEX)
).required().minLength(1));
}
@Override
public void preprocessEntity(final HsHostingAssetEntity entity) {
super.preprocessEntity(entity);
super.preprocessEntity(entity);
if (entity.getIdentifier() == null) {
entity.setIdentifier(combineIdentifier(entity));
}
}
@Override
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
return Pattern.compile("^"+ Pattern.quote(combineIdentifier(assetEntity)) + "$");
}
private static String combineIdentifier(final HsHostingAssetEntity emailAddressAssetEntity) {
return emailAddressAssetEntity.getDirectValue("local-part", String.class) +
ofNullable(emailAddressAssetEntity.getDirectValue("sub-domain", String.class)).map(s -> "." + s).orElse("") +
"@" +
emailAddressAssetEntity.getParentAsset().getIdentifier();
}
}

View File

@@ -13,7 +13,8 @@ components:
- DOMAIN_SETUP
- DOMAIN_DNS_SETUP
- DOMAIN_HTTP_SETUP
- DOMAIN_EMAIL_SETUP
- DOMAIN_SMTP_SETUP
- DOMAIN_MBOX_SETUP
- EMAIL_ALIAS
- EMAIL_ADDRESS
- PGSQL_USER

View File

@@ -12,7 +12,8 @@ create type HsHostingAssetType as enum (
'DOMAIN_SETUP',
'DOMAIN_DNS_SETUP',
'DOMAIN_HTTP_SETUP',
'DOMAIN_EMAIL_SETUP',
'DOMAIN_SMTP_SETUP',
'DOMAIN_MBOX_SETUP',
'EMAIL_ALIAS',
'EMAIL_ADDRESS',
'PGSQL_USER',
@@ -64,12 +65,13 @@ begin
when 'MANAGED_SERVER' then null
when 'MANAGED_WEBSPACE' then 'MANAGED_SERVER'
when 'UNIX_USER' then 'MANAGED_WEBSPACE'
when 'EMAIL_ALIAS' then 'MANAGED_WEBSPACE'
when 'DOMAIN_SETUP' then null
when 'DOMAIN_DNS_SETUP' then 'DOMAIN_SETUP'
when 'DOMAIN_HTTP_SETUP' then 'DOMAIN_SETUP'
when 'DOMAIN_EMAIL_SETUP' then 'DOMAIN_SETUP'
when 'EMAIL_ALIAS' then 'MANAGED_WEBSPACE'
when 'EMAIL_ADDRESS' then 'DOMAIN_EMAIL_SETUP'
when 'DOMAIN_SMTP_SETUP' then 'DOMAIN_SETUP'
when 'DOMAIN_MBOX_SETUP' then 'DOMAIN_SETUP'
when 'EMAIL_ADDRESS' then 'DOMAIN_MBOX_SETUP'
when 'PGSQL_USER' then 'MANAGED_WEBSPACE'
when 'PGSQL_DATABASE' then 'MANAGED_WEBSPACE'
when 'MARIADB_USER' then 'MANAGED_WEBSPACE'

View File

@@ -14,16 +14,17 @@ declare
currentTask varchar;
relatedProject hs_booking_project;
relatedDebitor hs_office_debitor;
relatedPrivateCloudBookingItem hs_booking_item;
relatedManagedServerBookingItem hs_booking_item;
relatedCloudServerBookingItem hs_booking_item;
relatedManagedWebspaceBookingItem hs_booking_item;
privateCloudBI hs_booking_item;
managedServerBI hs_booking_item;
cloudServerBI hs_booking_item;
managedWebspaceBI hs_booking_item;
debitorNumberSuffix varchar;
defaultPrefix varchar;
managedServerUuid uuid;
managedWebspaceUuid uuid;
webUnixUserUuid uuid;
domainSetupUuid uuid;
domainMBoxSetupUuid uuid;
begin
currentTask := 'creating hosting-asset test-data ' || givenProjectCaption;
call defineContext(currentTask, null, 'superuser-alex@hostsharing.net', 'global#global:ADMIN');
@@ -39,47 +40,51 @@ begin
where debitor.uuid = relatedProject.debitorUuid;
assert relatedDebitor.uuid is not null, 'relatedDebitor for "' || givenProjectCaption || '" must not be null';
select item.* into relatedPrivateCloudBookingItem
select item.* into privateCloudBI
from hs_booking_item item
where item.projectUuid = relatedProject.uuid
and item.type = 'PRIVATE_CLOUD';
assert relatedPrivateCloudBookingItem.uuid is not null, 'relatedPrivateCloudBookingItem for "' || givenProjectCaption|| '" must not be null';
assert privateCloudBI.uuid is not null, 'relatedPrivateCloudBookingItem for "' || givenProjectCaption|| '" must not be null';
select item.* into relatedManagedServerBookingItem
select item.* into managedServerBI
from hs_booking_item item
where item.projectUuid = relatedProject.uuid
and item.type = 'MANAGED_SERVER';
assert relatedManagedServerBookingItem.uuid is not null, 'relatedManagedServerBookingItem for "' || givenProjectCaption|| '" must not be null';
assert managedServerBI.uuid is not null, 'relatedManagedServerBookingItem for "' || givenProjectCaption|| '" must not be null';
select item.* into relatedCloudServerBookingItem
select item.* into cloudServerBI
from hs_booking_item item
where item.parentItemuuid = relatedPrivateCloudBookingItem.uuid
where item.parentItemuuid = privateCloudBI.uuid
and item.type = 'CLOUD_SERVER';
assert relatedCloudServerBookingItem.uuid is not null, 'relatedCloudServerBookingItem for "' || givenProjectCaption|| '" must not be null';
assert cloudServerBI.uuid is not null, 'relatedCloudServerBookingItem for "' || givenProjectCaption|| '" must not be null';
select item.* into relatedManagedWebspaceBookingItem
select item.* into managedWebspaceBI
from hs_booking_item item
where item.projectUuid = relatedProject.uuid
and item.type = 'MANAGED_WEBSPACE';
assert relatedManagedWebspaceBookingItem.uuid is not null, 'relatedManagedWebspaceBookingItem for "' || givenProjectCaption|| '" must not be null';
assert managedWebspaceBI.uuid is not null, 'relatedManagedWebspaceBookingItem for "' || givenProjectCaption|| '" must not be null';
select uuid_generate_v4() into managedServerUuid;
select uuid_generate_v4() into managedWebspaceUuid;
select uuid_generate_v4() into webUnixUserUuid;
select uuid_generate_v4() into domainSetupUuid;
select uuid_generate_v4() into domainMBoxSetupUuid;
debitorNumberSuffix := relatedDebitor.debitorNumberSuffix;
defaultPrefix := relatedDebitor.defaultPrefix;
insert into hs_hosting_asset
(uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config)
values (managedServerUuid, relatedManagedServerBookingItem.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(), relatedCloudServerBookingItem.uuid, 'CLOUD_SERVER', null, null, 'vm20' || debitorNumberSuffix, 'another CloudServer', '{}'::jsonb),
(managedWebspaceUuid, relatedManagedWebspaceBookingItem.uuid, 'MANAGED_WEBSPACE', managedServerUuid, null, defaultPrefix || '01', 'some Webspace', '{}'::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),
(domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb),
(uuid_generate_v4(), null, 'DOMAIN_DNS_SETUP', domainSetupUuid, null, defaultPrefix || '.example.org|DNS', 'some Domain-DNS-Setup', '{}'::jsonb),
(uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org|HTTP', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb);
(uuid, bookingitemuuid, type, parentAssetUuid, assignedToAssetUuid, identifier, caption, config)
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),
(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),
(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),
(domainSetupUuid, null, 'DOMAIN_SETUP', null, null, defaultPrefix || '.example.org', 'some Domain-Setup', '{}'::jsonb),
(uuid_generate_v4(), null, 'DOMAIN_DNS_SETUP', domainSetupUuid, null, defaultPrefix || '.example.org|DNS', 'some Domain-DNS-Setup', '{}'::jsonb),
(uuid_generate_v4(), null, 'DOMAIN_HTTP_SETUP', domainSetupUuid, webUnixUserUuid, defaultPrefix || '.example.org|HTTP', 'some Domain-HTTP-Setup', '{ "option-htdocsfallback": true, "use-fcgiphpbin": "/usr/lib/cgi-bin/php", "validsubdomainnames": "*"}'::jsonb),
(uuid_generate_v4(), null, 'DOMAIN_SMTP_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-SMPT-Setup', '{}'::jsonb),
(domainMBoxSetupUuid, null, 'DOMAIN_MBOX_SETUP', domainSetupUuid, managedWebspaceUuid, defaultPrefix || '.example.org|DNS', 'some Domain-MBOX-Setup', '{}'::jsonb),
(uuid_generate_v4(), null, 'EMAIL_ADDRESS', domainMBoxSetupUuid, null, 'test@' || defaultPrefix || '.example.org', 'some E-Mail-Address', '{}'::jsonb);
end; $$;
--//