add-webspace-gid-and-create-webspace-main-user (#94)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: #94 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
parent
8b5cf8adc1
commit
e57f4bf0c8
@ -34,6 +34,7 @@ import jakarta.persistence.OneToOne;
|
|||||||
import jakarta.persistence.PostLoad;
|
import jakarta.persistence.PostLoad;
|
||||||
import jakarta.persistence.Transient;
|
import jakarta.persistence.Transient;
|
||||||
import jakarta.persistence.Version;
|
import jakarta.persistence.Version;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@ -88,9 +89,10 @@ public abstract class HsHostingAsset implements Stringifyable, BaseEntity<HsHost
|
|||||||
@JoinColumn(name = "alarmcontactuuid")
|
@JoinColumn(name = "alarmcontactuuid")
|
||||||
private HsOfficeContactRealEntity alarmContact;
|
private HsOfficeContactRealEntity alarmContact;
|
||||||
|
|
||||||
|
@Builder.Default
|
||||||
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
|
@OneToMany(cascade = CascadeType.REFRESH, orphanRemoval = true, fetch = FetchType.LAZY)
|
||||||
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
|
@JoinColumn(name = "parentassetuuid", referencedColumnName = "uuid")
|
||||||
private List<HsHostingAssetRealEntity> subHostingAssets;
|
private List<HsHostingAssetRealEntity> subHostingAssets = new ArrayList<>();
|
||||||
|
|
||||||
@Column(name = "identifier")
|
@Column(name = "identifier")
|
||||||
private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc
|
private String identifier; // e.g. vm1234, xyz00, example.org, xyz00_abc
|
||||||
|
@ -79,7 +79,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
.preprocessEntity()
|
.preprocessEntity()
|
||||||
.validateEntity()
|
.validateEntity()
|
||||||
.prepareForSave()
|
.prepareForSave()
|
||||||
.saveUsing(rbacAssetRepo::save)
|
.save()
|
||||||
.validateContext()
|
.validateContext()
|
||||||
.mapUsing(e -> mapper.map(e, HsHostingAssetResource.class))
|
.mapUsing(e -> mapper.map(e, HsHostingAssetResource.class))
|
||||||
.revampProperties();
|
.revampProperties();
|
||||||
@ -140,7 +140,7 @@ public class HsHostingAssetController implements HsHostingAssetsApi {
|
|||||||
.preprocessEntity()
|
.preprocessEntity()
|
||||||
.validateEntity()
|
.validateEntity()
|
||||||
.prepareForSave()
|
.prepareForSave()
|
||||||
.saveUsing(rbacAssetRepo::save)
|
.save()
|
||||||
.validateContext()
|
.validateContext()
|
||||||
.mapUsing(e -> mapper.map(e, HsHostingAssetResource.class))
|
.mapUsing(e -> mapper.map(e, HsHostingAssetResource.class))
|
||||||
.revampProperties();
|
.revampProperties();
|
||||||
|
@ -58,17 +58,42 @@ public class HostingAssetEntitySaveProcessor {
|
|||||||
/// hashing passwords etc.
|
/// hashing passwords etc.
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
public HostingAssetEntitySaveProcessor prepareForSave() {
|
public HostingAssetEntitySaveProcessor prepareForSave() {
|
||||||
step("prepareForSave", "saveUsing");
|
step("prepareForSave", "save");
|
||||||
validator.prepareProperties(em, entity);
|
validator.prepareProperties(em, entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the entity using the given `saveFunction`.
|
||||||
|
*
|
||||||
|
* <p>`validator.postPersist(em, entity)` is NOT called.
|
||||||
|
* If any postprocessing is necessary, the saveFunction has to implement this.</p>
|
||||||
|
* @param saveFunction
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
public HostingAssetEntitySaveProcessor saveUsing(final Function<HsHostingAsset, HsHostingAsset> saveFunction) {
|
public HostingAssetEntitySaveProcessor saveUsing(final Function<HsHostingAsset, HsHostingAsset> saveFunction) {
|
||||||
step("saveUsing", "validateContext");
|
step("save", "validateContext");
|
||||||
entity = saveFunction.apply(entity);
|
entity = saveFunction.apply(entity);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Saves the using the `EntityManager`, but does NOT ever merge the entity.
|
||||||
|
*
|
||||||
|
* <p>`validator.postPersist(em, entity)` is called afterwards with the entity guaranteed to be flushed to the database.</p>
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public HostingAssetEntitySaveProcessor save() {
|
||||||
|
return saveUsing(e -> {
|
||||||
|
if (!em.contains(entity)) {
|
||||||
|
em.persist(entity);
|
||||||
|
}
|
||||||
|
em.flush(); // makes RbacEntity available as RealEntity if needed
|
||||||
|
validator.postPersist(em, entity);
|
||||||
|
return entity;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits)
|
/// validates the entity within it's parent and child hierarchy (e.g. totals validators and other limits)
|
||||||
public HostingAssetEntitySaveProcessor validateContext() {
|
public HostingAssetEntitySaveProcessor validateContext() {
|
||||||
step("validateContext", "mapUsing");
|
step("validateContext", "mapUsing");
|
||||||
|
@ -1,17 +1,22 @@
|
|||||||
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
package net.hostsharing.hsadminng.hs.hosting.asset.validators;
|
||||||
|
|
||||||
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAsset;
|
||||||
|
import net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetRealEntity;
|
||||||
|
|
||||||
|
import jakarta.persistence.EntityManager;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.MANAGED_WEBSPACE;
|
||||||
|
import static net.hostsharing.hsadminng.hs.hosting.asset.HsHostingAssetType.UNIX_USER;
|
||||||
|
import static net.hostsharing.hsadminng.hs.validation.IntegerProperty.integerProperty;
|
||||||
|
|
||||||
class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator {
|
class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator {
|
||||||
public HsManagedWebspaceHostingAssetValidator() {
|
public HsManagedWebspaceHostingAssetValidator() {
|
||||||
super(
|
super(
|
||||||
MANAGED_WEBSPACE,
|
MANAGED_WEBSPACE,
|
||||||
AlarmContact.isOptional(),
|
AlarmContact.isOptional(),
|
||||||
NO_EXTRA_PROPERTIES); // TODO.impl: groupid missing, should be equal to main user
|
integerProperty("groupid").readOnly()
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -22,4 +27,24 @@ class HsManagedWebspaceHostingAssetValidator extends HostingAssetEntityValidator
|
|||||||
: "[a-z][a-z0-9][a-z0-9]";
|
: "[a-z][a-z0-9][a-z0-9]";
|
||||||
return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$");
|
return Pattern.compile("^" + prefixPattern + "[0-9][0-9]$");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void postPersist(final EntityManager em, final HsHostingAsset webspaceAsset) {
|
||||||
|
if (!webspaceAsset.isLoaded()) {
|
||||||
|
final var unixUserAsset = HsHostingAssetRealEntity.builder()
|
||||||
|
.type(UNIX_USER)
|
||||||
|
.parentAsset(em.find(HsHostingAssetRealEntity.class, webspaceAsset.getUuid()))
|
||||||
|
.identifier(webspaceAsset.getIdentifier())
|
||||||
|
.caption(webspaceAsset.getIdentifier() + " webspace user")
|
||||||
|
.build();
|
||||||
|
webspaceAsset.getSubHostingAssets().add(unixUserAsset);
|
||||||
|
new HostingAssetEntitySaveProcessor(em, unixUserAsset)
|
||||||
|
.preprocessEntity()
|
||||||
|
.validateEntity()
|
||||||
|
.prepareForSave()
|
||||||
|
.save()
|
||||||
|
.validateContext();
|
||||||
|
webspaceAsset.getConfig().put("groupid", unixUserAsset.getConfig().get("userid"));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -160,4 +160,7 @@ public abstract class HsEntityValidator<E extends PropertiesProvider> {
|
|||||||
public ValidatableProperty<?, ?> getProperty(final String propertyName) {
|
public ValidatableProperty<?, ?> getProperty(final String propertyName) {
|
||||||
return stream(propertyValidators).filter(pv -> pv.propertyName().equals(propertyName)).findFirst().orElse(null);
|
return stream(propertyValidators).filter(pv -> pv.propertyName().equals(propertyName)).findFirst().orElse(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void postPersist(final EntityManager em, final E entity) {
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,17 +176,31 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
"type": "MANAGED_WEBSPACE",
|
"type": "MANAGED_WEBSPACE",
|
||||||
"identifier": "fir10",
|
"identifier": "fir10",
|
||||||
"caption": "some separate ManagedWebspace HA",
|
"caption": "some separate ManagedWebspace HA",
|
||||||
"config": {}
|
"config": {
|
||||||
|
"groupid": 1000000
|
||||||
|
}
|
||||||
}
|
}
|
||||||
"""))
|
"""))
|
||||||
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*"))
|
.header("Location", matchesRegex("http://localhost:[1-9][0-9]*/api/hs/hosting/assets/[^/]*"))
|
||||||
.extract().header("Location"); // @formatter:on
|
.extract().header("Location"); // @formatter:on
|
||||||
|
|
||||||
// finally, the new asset can be accessed under the generated UUID
|
// the new asset can be accessed under the generated UUID
|
||||||
final var newWebspace = UUID.fromString(
|
final var newWebspaceUuid = UUID.fromString(
|
||||||
location.substring(location.lastIndexOf('/') + 1));
|
location.substring(location.lastIndexOf('/') + 1));
|
||||||
assertThat(newWebspace).isNotNull();
|
assertThat(newWebspaceUuid).isNotNull();
|
||||||
toCleanup(HsHostingAssetRbacEntity.class, newWebspace);
|
toCleanup(HsHostingAssetRbacEntity.class, newWebspaceUuid);
|
||||||
|
|
||||||
|
// and a default user got created
|
||||||
|
final var webspaceUnixUser = em.createQuery("SELECT ha FROM HsHostingAssetRealEntity ha WHERE ha.parentAsset.uuid=:webspaceUUID")
|
||||||
|
.setParameter("webspaceUUID", newWebspaceUuid)
|
||||||
|
.getSingleResult();
|
||||||
|
assertThat(webspaceUnixUser).isNotNull().extracting(Object::toString)
|
||||||
|
.isEqualTo("""
|
||||||
|
HsHostingAsset(UNIX_USER, fir10, fir10 webspace user, MANAGED_WEBSPACE:fir10, {
|
||||||
|
"password" : null,
|
||||||
|
"userid" : 1000000
|
||||||
|
})
|
||||||
|
""".trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -325,7 +339,7 @@ class HsHostingAssetControllerAcceptanceTest extends ContextBasedTestWithCleanup
|
|||||||
|
|
||||||
jpaAttempt.transacted(() -> {
|
jpaAttempt.transacted(() -> {
|
||||||
context.define("superuser-alex@hostsharing.net");
|
context.define("superuser-alex@hostsharing.net");
|
||||||
for (int n = 0; n < UNIX_USER_PER_MULTI_OPTION -preExistingUnixUserCount+1; ++n) {
|
for (int n = 0; n < UNIX_USER_PER_MULTI_OPTION-preExistingUnixUserCount; ++n) {
|
||||||
toCleanup(realAssetRepo.save(
|
toCleanup(realAssetRepo.save(
|
||||||
HsHostingAssetRealEntity.builder()
|
HsHostingAssetRealEntity.builder()
|
||||||
.type(UNIX_USER)
|
.type(UNIX_USER)
|
||||||
|
@ -350,6 +350,18 @@ public class ImportHostingAssets extends BaseOfficeDataImport {
|
|||||||
9596=HsHostingAsset(UNIX_USER, dph00-dph, Domain admin, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594})
|
9596=HsHostingAsset(UNIX_USER, dph00-dph, Domain admin, MANAGED_WEBSPACE:dph00, {"SSD hard quota": 0, "SSD soft quota": 0, "locked": false, "shell": "/bin/bash", "userid": 110594})
|
||||||
}
|
}
|
||||||
""");
|
""");
|
||||||
|
|
||||||
|
// now with groupids
|
||||||
|
assertThat(firstOfEach(5, packetAssets, MANAGED_WEBSPACE))
|
||||||
|
.isEqualToIgnoringWhitespace("""
|
||||||
|
{
|
||||||
|
10630=HsHostingAsset(MANAGED_WEBSPACE, hsh00, HA hsh00, MANAGED_SERVER:vm1050, D-1000000:hsh default project:BI hsh00, {"groupid": 6824}),
|
||||||
|
11094=HsHostingAsset(MANAGED_WEBSPACE, lug00, HA lug00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI lug00, {"groupid": 5803}),
|
||||||
|
11111=HsHostingAsset(MANAGED_WEBSPACE, xyz68, HA xyz68, MANAGED_SERVER:vm1068, D-1000000:vm1068 Monitor:BI xyz68, {"groupid": 5961}),
|
||||||
|
11112=HsHostingAsset(MANAGED_WEBSPACE, mim00, HA mim00, MANAGED_SERVER:vm1068, D-1000300:mim default project:BI mim00, {"groupid": 5964}),
|
||||||
|
19959=HsHostingAsset(MANAGED_WEBSPACE, dph00, HA dph00, MANAGED_SERVER:vm1093, D-1101900:dph default project:BI dph00, {"groupid": 9546})
|
||||||
|
}
|
||||||
|
""");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -1235,9 +1247,10 @@ public class ImportHostingAssets extends BaseOfficeDataImport {
|
|||||||
.forEach(rec -> {
|
.forEach(rec -> {
|
||||||
final var unixuser_id = rec.getInteger("unixuser_id");
|
final var unixuser_id = rec.getInteger("unixuser_id");
|
||||||
final var packet_id = rec.getInteger("packet_id");
|
final var packet_id = rec.getInteger("packet_id");
|
||||||
|
final var parentWebspaceAsset = packetAssets.get(packet_id);
|
||||||
final var unixUserAsset = HsHostingAssetRealEntity.builder()
|
final var unixUserAsset = HsHostingAssetRealEntity.builder()
|
||||||
.type(UNIX_USER)
|
.type(UNIX_USER)
|
||||||
.parentAsset(packetAssets.get(packet_id))
|
.parentAsset(parentWebspaceAsset)
|
||||||
.identifier(rec.getString("name"))
|
.identifier(rec.getString("name"))
|
||||||
.caption(rec.getString("comment"))
|
.caption(rec.getString("comment"))
|
||||||
.isLoaded(true) // avoid overwriting imported userids with generated ids
|
.isLoaded(true) // avoid overwriting imported userids with generated ids
|
||||||
@ -1253,6 +1266,10 @@ public class ImportHostingAssets extends BaseOfficeDataImport {
|
|||||||
)))
|
)))
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
|
if (unixUserAsset.getIdentifier().equals(parentWebspaceAsset.getIdentifier())) {
|
||||||
|
parentWebspaceAsset.getConfig().put("groupid", unixuser_id);
|
||||||
|
}
|
||||||
|
|
||||||
// TODO.spec: crop SSD+HDD limits if > booked
|
// TODO.spec: crop SSD+HDD limits if > booked
|
||||||
if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)
|
if (unixUserAsset.getDirectValue("SSD hard quota", Integer.class, 0)
|
||||||
> 1024 * unixUserAsset.getContextValue("SSD", Integer.class, 0)) {
|
> 1024 * unixUserAsset.getContextValue("SSD", Integer.class, 0)) {
|
||||||
|
Loading…
Reference in New Issue
Block a user