HostingAsset-Hierarchie spec in enum HsHostingAssetType and generates PlantUML (#72)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #72 Reviewed-by: Timotheus Pokorra <timotheus.pokorra@hostsharing.net>
This commit is contained in:
parent
f6d66d5712
commit
afb6771ed7
199
doc/hs-hosting-asset-type-structure.md
Normal file
199
doc/hs-hosting-asset-type-structure.md
Normal file
@ -0,0 +1,199 @@
|
||||
## HostingAsset Type Structure
|
||||
|
||||
### Domain
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
package Booking #feb28c {
|
||||
entity BI_PRIVATE_CLOUD
|
||||
entity BI_CLOUD_SERVER
|
||||
entity BI_MANAGED_SERVER
|
||||
entity BI_MANAGED_WEBSPACE
|
||||
entity BI_DOMAIN_DNS_SETUP
|
||||
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
|
||||
}
|
||||
|
||||
package Hosting #feb28c{
|
||||
package Domain #99bcdb {
|
||||
entity HA_DOMAIN_SETUP
|
||||
entity HA_DOMAIN_DNS_SETUP
|
||||
entity HA_DOMAIN_HTTP_SETUP
|
||||
entity HA_DOMAIN_EMAIL_SUBMISSION_SETUP
|
||||
entity HA_DOMAIN_EMAIL_MAILBOX_SETUP
|
||||
entity HA_EMAIL_ADDRESS
|
||||
}
|
||||
|
||||
package Server #99bcdb {
|
||||
entity HA_CLOUD_SERVER
|
||||
entity HA_MANAGED_SERVER
|
||||
entity HA_IP_NUMBER
|
||||
}
|
||||
|
||||
package Webspace #99bcdb {
|
||||
entity HA_MANAGED_WEBSPACE
|
||||
entity HA_UNIX_USER
|
||||
entity HA_EMAIL_ALIAS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||
|
||||
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||
HA_DOMAIN_SETUP o..> HA_DOMAIN_SETUP
|
||||
HA_DOMAIN_DNS_SETUP *==> HA_DOMAIN_SETUP
|
||||
HA_DOMAIN_HTTP_SETUP *==> HA_DOMAIN_SETUP
|
||||
HA_DOMAIN_HTTP_SETUP o..> HA_UNIX_USER
|
||||
HA_DOMAIN_EMAIL_SUBMISSION_SETUP *==> HA_DOMAIN_SETUP
|
||||
HA_DOMAIN_EMAIL_SUBMISSION_SETUP o..> HA_MANAGED_WEBSPACE
|
||||
HA_DOMAIN_EMAIL_MAILBOX_SETUP *==> HA_DOMAIN_SETUP
|
||||
HA_DOMAIN_EMAIL_MAILBOX_SETUP o..> HA_MANAGED_WEBSPACE
|
||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||
HA_EMAIL_ADDRESS *==> HA_DOMAIN_EMAIL_MAILBOX_SETUP
|
||||
HA_IP_NUMBER o..> HA_CLOUD_SERVER
|
||||
HA_IP_NUMBER o..> HA_MANAGED_SERVER
|
||||
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
||||
|
||||
package Legend #white {
|
||||
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
|
||||
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
|
||||
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
|
||||
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
|
||||
}
|
||||
Booking -down[hidden]->Legend
|
||||
```
|
||||
### MariaDB
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
package Booking #feb28c {
|
||||
entity BI_PRIVATE_CLOUD
|
||||
entity BI_CLOUD_SERVER
|
||||
entity BI_MANAGED_SERVER
|
||||
entity BI_MANAGED_WEBSPACE
|
||||
entity BI_DOMAIN_DNS_SETUP
|
||||
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
|
||||
}
|
||||
|
||||
package Hosting #feb28c{
|
||||
package MariaDB #99bcdb {
|
||||
entity HA_MARIADB_INSTANCE
|
||||
entity HA_MARIADB_USER
|
||||
entity HA_MARIADB_DATABASE
|
||||
}
|
||||
|
||||
package Server #99bcdb {
|
||||
entity HA_CLOUD_SERVER
|
||||
entity HA_MANAGED_SERVER
|
||||
entity HA_IP_NUMBER
|
||||
}
|
||||
|
||||
package Webspace #99bcdb {
|
||||
entity HA_MANAGED_WEBSPACE
|
||||
entity HA_UNIX_USER
|
||||
entity HA_EMAIL_ALIAS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||
|
||||
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||
HA_MARIADB_INSTANCE *==> HA_MANAGED_SERVER
|
||||
HA_MARIADB_USER *==> HA_MARIADB_INSTANCE
|
||||
HA_MARIADB_USER o..> HA_MANAGED_WEBSPACE
|
||||
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_MANAGED_SERVER
|
||||
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
||||
|
||||
package Legend #white {
|
||||
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
|
||||
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
|
||||
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
|
||||
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
|
||||
}
|
||||
Booking -down[hidden]->Legend
|
||||
```
|
||||
### PostgreSQL
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
package Booking #feb28c {
|
||||
entity BI_PRIVATE_CLOUD
|
||||
entity BI_CLOUD_SERVER
|
||||
entity BI_MANAGED_SERVER
|
||||
entity BI_MANAGED_WEBSPACE
|
||||
entity BI_DOMAIN_DNS_SETUP
|
||||
entity BI_DOMAIN_EMAIL_SUBMISSION_SETUP
|
||||
}
|
||||
|
||||
package Hosting #feb28c{
|
||||
package PostgreSQL #99bcdb {
|
||||
entity HA_PGSQL_INSTANCE
|
||||
entity HA_PGSQL_USER
|
||||
entity HA_PGSQL_DATABASE
|
||||
}
|
||||
|
||||
package Server #99bcdb {
|
||||
entity HA_CLOUD_SERVER
|
||||
entity HA_MANAGED_SERVER
|
||||
entity HA_IP_NUMBER
|
||||
}
|
||||
|
||||
package Webspace #99bcdb {
|
||||
entity HA_MANAGED_WEBSPACE
|
||||
entity HA_UNIX_USER
|
||||
entity HA_EMAIL_ALIAS
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
BI_CLOUD_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_SERVER *--> BI_PRIVATE_CLOUD
|
||||
BI_MANAGED_WEBSPACE *--> BI_MANAGED_SERVER
|
||||
|
||||
HA_CLOUD_SERVER ==* BI_CLOUD_SERVER
|
||||
HA_MANAGED_SERVER ==* BI_MANAGED_SERVER
|
||||
HA_MANAGED_WEBSPACE ==* BI_MANAGED_WEBSPACE
|
||||
HA_MANAGED_WEBSPACE o..> HA_MANAGED_SERVER
|
||||
HA_UNIX_USER *==> HA_MANAGED_WEBSPACE
|
||||
HA_EMAIL_ALIAS *==> HA_MANAGED_WEBSPACE
|
||||
HA_PGSQL_INSTANCE *==> HA_MANAGED_SERVER
|
||||
HA_PGSQL_USER *==> HA_PGSQL_INSTANCE
|
||||
HA_PGSQL_USER o..> HA_MANAGED_WEBSPACE
|
||||
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_MANAGED_SERVER
|
||||
HA_IP_NUMBER o..> HA_MANAGED_WEBSPACE
|
||||
|
||||
package Legend #white {
|
||||
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
|
||||
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
|
||||
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
|
||||
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
|
||||
}
|
||||
Booking -down[hidden]->Legend
|
||||
```
|
||||
This code generated was by HsHostingAssetType.main, do not amend manually.
|
@ -1,8 +1,37 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.item;
|
||||
|
||||
public enum HsBookingItemType {
|
||||
import java.util.List;
|
||||
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
public enum HsBookingItemType implements Node {
|
||||
PRIVATE_CLOUD,
|
||||
CLOUD_SERVER,
|
||||
MANAGED_SERVER,
|
||||
MANAGED_WEBSPACE
|
||||
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
|
||||
|
||||
private final HsBookingItemType parentItemType;
|
||||
|
||||
HsBookingItemType() {
|
||||
this.parentItemType = null;
|
||||
}
|
||||
|
||||
HsBookingItemType(final HsBookingItemType parentItemType) {
|
||||
this.parentItemType = parentItemType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> edges() {
|
||||
return ofNullable(parentItemType)
|
||||
.map(p -> (nodeName() + " *--> " + p.nodeName()))
|
||||
.stream().toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeName() {
|
||||
return "BI_" + name();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package net.hostsharing.hsadminng.hs.booking.item;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface Node {
|
||||
|
||||
String nodeName();
|
||||
List<String> edges();
|
||||
}
|
@ -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_SETUP;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_EMAIL_MAILBOX_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_SETUP)
|
||||
.filter(bi -> bi.getType() == DOMAIN_EMAIL_MAILBOX_SETUP)
|
||||
.flatMap(domainEMailSetup -> domainEMailSetup.getSubHostingAssets().stream()
|
||||
.filter(subAsset -> subAsset.getType()==EMAIL_ADDRESS))
|
||||
.count())
|
||||
|
@ -50,6 +50,7 @@ import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.DELETE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.INSERT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.SELECT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Permission.UPDATE;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.RbacUserReference.UserRole.CREATOR;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.ADMIN;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.AGENT;
|
||||
import static net.hostsharing.hsadminng.rbac.rbacdef.RbacView.Role.GUEST;
|
||||
@ -204,11 +205,12 @@ public class HsHostingAssetEntity implements Stringifyable, RbacObject, Properti
|
||||
.switchOnColumn("type",
|
||||
inCaseOf("DOMAIN_SETUP", then -> {
|
||||
then.toRole(GLOBAL, GUEST).grantPermission(INSERT);
|
||||
then.toRole(GLOBAL, ADMIN).grantPermission(SELECT); // TODO.spec: replace by a proper solution
|
||||
})
|
||||
)
|
||||
|
||||
.createRole(OWNER, (with) -> {
|
||||
with.owningUser(CREATOR);
|
||||
with.incomingSuperRole(GLOBAL, ADMIN).unassumed(); // TODO.spec: replace by a better solution
|
||||
with.incomingSuperRole("bookingItem", ADMIN);
|
||||
with.incomingSuperRole("parentAsset", ADMIN);
|
||||
with.permission(DELETE);
|
||||
|
@ -1,33 +1,204 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemEntity;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||
import net.hostsharing.hsadminng.hs.booking.item.Node;
|
||||
|
||||
public enum HsHostingAssetType {
|
||||
CLOUD_SERVER, // named e.g. vm1234
|
||||
MANAGED_SERVER, // named e.g. vm1234
|
||||
MANAGED_WEBSPACE(MANAGED_SERVER), // named eg. xyz00
|
||||
UNIX_USER(MANAGED_WEBSPACE), // named e.g. xyz00-abc
|
||||
DOMAIN_SETUP, // named e.g. example.org
|
||||
DOMAIN_DNS_SETUP(DOMAIN_SETUP), // named e.g. example.org
|
||||
DOMAIN_HTTP_SETUP(DOMAIN_SETUP), // named e.g. example.org
|
||||
DOMAIN_EMAIL_SETUP(DOMAIN_SETUP), // named e.g. example.org
|
||||
import javax.naming.NamingException;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.stream.Collectors.joining;
|
||||
import static java.util.stream.Collectors.toSet;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.EntityTypeRelation.*;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.OPTIONAL;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationPolicy.REQUIRED;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.ASSIGNED_TO_ASSET;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.BOOKING_ITEM;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.RelationType.PARENT_ASSET;
|
||||
|
||||
public enum HsHostingAssetType implements Node {
|
||||
SAME_TYPE, // pseudo-type for recursive references
|
||||
|
||||
CLOUD_SERVER( // named e.g. vm1234
|
||||
inGroup("Server"),
|
||||
requires(HsBookingItemType.CLOUD_SERVER)),
|
||||
|
||||
MANAGED_SERVER( // named e.g. vm1234
|
||||
inGroup("Server"),
|
||||
requires(HsBookingItemType.MANAGED_SERVER)),
|
||||
|
||||
MANAGED_WEBSPACE( // named eg. xyz00
|
||||
inGroup("Webspace"),
|
||||
requires(HsBookingItemType.MANAGED_WEBSPACE),
|
||||
optionalParent(MANAGED_SERVER)),
|
||||
|
||||
UNIX_USER( // named e.g. xyz00-abc
|
||||
inGroup("Webspace"),
|
||||
requiredParent(MANAGED_WEBSPACE)),
|
||||
|
||||
DOMAIN_SETUP( // named e.g. example.org
|
||||
inGroup("Domain"),
|
||||
optionalParent(SAME_TYPE)
|
||||
),
|
||||
|
||||
DOMAIN_DNS_SETUP( // named e.g. example.org
|
||||
inGroup("Domain"),
|
||||
requiredParent(DOMAIN_SETUP)),
|
||||
|
||||
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
|
||||
inGroup("Domain"),
|
||||
requiredParent(DOMAIN_SETUP),
|
||||
assignedTo(MANAGED_WEBSPACE)),
|
||||
|
||||
DOMAIN_EMAIL_MAILBOX_SETUP( // named e.g. example.org
|
||||
inGroup("Domain"),
|
||||
requiredParent(DOMAIN_SETUP),
|
||||
assignedTo(MANAGED_WEBSPACE)),
|
||||
|
||||
// TODO.spec: SECURE_MX
|
||||
EMAIL_ALIAS(MANAGED_WEBSPACE), // named e.g. xyz00-abc
|
||||
EMAIL_ADDRESS(DOMAIN_EMAIL_SETUP), // named e.g. sample@example.org
|
||||
PGSQL_USER(MANAGED_WEBSPACE), // named e.g. xyz00_abc
|
||||
PGSQL_DATABASE(MANAGED_WEBSPACE), // named e.g. xyz00_abc, TODO.spec: or PGSQL_USER?
|
||||
MARIADB_USER(MANAGED_WEBSPACE), // named e.g. xyz00_abc
|
||||
MARIADB_DATABASE(MANAGED_WEBSPACE); // named e.g. xyz00_abc, TODO.spec: or MARIADB_USER?
|
||||
|
||||
EMAIL_ALIAS( // named e.g. xyz00-abc
|
||||
inGroup("Webspace"),
|
||||
requiredParent(MANAGED_WEBSPACE)),
|
||||
|
||||
public final HsHostingAssetType parentAssetType;
|
||||
EMAIL_ADDRESS( // named e.g. sample@example.org
|
||||
inGroup("Domain"),
|
||||
requiredParent(DOMAIN_EMAIL_MAILBOX_SETUP)),
|
||||
|
||||
HsHostingAssetType(final HsHostingAssetType parentAssetType) {
|
||||
this.parentAssetType = parentAssetType;
|
||||
PGSQL_INSTANCE( // TODO.spec: identifier to be specified
|
||||
inGroup("PostgreSQL"),
|
||||
requiredParent(MANAGED_SERVER)),
|
||||
|
||||
PGSQL_USER( // named e.g. xyz00_abc
|
||||
inGroup("PostgreSQL"),
|
||||
requiredParent(PGSQL_INSTANCE),
|
||||
assignedTo(MANAGED_WEBSPACE)),
|
||||
|
||||
PGSQL_DATABASE( // named e.g. xyz00_abc
|
||||
inGroup("PostgreSQL"),
|
||||
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
|
||||
inGroup("MariaDB"),
|
||||
requiredParent(MANAGED_SERVER)), // TODO.spec: or MANAGED_WEBSPACE?
|
||||
|
||||
MARIADB_USER( // named e.g. xyz00_abc
|
||||
inGroup("MariaDB"),
|
||||
requiredParent(MARIADB_INSTANCE),
|
||||
assignedTo(MANAGED_WEBSPACE)),
|
||||
|
||||
MARIADB_DATABASE( // named e.g. xyz00_abc
|
||||
inGroup("MariaDB"),
|
||||
requiredParent(MANAGED_WEBSPACE), // TODO.spec: or MARIADB_USER?
|
||||
assignedTo(MARIADB_INSTANCE)), // TODO.spec: or swapping parent+assignedTo?
|
||||
|
||||
IP_NUMBER(
|
||||
inGroup("Server"),
|
||||
assignedTo(CLOUD_SERVER),
|
||||
assignedTo(MANAGED_SERVER),
|
||||
assignedTo(MANAGED_WEBSPACE)
|
||||
);
|
||||
|
||||
private final String groupName;
|
||||
private final EntityTypeRelation<?, ?>[] relations;
|
||||
|
||||
HsHostingAssetType(
|
||||
final String groupName,
|
||||
final EntityTypeRelation<?, ?>... relations
|
||||
) {
|
||||
this.groupName = groupName;
|
||||
this.relations = relations;
|
||||
}
|
||||
|
||||
HsHostingAssetType() {
|
||||
this(null);
|
||||
this.groupName = null;
|
||||
this.relations = null;
|
||||
}
|
||||
|
||||
/// just syntactic sugar
|
||||
private static String inGroup(final String groupName) {
|
||||
return groupName;
|
||||
}
|
||||
|
||||
// TODO.refa: try to get rid of the following similar methods:
|
||||
|
||||
public RelationPolicy bookingItemPolicy() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == BOOKING_ITEM)
|
||||
.map(r -> r.relationPolicy)
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(RelationPolicy.FORBIDDEN);
|
||||
}
|
||||
|
||||
public HsBookingItemType bookingItemType() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == BOOKING_ITEM)
|
||||
.map(r -> HsBookingItemType.valueOf(r.relatedType(this).toString()))
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public RelationPolicy parentAssetPolicy() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == PARENT_ASSET)
|
||||
.map(r -> r.relationPolicy)
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(RelationPolicy.FORBIDDEN);
|
||||
}
|
||||
|
||||
public HsHostingAssetType parentAssetType() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == PARENT_ASSET)
|
||||
.map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString()))
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public RelationPolicy assignedToAssetPolicy() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == ASSIGNED_TO_ASSET)
|
||||
.map(r -> r.relationPolicy)
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(RelationPolicy.FORBIDDEN);
|
||||
}
|
||||
|
||||
public HsHostingAssetType assignedToAssetType() {
|
||||
return stream(relations)
|
||||
.filter(r -> r.relationType == ASSIGNED_TO_ASSET)
|
||||
.map(r -> HsHostingAssetType.valueOf(r.relatedType(this).toString()))
|
||||
.reduce(HsHostingAssetType::onlyASingleElementExpectedException)
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static <X> X onlyASingleElementExpectedException(Object a, Object b) {
|
||||
throw new IllegalStateException("Only a single element expected to match criteria.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> edges() {
|
||||
return stream(relations)
|
||||
.map(r -> nodeName() + r.edge + r.relatedType(this).nodeName())
|
||||
.toList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String nodeName() {
|
||||
return "HA_" + name();
|
||||
}
|
||||
|
||||
public static <T extends Enum<?>> HsHostingAssetType of(final T value) {
|
||||
@ -37,4 +208,148 @@ public enum HsHostingAssetType {
|
||||
static String asString(final HsHostingAssetType type) {
|
||||
return type == null ? null : type.name();
|
||||
}
|
||||
|
||||
private static String renderAsPlantUML(final String caption, final Set<String> includedHostingGroups) {
|
||||
final String bookingNodes = stream(HsBookingItemType.values())
|
||||
.map(t -> " entity " + t.nodeName())
|
||||
.collect(joining("\n"));
|
||||
final String hostingGroups = includedHostingGroups.stream().sorted()
|
||||
.map(HsHostingAssetType::generateGroup)
|
||||
.collect(joining("\n"));
|
||||
final String hostingAssetNodes = stream(HsHostingAssetType.values())
|
||||
.filter(t -> t.isInGroups(includedHostingGroups))
|
||||
.map(t -> "entity " + t.nodeName())
|
||||
.collect(joining("\n"));
|
||||
final String bookingItemEdges = stream(HsBookingItemType.values())
|
||||
.map(HsBookingItemType::edges)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(joining("\n"));
|
||||
final String hostingAssetEdges = stream(HsHostingAssetType.values())
|
||||
.filter(t -> t.isInGroups(includedHostingGroups))
|
||||
.map(HsHostingAssetType::edges)
|
||||
.flatMap(Collection::stream)
|
||||
.collect(joining("\n"));
|
||||
return """
|
||||
|
||||
### %{caption}
|
||||
|
||||
```plantuml
|
||||
@startuml
|
||||
left to right direction
|
||||
|
||||
package Booking #feb28c {
|
||||
%{bookingNodes}
|
||||
}
|
||||
|
||||
package Hosting #feb28c{
|
||||
%{hostingGroups}
|
||||
}
|
||||
|
||||
%{bookingItemEdges}
|
||||
|
||||
%{hostingAssetEdges}
|
||||
|
||||
package Legend #white {
|
||||
SUB_ENTITY1 *--> REQUIRED_PARENT_ENTITY
|
||||
SUB_ENTITY2 *..> OPTIONAL_PARENT_ENTITY
|
||||
ASSIGNED_ENTITY1 o--> REQUIRED_ASSIGNED_TO_ENTITY1
|
||||
ASSIGNED_ENTITY2 o..> OPTIONAL_ASSIGNED_TO_ENTITY2
|
||||
}
|
||||
Booking -down[hidden]->Legend
|
||||
```
|
||||
"""
|
||||
.replace("%{caption}", caption)
|
||||
.replace("%{bookingNodes}", bookingNodes)
|
||||
.replace("%{hostingGroups}", hostingGroups)
|
||||
.replace("%{hostingAssetNodeStyles}", hostingAssetNodes)
|
||||
.replace("%{bookingItemEdges}", bookingItemEdges)
|
||||
.replace("%{hostingAssetEdges}", hostingAssetEdges);
|
||||
}
|
||||
|
||||
private boolean isInGroups(final Set<String> assetGroups) {
|
||||
return groupName != null && assetGroups.contains(groupName);
|
||||
}
|
||||
|
||||
private static String generateGroup(final String group) {
|
||||
return " package " + group + " #99bcdb {\n"
|
||||
+ stream(HsHostingAssetType.values())
|
||||
.filter(t -> group.equals(t.groupName))
|
||||
.map(t -> " entity " + t.nodeName())
|
||||
.collect(joining("\n"))
|
||||
+ "\n }\n";
|
||||
}
|
||||
|
||||
static String renderAsEmbeddedPlantUml() {
|
||||
|
||||
final var markdown = new StringBuilder("""
|
||||
## HostingAsset Type Structure
|
||||
|
||||
""");
|
||||
|
||||
// rendering all types in a single diagram is currently ignored
|
||||
renderAsPlantUML("Domain", stream(HsHostingAssetType.values())
|
||||
.filter(t -> t.groupName != null)
|
||||
.map(t -> t.groupName)
|
||||
.collect(toSet()));
|
||||
|
||||
markdown.append(renderAsPlantUML("Domain", Set.of("Domain", "Webspace", "Server")))
|
||||
.append(renderAsPlantUML("MariaDB", Set.of("MariaDB", "Webspace", "Server")))
|
||||
.append(renderAsPlantUML("PostgreSQL", Set.of("PostgreSQL", "Webspace", "Server")));
|
||||
|
||||
markdown.append("""
|
||||
|
||||
This code generated was by %{this}.main, do not amend manually.
|
||||
"""
|
||||
.replace("%{this}", HsHostingAssetType.class.getSimpleName()));
|
||||
|
||||
return markdown.toString();
|
||||
}
|
||||
|
||||
public static void main(final String[] args) throws IOException, NamingException {
|
||||
Files.writeString(
|
||||
Path.of("doc/hs-hosting-asset-type-structure.md"),
|
||||
renderAsEmbeddedPlantUml(),
|
||||
StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
|
||||
}
|
||||
|
||||
public enum RelationPolicy {
|
||||
FORBIDDEN, OPTIONAL, REQUIRED
|
||||
}
|
||||
|
||||
public enum RelationType {
|
||||
BOOKING_ITEM,
|
||||
PARENT_ASSET,
|
||||
ASSIGNED_TO_ASSET
|
||||
}
|
||||
}
|
||||
|
||||
@AllArgsConstructor
|
||||
class EntityTypeRelation<E, T extends Node> {
|
||||
|
||||
final HsHostingAssetType.RelationPolicy relationPolicy;
|
||||
final HsHostingAssetType.RelationType relationType;
|
||||
final Function<HsHostingAssetEntity, E> getter;
|
||||
private final T relatedType;
|
||||
final String edge;
|
||||
|
||||
public T relatedType(final HsHostingAssetType referringType) {
|
||||
//noinspection unchecked
|
||||
return relatedType == HsHostingAssetType.SAME_TYPE ? (T) referringType : relatedType;
|
||||
}
|
||||
|
||||
static EntityTypeRelation<HsBookingItemEntity, HsBookingItemType> requires(final HsBookingItemType bookingItemType) {
|
||||
return new EntityTypeRelation<>(REQUIRED, BOOKING_ITEM, HsHostingAssetEntity::getBookingItem, bookingItemType, " *==> ");
|
||||
}
|
||||
|
||||
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> optionalParent(final HsHostingAssetType hostingAssetType) {
|
||||
return new EntityTypeRelation<>(OPTIONAL, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " o..> ");
|
||||
}
|
||||
|
||||
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> requiredParent(final HsHostingAssetType hostingAssetType) {
|
||||
return new EntityTypeRelation<>(REQUIRED, PARENT_ASSET, HsHostingAssetEntity::getParentAsset, hostingAssetType, " *==> ");
|
||||
}
|
||||
|
||||
static EntityTypeRelation<HsHostingAssetEntity, HsHostingAssetType> assignedTo(final HsHostingAssetType hostingAssetType) {
|
||||
return new EntityTypeRelation<>(REQUIRED, ASSIGNED_TO_ASSET, HsHostingAssetEntity::getAssignedToAsset, hostingAssetType, " o..> ");
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +1,16 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.CLOUD_SERVER;
|
||||
|
||||
class HsCloudServerHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
|
||||
HsCloudServerHostingAssetValidator() {
|
||||
super(
|
||||
BookingItem.mustBeOfType(HsBookingItemType.CLOUD_SERVER),
|
||||
ParentAsset.mustBeNull(),
|
||||
AssignedToAsset.mustBeNull(),
|
||||
CLOUD_SERVER,
|
||||
AlarmContact.isOptional(),
|
||||
NO_EXTRA_PROPERTIES);
|
||||
}
|
||||
|
@ -2,7 +2,6 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import lombok.SneakyThrows;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||
import net.hostsharing.hsadminng.system.SystemProcess;
|
||||
|
||||
import java.util.List;
|
||||
@ -10,6 +9,7 @@ import java.util.regex.Pattern;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.ofNullable;
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_DNS_SETUP;
|
||||
import static net.hostsharing.hsadminng.hs.validation.ArrayProperty.arrayOf;
|
||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||
@ -30,11 +30,11 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
||||
|
||||
static final String RR_REGEX_IN_TTL =
|
||||
RR_REGEX_NAME + RR_REGEX_IN + RR_REGEX_TTL + RR_RECORD_TYPE + RR_RECORD_DATA + RR_COMMENT;
|
||||
public static final String IDENTIFIER_SUFFIX = "|DNS";
|
||||
|
||||
HsDomainDnsSetupHostingAssetValidator() {
|
||||
super( BookingItem.mustBeNull(),
|
||||
ParentAsset.mustBeOfType(HsHostingAssetType.DOMAIN_SETUP),
|
||||
AssignedToAsset.mustBeNull(),
|
||||
super(
|
||||
DOMAIN_DNS_SETUP,
|
||||
AlarmContact.isOptional(),
|
||||
|
||||
integerProperty("TTL").min(0).withDefault(21600),
|
||||
@ -60,14 +60,14 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
||||
|
||||
@Override
|
||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||
return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + "$");
|
||||
return Pattern.compile("^" + assetEntity.getParentAsset().getIdentifier() + Pattern.quote(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()));
|
||||
ofNullable(entity.getParentAsset()).ifPresent(pa -> entity.setIdentifier(pa.getIdentifier() + IDENTIFIER_SUFFIX));
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
||||
final var result = super.validateContext(assetEntity);
|
||||
|
||||
// TODO.spec: define which checks should get raised to error level
|
||||
final var namedCheckZone = new SystemProcess("named-checkzone", assetEntity.getIdentifier());
|
||||
final var namedCheckZone = new SystemProcess("named-checkzone", fqdn(assetEntity));
|
||||
if (namedCheckZone.execute(toZonefileString(assetEntity)) != 0) {
|
||||
// yes, named-checkzone writes error messages to stdout
|
||||
stream(namedCheckZone.getStdOut().split("\n"))
|
||||
@ -99,8 +99,12 @@ class HsDomainDnsSetupHostingAssetValidator extends HsHostingAssetEntityValidato
|
||||
|
||||
{userRRs}
|
||||
"""
|
||||
.replace("{domain}", assetEntity.getIdentifier())
|
||||
.replace("{domain}", fqdn(assetEntity))
|
||||
.replace("{ttl}", getPropertyValue(assetEntity, "TTL"))
|
||||
.replace("{userRRs}", getPropertyValues(assetEntity, "user-RR") );
|
||||
}
|
||||
|
||||
private String fqdn(final HsHostingAssetEntity assetEntity) {
|
||||
return assetEntity.getIdentifier().substring(0, assetEntity.getIdentifier().length()-IDENTIFIER_SUFFIX.length());
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,11 @@ package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.DOMAIN_SETUP;
|
||||
|
||||
class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
|
||||
public static final String DOMAIN_NAME_REGEX = "^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\\.)+[A-Za-z]{2,6}";
|
||||
@ -11,15 +14,43 @@ class HsDomainSetupHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
private final Pattern identifierPattern;
|
||||
|
||||
HsDomainSetupHostingAssetValidator() {
|
||||
super( BookingItem.mustBeNull(),
|
||||
ParentAsset.mustBeNull(),
|
||||
AssignedToAsset.mustBeNull(),
|
||||
super( DOMAIN_SETUP,
|
||||
AlarmContact.isOptional(),
|
||||
|
||||
NO_EXTRA_PROPERTIES);
|
||||
this.identifierPattern = Pattern.compile(DOMAIN_NAME_REGEX);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> validateEntity(final HsHostingAssetEntity assetEntity) {
|
||||
// TODO.impl: for newly created entities, check the permission of setting up a domain
|
||||
//
|
||||
// reject, if the domain is any of these:
|
||||
// hostsharing.com|net|org|coop, // just to be on the safe side
|
||||
// [^.}+, // top-level-domain
|
||||
// co.uk, org.uk, gov.uk, ac.uk, sch.uk,
|
||||
// com.au, net.au, org.au, edu.au, gov.au, asn.au, id.au,
|
||||
// co.jp, ne.jp, or.jp, ac.jp, go.jp,
|
||||
// com.cn, net.cn, org.cn, gov.cn, edu.cn, ac.cn,
|
||||
// com.br, net.br, org.br, gov.br, edu.br, mil.br, art.br,
|
||||
// co.in, net.in, org.in, gen.in, firm.in, ind.in,
|
||||
// com.mx, net.mx, org.mx, gob.mx, edu.mx,
|
||||
// gov.it, edu.it,
|
||||
// co.nz, net.nz, org.nz, govt.nz, ac.nz, school.nz, geek.nz, kiwi.nz,
|
||||
// co.kr, ne.kr, or.kr, go.kr, re.kr, pe.kr
|
||||
//
|
||||
// allow if
|
||||
// - 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();
|
||||
|
||||
return super.validateEntity(assetEntity);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Pattern identifierPattern(final HsHostingAssetEntity assetEntity) {
|
||||
return identifierPattern;
|
||||
|
@ -15,9 +15,7 @@ class HsEMailAliasHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
public static final int EMAIL_ADDRESS_MAX_LENGTH = 320; // according to RFC 5321 and RFC 5322
|
||||
|
||||
HsEMailAliasHostingAssetValidator() {
|
||||
super( BookingItem.mustBeNull(),
|
||||
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
||||
AssignedToAsset.mustBeNull(),
|
||||
super( HsHostingAssetType.EMAIL_ALIAS,
|
||||
AlarmContact.isOptional(),
|
||||
|
||||
arrayOf(
|
||||
|
@ -9,7 +9,6 @@ import net.hostsharing.hsadminng.hs.office.contact.HsOfficeContactEntity;
|
||||
import net.hostsharing.hsadminng.hs.validation.HsEntityValidator;
|
||||
import net.hostsharing.hsadminng.hs.validation.ValidatableProperty;
|
||||
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
@ -26,21 +25,31 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
||||
|
||||
static final ValidatableProperty<?, ?>[] NO_EXTRA_PROPERTIES = new ValidatableProperty<?, ?>[0];
|
||||
|
||||
private final HsHostingAssetEntityValidator.BookingItem bookingItemValidation;
|
||||
private final HsHostingAssetEntityValidator.ParentAsset parentAssetValidation;
|
||||
private final HsHostingAssetEntityValidator.AssignedToAsset assignedToAssetValidation;
|
||||
private final ReferenceValidator<HsBookingItemEntity, HsBookingItemType> bookingItemReferenceValidation;
|
||||
private final ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> parentAssetReferenceValidation;
|
||||
private final ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> assignedToAssetReferenceValidation;
|
||||
private final HsHostingAssetEntityValidator.AlarmContact alarmContactValidation;
|
||||
|
||||
HsHostingAssetEntityValidator(
|
||||
@NotNull final BookingItem bookingItemValidation,
|
||||
@NotNull final ParentAsset parentAssetValidation,
|
||||
@NotNull final AssignedToAsset assignedToAssetValidation,
|
||||
@NotNull final AlarmContact alarmContactValidation,
|
||||
final HsHostingAssetType assetType,
|
||||
final AlarmContact alarmContactValidation,
|
||||
final ValidatableProperty<?, ?>... properties) {
|
||||
super(properties);
|
||||
this.bookingItemValidation = bookingItemValidation;
|
||||
this.parentAssetValidation = parentAssetValidation;
|
||||
this.assignedToAssetValidation = assignedToAssetValidation;
|
||||
this.bookingItemReferenceValidation = new ReferenceValidator<>(
|
||||
assetType.bookingItemPolicy(),
|
||||
assetType.bookingItemType(),
|
||||
HsHostingAssetEntity::getBookingItem,
|
||||
HsBookingItemEntity::getType);
|
||||
this.parentAssetReferenceValidation = new ReferenceValidator<>(
|
||||
assetType.parentAssetPolicy(),
|
||||
assetType.parentAssetType(),
|
||||
HsHostingAssetEntity::getParentAsset,
|
||||
HsHostingAssetEntity::getType);
|
||||
this.assignedToAssetReferenceValidation = new ReferenceValidator<>(
|
||||
assetType.assignedToAssetPolicy(),
|
||||
assetType.assignedToAssetType(),
|
||||
HsHostingAssetEntity::getAssignedToAsset,
|
||||
HsHostingAssetEntity::getType);
|
||||
this.alarmContactValidation = alarmContactValidation;
|
||||
}
|
||||
|
||||
@ -63,11 +72,11 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
||||
|
||||
private List<String> validateEntityReferencesAndProperties(final HsHostingAssetEntity assetEntity) {
|
||||
return Stream.of(
|
||||
validateReferencedEntity(assetEntity, "bookingItem", bookingItemValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "parentAsset", parentAssetValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate),
|
||||
validateProperties(assetEntity))
|
||||
validateReferencedEntity(assetEntity, "bookingItem", bookingItemReferenceValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "parentAsset", parentAssetReferenceValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "assignedToAsset", assignedToAssetReferenceValidation::validate),
|
||||
validateReferencedEntity(assetEntity, "alarmContact", alarmContactValidation::validate),
|
||||
validateProperties(assetEntity))
|
||||
.filter(Objects::nonNull)
|
||||
.flatMap(List::stream)
|
||||
.filter(Objects::nonNull)
|
||||
@ -87,25 +96,28 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
||||
|
||||
private static List<String> optionallyValidate(final HsHostingAssetEntity assetEntity) {
|
||||
return assetEntity != null
|
||||
? enrich(prefix(assetEntity.toShortString(), "parentAsset"),
|
||||
HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validateContext(assetEntity))
|
||||
? enrich(
|
||||
prefix(assetEntity.toShortString(), "parentAsset"),
|
||||
HsHostingAssetEntityValidatorRegistry.forType(assetEntity.getType()).validateContext(assetEntity))
|
||||
: emptyList();
|
||||
}
|
||||
|
||||
private static List<String> optionallyValidate(final HsBookingItemEntity bookingItem) {
|
||||
return bookingItem != null
|
||||
? enrich(prefix(bookingItem.toShortString(), "bookingItem"),
|
||||
HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem))
|
||||
? enrich(
|
||||
prefix(bookingItem.toShortString(), "bookingItem"),
|
||||
HsBookingItemEntityValidatorRegistry.forType(bookingItem.getType()).validateContext(bookingItem))
|
||||
: emptyList();
|
||||
}
|
||||
|
||||
protected List<String> validateAgainstSubEntities(final HsHostingAssetEntity assetEntity) {
|
||||
return enrich(prefix(assetEntity.toShortString(), "config"),
|
||||
return enrich(
|
||||
prefix(assetEntity.toShortString(), "config"),
|
||||
stream(propertyValidators)
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(assetEntity, prop))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
.filter(ValidatableProperty::isTotalsValidator)
|
||||
.map(prop -> validateMaxTotalValue(assetEntity, prop))
|
||||
.filter(Objects::nonNull)
|
||||
.toList());
|
||||
}
|
||||
|
||||
// TODO.test: check, if there are any hosting assets which need this validation at all
|
||||
@ -130,114 +142,79 @@ public abstract class HsHostingAssetEntityValidator extends HsEntityValidator<Hs
|
||||
final var expectedIdentifierPattern = identifierPattern(assetEntity);
|
||||
if (assetEntity.getIdentifier() == null ||
|
||||
!expectedIdentifierPattern.matcher(assetEntity.getIdentifier()).matches()) {
|
||||
return List.of("'identifier' expected to match '"+expectedIdentifierPattern+"', but is '" + assetEntity.getIdentifier() + "'");
|
||||
return List.of(
|
||||
"'identifier' expected to match '" + expectedIdentifierPattern + "', but is '" + assetEntity.getIdentifier()
|
||||
+ "'");
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
protected abstract Pattern identifierPattern(HsHostingAssetEntity assetEntity);
|
||||
|
||||
static abstract class ReferenceValidator<S, T> {
|
||||
static class ReferenceValidator<S, T> {
|
||||
|
||||
private final Policy policy;
|
||||
private final T subEntityType;
|
||||
private final Function<HsHostingAssetEntity, S> subEntityGetter;
|
||||
private final Function<S,T> subEntityTypeGetter;
|
||||
private final HsHostingAssetType.RelationPolicy policy;
|
||||
private final T referencedEntityType;
|
||||
private final Function<HsHostingAssetEntity, S> referencedEntityGetter;
|
||||
private final Function<S, T> referencedEntityTypeGetter;
|
||||
|
||||
public ReferenceValidator(
|
||||
final Policy policy,
|
||||
final HsHostingAssetType.RelationPolicy policy,
|
||||
final T subEntityType,
|
||||
final Function<HsHostingAssetEntity, S> subEntityGetter,
|
||||
final Function<S, T> subEntityTypeGetter) {
|
||||
final Function<HsHostingAssetEntity, S> referencedEntityGetter,
|
||||
final Function<S, T> referencedEntityTypeGetter) {
|
||||
this.policy = policy;
|
||||
this.subEntityType = subEntityType;
|
||||
this.subEntityGetter = subEntityGetter;
|
||||
this.subEntityTypeGetter = subEntityTypeGetter;
|
||||
this.referencedEntityType = subEntityType;
|
||||
this.referencedEntityGetter = referencedEntityGetter;
|
||||
this.referencedEntityTypeGetter = referencedEntityTypeGetter;
|
||||
}
|
||||
|
||||
public ReferenceValidator(
|
||||
final Policy policy,
|
||||
final Function<HsHostingAssetEntity, S> subEntityGetter) {
|
||||
final HsHostingAssetType.RelationPolicy policy,
|
||||
final Function<HsHostingAssetEntity, S> referencedEntityGetter) {
|
||||
this.policy = policy;
|
||||
this.subEntityType = null;
|
||||
this.subEntityGetter = subEntityGetter;
|
||||
this.subEntityTypeGetter = e -> null;
|
||||
}
|
||||
|
||||
enum Policy {
|
||||
OPTIONAL, FORBIDDEN, REQUIRED
|
||||
this.referencedEntityType = null;
|
||||
this.referencedEntityGetter = referencedEntityGetter;
|
||||
this.referencedEntityTypeGetter = e -> null;
|
||||
}
|
||||
|
||||
List<String> validate(final HsHostingAssetEntity assetEntity, final String referenceFieldName) {
|
||||
|
||||
final var subEntity = subEntityGetter.apply(assetEntity);
|
||||
if (policy == Policy.REQUIRED && subEntity == null) {
|
||||
return List.of(referenceFieldName + "' must not be null but is null");
|
||||
}
|
||||
if (policy == Policy.FORBIDDEN && subEntity != null) {
|
||||
return List.of(referenceFieldName + "' must be null but is set to "+ assetEntity.getBookingItem().toShortString());
|
||||
}
|
||||
final var subItemType = subEntity != null ? subEntityTypeGetter.apply(subEntity) : null;
|
||||
if (subEntityType != null && subItemType != subEntityType) {
|
||||
return List.of(referenceFieldName + "' must be of type " + subEntityType + " but is of type " + subItemType);
|
||||
final var actualEntity = referencedEntityGetter.apply(assetEntity);
|
||||
final var actualEntityType = actualEntity != null ? referencedEntityTypeGetter.apply(actualEntity) : null;
|
||||
|
||||
switch (policy) {
|
||||
case REQUIRED:
|
||||
if (actualEntityType != referencedEntityType) {
|
||||
return List.of(actualEntityType == null
|
||||
? referenceFieldName + "' must be of type " + referencedEntityType + " but is null"
|
||||
: referenceFieldName + "' must be of type " + referencedEntityType + " but is of type " + actualEntityType);
|
||||
}
|
||||
break;
|
||||
case OPTIONAL:
|
||||
if (actualEntityType != null && actualEntityType != referencedEntityType) {
|
||||
return List.of(referenceFieldName + "' must be null or of type " + referencedEntityType + " but is of type "
|
||||
+ actualEntityType);
|
||||
}
|
||||
break;
|
||||
case FORBIDDEN:
|
||||
if (actualEntityType != null) {
|
||||
return List.of(referenceFieldName + "' must be null but is of type " + actualEntityType);
|
||||
}
|
||||
break;
|
||||
}
|
||||
return emptyList();
|
||||
}
|
||||
}
|
||||
|
||||
static class BookingItem extends ReferenceValidator<HsBookingItemEntity, HsBookingItemType> {
|
||||
|
||||
BookingItem(final Policy policy, final HsBookingItemType bookingItemType) {
|
||||
super(policy, bookingItemType, HsHostingAssetEntity::getBookingItem, HsBookingItemEntity::getType);
|
||||
}
|
||||
|
||||
static BookingItem mustBeNull() {
|
||||
return new BookingItem(Policy.FORBIDDEN, null);
|
||||
}
|
||||
|
||||
static BookingItem mustBeOfType(final HsBookingItemType hsBookingItemType) {
|
||||
return new BookingItem(Policy.REQUIRED, hsBookingItemType);
|
||||
}
|
||||
}
|
||||
|
||||
static class ParentAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||
|
||||
ParentAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType parentAssetType) {
|
||||
super(policy, parentAssetType, HsHostingAssetEntity::getParentAsset, HsHostingAssetEntity::getType);
|
||||
}
|
||||
|
||||
static ParentAsset mustBeNull() {
|
||||
return new ParentAsset(Policy.FORBIDDEN, null);
|
||||
}
|
||||
|
||||
static ParentAsset mustBeOfType(final HsHostingAssetType hostingAssetType) {
|
||||
return new ParentAsset(Policy.REQUIRED, hostingAssetType);
|
||||
}
|
||||
|
||||
static ParentAsset mustBeNullOrOfType(final HsHostingAssetType hostingAssetType) {
|
||||
return new ParentAsset(Policy.OPTIONAL, hostingAssetType);
|
||||
}
|
||||
}
|
||||
|
||||
static class AssignedToAsset extends ReferenceValidator<HsHostingAssetEntity, HsHostingAssetType> {
|
||||
|
||||
AssignedToAsset(final ReferenceValidator.Policy policy, final HsHostingAssetType assignedToAssetType) {
|
||||
super(policy, assignedToAssetType, HsHostingAssetEntity::getAssignedToAsset, HsHostingAssetEntity::getType);
|
||||
}
|
||||
|
||||
static AssignedToAsset mustBeNull() {
|
||||
return new AssignedToAsset(Policy.FORBIDDEN, null);
|
||||
}
|
||||
}
|
||||
|
||||
static class AlarmContact extends ReferenceValidator<HsOfficeContactEntity, Enum<?>> {
|
||||
|
||||
AlarmContact(final ReferenceValidator.Policy policy) {
|
||||
AlarmContact(final HsHostingAssetType.RelationPolicy policy) {
|
||||
super(policy, HsHostingAssetEntity::getAlarmContact);
|
||||
}
|
||||
|
||||
static AlarmContact isOptional() {
|
||||
return new AlarmContact(Policy.OPTIONAL);
|
||||
return new AlarmContact(HsHostingAssetType.RelationPolicy.OPTIONAL);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_SERVER;
|
||||
import static net.hostsharing.hsadminng.hs.validation.BooleanProperty.booleanProperty;
|
||||
import static net.hostsharing.hsadminng.hs.validation.EnumerationProperty.enumerationProperty;
|
||||
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||
@ -13,9 +13,7 @@ class HsManagedServerHostingAssetValidator extends HsHostingAssetEntityValidator
|
||||
|
||||
public HsManagedServerHostingAssetValidator() {
|
||||
super(
|
||||
BookingItem.mustBeOfType(HsBookingItemType.MANAGED_SERVER),
|
||||
ParentAsset.mustBeNull(), // until we introduce a hosting asset for 'HOST'
|
||||
AssignedToAsset.mustBeNull(),
|
||||
MANAGED_SERVER,
|
||||
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||
|
||||
// monitoring
|
||||
|
@ -1,16 +1,15 @@
|
||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||
|
||||
import net.hostsharing.hsadminng.hs.booking.item.HsBookingItemType;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetEntity;
|
||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType;
|
||||
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||
|
||||
class HsManagedWebspaceHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
public HsManagedWebspaceHostingAssetValidator() {
|
||||
super(BookingItem.mustBeOfType(HsBookingItemType.MANAGED_WEBSPACE),
|
||||
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_SERVER), // the (shared or private) ManagedServer
|
||||
AssignedToAsset.mustBeNull(),
|
||||
super(
|
||||
MANAGED_WEBSPACE,
|
||||
AlarmContact.isOptional(), // hostmaster alert address is implicitly added
|
||||
NO_EXTRA_PROPERTIES);
|
||||
}
|
||||
|
@ -17,9 +17,8 @@ class HsUnixUserHostingAssetValidator extends HsHostingAssetEntityValidator {
|
||||
private static final int DASH_LENGTH = "-".length();
|
||||
|
||||
HsUnixUserHostingAssetValidator() {
|
||||
super( BookingItem.mustBeNull(),
|
||||
ParentAsset.mustBeOfType(HsHostingAssetType.MANAGED_WEBSPACE),
|
||||
AssignedToAsset.mustBeNull(),
|
||||
super(
|
||||
HsHostingAssetType.UNIX_USER,
|
||||
AlarmContact.isOptional(),
|
||||
|
||||
integerProperty("SSD hard quota").unit("GB").maxFrom("SSD").optional(),
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.hostsharing.hsadminng.hs.validation;
|
||||
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -18,7 +19,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
||||
|
||||
public final ValidatableProperty<?, ?>[] propertyValidators;
|
||||
|
||||
public HsEntityValidator(final ValidatableProperty<?, ?>... validators) {
|
||||
public <T extends Enum <T>> HsEntityValidator(final ValidatableProperty<?, ?>... validators) {
|
||||
propertyValidators = validators;
|
||||
stream(propertyValidators).forEach(p -> p.deferredInit(propertyValidators));
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ create or replace function cleanIdentifier(rawIdentifier varchar)
|
||||
declare
|
||||
cleanIdentifier varchar;
|
||||
begin
|
||||
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._]+', '', 'g');
|
||||
cleanIdentifier := regexp_replace(rawIdentifier, '[^A-Za-z0-9\-._|]+', '', 'g');
|
||||
return cleanIdentifier;
|
||||
end; $$;
|
||||
|
||||
|
@ -36,9 +36,9 @@ subgraph asset["`**asset**`"]
|
||||
style asset:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:asset:INSERT{{asset:INSERT}}
|
||||
perm:asset:SELECT{{asset:SELECT}}
|
||||
perm:asset:DELETE{{asset:DELETE}}
|
||||
perm:asset:UPDATE{{asset:UPDATE}}
|
||||
perm:asset:SELECT{{asset:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
@ -80,6 +80,9 @@ subgraph parentAsset["`**parentAsset**`"]
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to users
|
||||
user:creator ==> role:asset:OWNER
|
||||
|
||||
%% granting roles to roles
|
||||
role:bookingItem:OWNER -.-> role:bookingItem:ADMIN
|
||||
role:bookingItem:ADMIN -.-> role:bookingItem:AGENT
|
||||
@ -87,6 +90,7 @@ role:bookingItem:AGENT -.-> role:bookingItem:TENANT
|
||||
role:global:ADMIN -.-> role:alarmContact:OWNER
|
||||
role:alarmContact:OWNER -.-> role:alarmContact:ADMIN
|
||||
role:alarmContact:ADMIN -.-> role:alarmContact:REFERRER
|
||||
role:global:ADMIN ==>|XX| role:asset:OWNER
|
||||
role:bookingItem:ADMIN ==> role:asset:OWNER
|
||||
role:parentAsset:ADMIN ==> role:asset:OWNER
|
||||
role:asset:OWNER ==> role:asset:ADMIN
|
||||
@ -104,7 +108,6 @@ role:alarmContact:ADMIN ==> role:asset:TENANT
|
||||
role:global:ADMIN ==> perm:asset:INSERT
|
||||
role:parentAsset:ADMIN ==> perm:asset:INSERT
|
||||
role:global:GUEST ==> perm:asset:INSERT
|
||||
role:global:ADMIN ==> perm:asset:SELECT
|
||||
role:asset:OWNER ==> perm:asset:DELETE
|
||||
role:asset:ADMIN ==> perm:asset:UPDATE
|
||||
role:asset:TENANT ==> perm:asset:SELECT
|
||||
|
@ -50,8 +50,10 @@ begin
|
||||
hsHostingAssetOWNER(NEW),
|
||||
permissions => array['DELETE'],
|
||||
incomingSuperRoles => array[
|
||||
globalADMIN(unassumed()),
|
||||
hsBookingItemADMIN(newBookingItem),
|
||||
hsHostingAssetADMIN(newParentAsset)]
|
||||
hsHostingAssetADMIN(newParentAsset)],
|
||||
userUuids => array[currentUserUuid()]
|
||||
);
|
||||
|
||||
perform createRoleWithGrants(
|
||||
@ -85,10 +87,6 @@ begin
|
||||
IF NEW.type = 'DOMAIN_SETUP' THEN
|
||||
END IF;
|
||||
|
||||
|
||||
|
||||
call grantPermissionToRole(createPermission(NEW.uuid, 'SELECT'), globalAdmin());
|
||||
|
||||
call leaveTriggerForObjectUuid(NEW.uuid);
|
||||
end; $$;
|
||||
|
||||